@mcpcloud/cli 0.3.0 → 0.4.0
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 +749 -288
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4151,9 +4151,83 @@ async function confirmUnset(bindingName, serverId) {
|
|
|
4151
4151
|
return ch === "y";
|
|
4152
4152
|
}
|
|
4153
4153
|
|
|
4154
|
+
// src/commands/servers-export.ts
|
|
4155
|
+
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
4156
|
+
import { dirname as dirname2, isAbsolute, join as join4, resolve as resolve2 } from "node:path";
|
|
4157
|
+
function formatBytes(bytes) {
|
|
4158
|
+
if (bytes < 1024)
|
|
4159
|
+
return `${bytes} B`;
|
|
4160
|
+
if (bytes < 1024 * 1024)
|
|
4161
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
4162
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
4163
|
+
}
|
|
4164
|
+
function assertSafeRelativePath(path) {
|
|
4165
|
+
if (isAbsolute(path) || path.split(/[\\/]/).includes("..")) {
|
|
4166
|
+
throw new Error(`Refusing to write unsafe bundle path: ${path}`);
|
|
4167
|
+
}
|
|
4168
|
+
}
|
|
4169
|
+
async function downloadTar(downloadPath) {
|
|
4170
|
+
requireApiKey();
|
|
4171
|
+
const url = new URL(downloadPath, getBaseUrl());
|
|
4172
|
+
const res = await fetch(url.toString(), {
|
|
4173
|
+
headers: { Authorization: `Bearer ${getApiKey()}` }
|
|
4174
|
+
});
|
|
4175
|
+
if (!res.ok) {
|
|
4176
|
+
throw new Error(`Bundle download failed (${res.status}).`);
|
|
4177
|
+
}
|
|
4178
|
+
return new Uint8Array(await res.arrayBuffer());
|
|
4179
|
+
}
|
|
4180
|
+
function registerServerExportCommands(servers) {
|
|
4181
|
+
servers.command("export <serverId>").description("Export a self-host bundle (Dockerfile + node entry + config) for a server").option("--org <organizationId>", "Organization ID").requiredOption("--project <projectId>", "Project ID").option("--output <dir>", "Write the bundle files to a directory (default: ./<slug>)").option("--tar [file]", "Download the bundle as a single .tar instead").addHelpText("after", [
|
|
4182
|
+
"",
|
|
4183
|
+
"Examples:",
|
|
4184
|
+
" # Write the bundle into a directory and run it:",
|
|
4185
|
+
" $ mcp servers export srv_123 --project prj_123 --output ./my-server",
|
|
4186
|
+
" $ cd ./my-server && docker compose up --build",
|
|
4187
|
+
"",
|
|
4188
|
+
" # Download a single .tar:",
|
|
4189
|
+
" $ mcp servers export srv_123 --project prj_123 --tar my-server.tar"
|
|
4190
|
+
].join(`
|
|
4191
|
+
`)).action(runAction(async (serverId, opts) => {
|
|
4192
|
+
const organizationId = await resolveOrgId(opts.org);
|
|
4193
|
+
const result = await api.post("/api/v1/server/self-host-export", { organizationId, projectId: opts.project, serverId });
|
|
4194
|
+
const summary = {
|
|
4195
|
+
artifactId: result.artifactId,
|
|
4196
|
+
downloadPath: result.downloadPath,
|
|
4197
|
+
fileCount: result.fileCount,
|
|
4198
|
+
byteSize: result.byteSize,
|
|
4199
|
+
server: result.server
|
|
4200
|
+
};
|
|
4201
|
+
if (opts.tar !== undefined) {
|
|
4202
|
+
const tarPath = resolve2(typeof opts.tar === "string" ? opts.tar : `${result.server.slug}.tar`);
|
|
4203
|
+
writeFileSync2(tarPath, await downloadTar(result.downloadPath));
|
|
4204
|
+
if (isJsonMode()) {
|
|
4205
|
+
printJson({ ...summary, output: tarPath });
|
|
4206
|
+
return;
|
|
4207
|
+
}
|
|
4208
|
+
printSuccess(`Downloaded self-host bundle (${result.fileCount} files, ${formatBytes(result.byteSize)}) to ${tarPath}.`);
|
|
4209
|
+
printKeyValue({ Unpack: `tar xf ${tarPath} && docker compose up --build` });
|
|
4210
|
+
return;
|
|
4211
|
+
}
|
|
4212
|
+
const dir = resolve2(opts.output ?? `./${result.server.slug}`);
|
|
4213
|
+
for (const file of result.files) {
|
|
4214
|
+
assertSafeRelativePath(file.path);
|
|
4215
|
+
const full = join4(dir, file.path);
|
|
4216
|
+
mkdirSync3(dirname2(full), { recursive: true });
|
|
4217
|
+
writeFileSync2(full, file.content);
|
|
4218
|
+
}
|
|
4219
|
+
if (isJsonMode()) {
|
|
4220
|
+
printJson({ ...summary, output: dir });
|
|
4221
|
+
return;
|
|
4222
|
+
}
|
|
4223
|
+
printSuccess(`Exported ${result.fileCount} files (${formatBytes(result.byteSize)}) to ${dir}.`);
|
|
4224
|
+
printKeyValue({ "Run it": `cd ${dir} && docker compose up --build` });
|
|
4225
|
+
}));
|
|
4226
|
+
}
|
|
4227
|
+
|
|
4154
4228
|
// src/commands/servers-lifecycle.ts
|
|
4155
4229
|
import { existsSync as existsSync5 } from "node:fs";
|
|
4156
|
-
import { isAbsolute, resolve as
|
|
4230
|
+
import { isAbsolute as isAbsolute2, resolve as resolve3 } from "node:path";
|
|
4157
4231
|
|
|
4158
4232
|
// src/lib/dev/spec-watcher.ts
|
|
4159
4233
|
import { createHash as createHash2 } from "node:crypto";
|
|
@@ -4162,7 +4236,7 @@ import {
|
|
|
4162
4236
|
readFileSync as readFileSync4,
|
|
4163
4237
|
watch as fsWatch
|
|
4164
4238
|
} from "node:fs";
|
|
4165
|
-
import { dirname as
|
|
4239
|
+
import { dirname as dirname3 } from "node:path";
|
|
4166
4240
|
async function regenerateDevBundle(args) {
|
|
4167
4241
|
try {
|
|
4168
4242
|
const response = await api.post("/api/v1/server/dev-bundle", {
|
|
@@ -4221,7 +4295,7 @@ function watchSpecFile(options) {
|
|
|
4221
4295
|
let lastHash = null;
|
|
4222
4296
|
let pendingTimer = null;
|
|
4223
4297
|
let closed = false;
|
|
4224
|
-
const dir =
|
|
4298
|
+
const dir = dirname3(options.specPath);
|
|
4225
4299
|
const baseName = options.specPath.slice(dir.length + 1);
|
|
4226
4300
|
const fire = () => {
|
|
4227
4301
|
if (closed)
|
|
@@ -4398,7 +4472,7 @@ function registerServerLifecycleCommands(servers) {
|
|
|
4398
4472
|
].join(`
|
|
4399
4473
|
`)).action(runAction(async (serverId, opts) => {
|
|
4400
4474
|
const cwd = process.cwd();
|
|
4401
|
-
const specPath =
|
|
4475
|
+
const specPath = isAbsolute2(opts.spec) ? opts.spec : resolve3(cwd, opts.spec);
|
|
4402
4476
|
if (!existsSync5(specPath)) {
|
|
4403
4477
|
throw new Error(`Spec file not found: ${specPath}`);
|
|
4404
4478
|
}
|
|
@@ -4904,6 +4978,7 @@ function registerServerTestCommands(servers) {
|
|
|
4904
4978
|
function registerServerCommands(program2) {
|
|
4905
4979
|
const servers = program2.command("servers").description("Manage MCP servers");
|
|
4906
4980
|
registerServerEnvCommands(servers);
|
|
4981
|
+
registerServerExportCommands(servers);
|
|
4907
4982
|
registerServerLifecycleCommands(servers);
|
|
4908
4983
|
registerServerMutationCommands(servers);
|
|
4909
4984
|
registerServerTestCommands(servers);
|
|
@@ -5093,31 +5168,35 @@ import { relative } from "node:path";
|
|
|
5093
5168
|
// src/lib/dev/handlers-sync.ts
|
|
5094
5169
|
import {
|
|
5095
5170
|
existsSync as existsSync8,
|
|
5096
|
-
mkdirSync as
|
|
5171
|
+
mkdirSync as mkdirSync6,
|
|
5097
5172
|
readFileSync as readFileSync7,
|
|
5098
5173
|
statSync,
|
|
5099
|
-
writeFileSync as
|
|
5174
|
+
writeFileSync as writeFileSync5
|
|
5100
5175
|
} from "node:fs";
|
|
5101
5176
|
import { createHash as createHash3 } from "node:crypto";
|
|
5102
|
-
import { dirname as
|
|
5177
|
+
import { dirname as dirname4, join as join7 } from "node:path";
|
|
5103
5178
|
|
|
5104
5179
|
// src/lib/dev/state.ts
|
|
5105
|
-
import { existsSync as existsSync6, mkdirSync as
|
|
5106
|
-
import { join as
|
|
5180
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "node:fs";
|
|
5181
|
+
import { join as join5, resolve as resolve4 } from "node:path";
|
|
5107
5182
|
function devRoot(cwd) {
|
|
5108
|
-
return
|
|
5183
|
+
return join5(cwd, ".mcpcloud");
|
|
5109
5184
|
}
|
|
5110
5185
|
function stateFile(cwd) {
|
|
5111
|
-
return
|
|
5186
|
+
return join5(devRoot(cwd), "state.json");
|
|
5112
5187
|
}
|
|
5113
5188
|
function serverDir(cwd) {
|
|
5114
|
-
return
|
|
5189
|
+
return join5(devRoot(cwd), "server");
|
|
5190
|
+
}
|
|
5191
|
+
function gitCloneDir(cwd, owner, name) {
|
|
5192
|
+
const safe = `${owner}__${name}`.replace(/[^a-zA-Z0-9._-]/g, "-");
|
|
5193
|
+
return join5(devRoot(cwd), "git", safe);
|
|
5115
5194
|
}
|
|
5116
5195
|
function envFile(cwd) {
|
|
5117
|
-
return
|
|
5196
|
+
return join5(devRoot(cwd), "env.json");
|
|
5118
5197
|
}
|
|
5119
5198
|
function backupsDir(cwd) {
|
|
5120
|
-
return
|
|
5199
|
+
return join5(devRoot(cwd), "agent-backups");
|
|
5121
5200
|
}
|
|
5122
5201
|
function readState(cwd) {
|
|
5123
5202
|
const path = stateFile(cwd);
|
|
@@ -5138,20 +5217,21 @@ function readState(cwd) {
|
|
|
5138
5217
|
}
|
|
5139
5218
|
function writeState(cwd, state) {
|
|
5140
5219
|
if (!existsSync6(devRoot(cwd))) {
|
|
5141
|
-
|
|
5220
|
+
mkdirSync4(devRoot(cwd), { recursive: true });
|
|
5142
5221
|
}
|
|
5143
|
-
|
|
5222
|
+
writeFileSync3(stateFile(cwd), JSON.stringify(state, null, 2), "utf-8");
|
|
5144
5223
|
}
|
|
5145
5224
|
function ensureGitignore(cwd) {
|
|
5146
|
-
const ignoreFile =
|
|
5225
|
+
const ignoreFile = join5(devRoot(cwd), ".gitignore");
|
|
5147
5226
|
if (existsSync6(ignoreFile))
|
|
5148
5227
|
return;
|
|
5149
5228
|
if (!existsSync6(devRoot(cwd))) {
|
|
5150
|
-
|
|
5229
|
+
mkdirSync4(devRoot(cwd), { recursive: true });
|
|
5151
5230
|
}
|
|
5152
|
-
|
|
5231
|
+
writeFileSync3(ignoreFile, [
|
|
5153
5232
|
"# generated by `mcp dev`",
|
|
5154
5233
|
"server/",
|
|
5234
|
+
"git/",
|
|
5155
5235
|
"env.json",
|
|
5156
5236
|
"agent-backups/",
|
|
5157
5237
|
"inspector/",
|
|
@@ -5163,13 +5243,13 @@ function ensureGitignore(cwd) {
|
|
|
5163
5243
|
// src/lib/dev/tools-sync.ts
|
|
5164
5244
|
import {
|
|
5165
5245
|
existsSync as existsSync7,
|
|
5166
|
-
mkdirSync as
|
|
5246
|
+
mkdirSync as mkdirSync5,
|
|
5167
5247
|
readFileSync as readFileSync6,
|
|
5168
5248
|
readdirSync,
|
|
5169
5249
|
rmSync,
|
|
5170
|
-
writeFileSync as
|
|
5250
|
+
writeFileSync as writeFileSync4
|
|
5171
5251
|
} from "node:fs";
|
|
5172
|
-
import { join as
|
|
5252
|
+
import { join as join6 } from "node:path";
|
|
5173
5253
|
var TOOLS_DIR_NAME = "tools";
|
|
5174
5254
|
var RISK_CLASSES = [
|
|
5175
5255
|
"read",
|
|
@@ -5178,10 +5258,10 @@ var RISK_CLASSES = [
|
|
|
5178
5258
|
"external_effect"
|
|
5179
5259
|
];
|
|
5180
5260
|
function toolsDir(devRootDir) {
|
|
5181
|
-
return
|
|
5261
|
+
return join6(devRootDir, TOOLS_DIR_NAME);
|
|
5182
5262
|
}
|
|
5183
5263
|
function serverToolsDir(devRootDir, serverId) {
|
|
5184
|
-
return
|
|
5264
|
+
return join6(toolsDir(devRootDir), serverId);
|
|
5185
5265
|
}
|
|
5186
5266
|
function escapeFrontmatterScalar(raw) {
|
|
5187
5267
|
if (raw === "")
|
|
@@ -5371,7 +5451,7 @@ function arraysEqual(a, b) {
|
|
|
5371
5451
|
}
|
|
5372
5452
|
var STATE_FILE = ".tools-state.json";
|
|
5373
5453
|
function toolsStateFile(devRootDir, serverId) {
|
|
5374
|
-
return
|
|
5454
|
+
return join6(serverToolsDir(devRootDir, serverId), STATE_FILE);
|
|
5375
5455
|
}
|
|
5376
5456
|
function readToolsState(devRootDir, serverId) {
|
|
5377
5457
|
const file = toolsStateFile(devRootDir, serverId);
|
|
@@ -5393,8 +5473,8 @@ function readToolsState(devRootDir, serverId) {
|
|
|
5393
5473
|
function writeToolsState(devRootDir, state) {
|
|
5394
5474
|
const dir = serverToolsDir(devRootDir, state.serverId);
|
|
5395
5475
|
if (!existsSync7(dir))
|
|
5396
|
-
|
|
5397
|
-
|
|
5476
|
+
mkdirSync5(dir, { recursive: true });
|
|
5477
|
+
writeFileSync4(toolsStateFile(devRootDir, state.serverId), JSON.stringify(state, null, 2), "utf-8");
|
|
5398
5478
|
}
|
|
5399
5479
|
function hashLocalView(view) {
|
|
5400
5480
|
return JSON.stringify({
|
|
@@ -5407,13 +5487,13 @@ function hashLocalView(view) {
|
|
|
5407
5487
|
function materializeTools(args) {
|
|
5408
5488
|
const dir = serverToolsDir(args.devRootDir, args.serverId);
|
|
5409
5489
|
if (!existsSync7(dir))
|
|
5410
|
-
|
|
5490
|
+
mkdirSync5(dir, { recursive: true });
|
|
5411
5491
|
const kept = new Set;
|
|
5412
5492
|
let filesWritten = 0;
|
|
5413
5493
|
for (const tool of args.tools) {
|
|
5414
5494
|
const filename = `${tool.name}.md`;
|
|
5415
5495
|
kept.add(filename);
|
|
5416
|
-
|
|
5496
|
+
writeFileSync4(join6(dir, filename), renderToolMarkdown(tool), "utf-8");
|
|
5417
5497
|
filesWritten += 1;
|
|
5418
5498
|
}
|
|
5419
5499
|
let filesRemoved = 0;
|
|
@@ -5425,7 +5505,7 @@ function materializeTools(args) {
|
|
|
5425
5505
|
if (kept.has(entry))
|
|
5426
5506
|
continue;
|
|
5427
5507
|
try {
|
|
5428
|
-
rmSync(
|
|
5508
|
+
rmSync(join6(dir, entry), { force: true });
|
|
5429
5509
|
filesRemoved += 1;
|
|
5430
5510
|
} catch {}
|
|
5431
5511
|
}
|
|
@@ -5451,7 +5531,7 @@ function materializeTools(args) {
|
|
|
5451
5531
|
return { filesWritten, filesRemoved };
|
|
5452
5532
|
}
|
|
5453
5533
|
function readLocalTool(devRootDir, serverId, toolName) {
|
|
5454
|
-
const filePath =
|
|
5534
|
+
const filePath = join6(serverToolsDir(devRootDir, serverId), `${toolName}.md`);
|
|
5455
5535
|
if (!existsSync7(filePath))
|
|
5456
5536
|
return null;
|
|
5457
5537
|
const state = readToolsState(devRootDir, serverId);
|
|
@@ -5488,7 +5568,7 @@ async function pushLocalToolEdit(args) {
|
|
|
5488
5568
|
};
|
|
5489
5569
|
}
|
|
5490
5570
|
if (!local) {
|
|
5491
|
-
const filePath =
|
|
5571
|
+
const filePath = join6(serverToolsDir(args.devRootDir, args.serverId), `${args.toolName}.md`);
|
|
5492
5572
|
if (!existsSync7(filePath))
|
|
5493
5573
|
return { kind: "missing-file" };
|
|
5494
5574
|
return { kind: "unknown-tool" };
|
|
@@ -5572,7 +5652,7 @@ function toKebabSlug(value) {
|
|
|
5572
5652
|
return value.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase() || "generated-mcp-server";
|
|
5573
5653
|
}
|
|
5574
5654
|
function serverBundleHandlersDir(devRootDir) {
|
|
5575
|
-
return
|
|
5655
|
+
return join7(devRootDir, "server", "src", "tools");
|
|
5576
5656
|
}
|
|
5577
5657
|
function handlerSlugFromWatcherPath(rel) {
|
|
5578
5658
|
if (!rel)
|
|
@@ -5601,7 +5681,7 @@ function hashSource(source) {
|
|
|
5601
5681
|
}
|
|
5602
5682
|
var lastPushedHash = new Map;
|
|
5603
5683
|
async function pushLocalHandlerEdit(args) {
|
|
5604
|
-
const filePath =
|
|
5684
|
+
const filePath = join7(serverBundleHandlersDir(args.devRootDir), `${args.slug}.ts`);
|
|
5605
5685
|
if (!existsSync8(filePath)) {
|
|
5606
5686
|
return { kind: "missing-file" };
|
|
5607
5687
|
}
|
|
@@ -5753,9 +5833,9 @@ async function resetCloudHandler(args) {
|
|
|
5753
5833
|
}
|
|
5754
5834
|
}
|
|
5755
5835
|
function writeLocalHandlerFromCloud(args) {
|
|
5756
|
-
const filePath =
|
|
5757
|
-
|
|
5758
|
-
|
|
5836
|
+
const filePath = join7(serverBundleHandlersDir(args.devRootDir), `${args.slug}.ts`);
|
|
5837
|
+
mkdirSync6(dirname4(filePath), { recursive: true });
|
|
5838
|
+
writeFileSync5(filePath, args.content, "utf-8");
|
|
5759
5839
|
lastPushedHash.set(filePath, hashSource(args.content));
|
|
5760
5840
|
return filePath;
|
|
5761
5841
|
}
|
|
@@ -5764,7 +5844,7 @@ function handlerSlugFromToolName(toolName) {
|
|
|
5764
5844
|
}
|
|
5765
5845
|
var RECENT_EDIT_WINDOW_MS = 5 * 60 * 1000;
|
|
5766
5846
|
function inspectLocalHandler(args) {
|
|
5767
|
-
const filePath =
|
|
5847
|
+
const filePath = join7(serverBundleHandlersDir(args.devRootDir), `${args.slug}.ts`);
|
|
5768
5848
|
if (!existsSync8(filePath))
|
|
5769
5849
|
return { kind: "no-local-file" };
|
|
5770
5850
|
const cached2 = lastPushedHash.get(filePath);
|
|
@@ -6066,13 +6146,13 @@ function renderBool(value) {
|
|
|
6066
6146
|
|
|
6067
6147
|
// src/commands/tools-edit.ts
|
|
6068
6148
|
import { existsSync as existsSync11 } from "node:fs";
|
|
6069
|
-
import { join as
|
|
6149
|
+
import { join as join9, relative as relative2 } from "node:path";
|
|
6070
6150
|
|
|
6071
6151
|
// src/lib/editor-shell.ts
|
|
6072
6152
|
import { spawn as spawn2 } from "node:child_process";
|
|
6073
6153
|
import { existsSync as existsSync10, readFileSync as readFileSync9 } from "node:fs";
|
|
6074
6154
|
import { platform as platform2 } from "node:os";
|
|
6075
|
-
import { delimiter, join as
|
|
6155
|
+
import { delimiter, join as join8 } from "node:path";
|
|
6076
6156
|
var DEFAULT_FALLBACKS = ["vi"];
|
|
6077
6157
|
function isExecutableFile(path) {
|
|
6078
6158
|
try {
|
|
@@ -6093,7 +6173,7 @@ function findOnPath(command) {
|
|
|
6093
6173
|
if (!dir)
|
|
6094
6174
|
continue;
|
|
6095
6175
|
for (const ext of exts) {
|
|
6096
|
-
const candidate =
|
|
6176
|
+
const candidate = join8(dir, command + ext);
|
|
6097
6177
|
if (isExecutableFile(candidate))
|
|
6098
6178
|
return candidate;
|
|
6099
6179
|
}
|
|
@@ -6114,10 +6194,10 @@ function resolveEditorCommand(args) {
|
|
|
6114
6194
|
return { command: DEFAULT_FALLBACKS[0], source: "fallback" };
|
|
6115
6195
|
}
|
|
6116
6196
|
function defaultRun(cmd, args) {
|
|
6117
|
-
return new Promise((
|
|
6197
|
+
return new Promise((resolve5, reject) => {
|
|
6118
6198
|
const child = spawn2(cmd, args, { stdio: "inherit" });
|
|
6119
6199
|
child.on("error", (err) => reject(err));
|
|
6120
|
-
child.on("exit", (code) =>
|
|
6200
|
+
child.on("exit", (code) => resolve5(code ?? 0));
|
|
6121
6201
|
});
|
|
6122
6202
|
}
|
|
6123
6203
|
async function runEditor(args) {
|
|
@@ -6129,8 +6209,8 @@ async function runEditor(args) {
|
|
|
6129
6209
|
};
|
|
6130
6210
|
}
|
|
6131
6211
|
const { command } = resolveEditorCommand({ command: args.command });
|
|
6132
|
-
const
|
|
6133
|
-
const resolved =
|
|
6212
|
+
const resolve5 = args.resolveCommand ?? findOnPath;
|
|
6213
|
+
const resolved = resolve5(command);
|
|
6134
6214
|
if (!resolved) {
|
|
6135
6215
|
return {
|
|
6136
6216
|
ok: false,
|
|
@@ -6189,7 +6269,7 @@ async function runToolsEdit(args) {
|
|
|
6189
6269
|
if (!ctx)
|
|
6190
6270
|
return;
|
|
6191
6271
|
const cwd = process.cwd();
|
|
6192
|
-
const filePath =
|
|
6272
|
+
const filePath = join9(serverToolsDir(devRoot(cwd), ctx.serverId), `${args.toolName}.md`);
|
|
6193
6273
|
if (!existsSync11(filePath)) {
|
|
6194
6274
|
printError(`Tool file not found: ${relative2(cwd, filePath)}`);
|
|
6195
6275
|
printInfo(` ${c.dim("Run")} ${c.bold(`mcp tools pull ${args.toolName}`)} ${c.dim("first to materialize it, or check the name is spelled correctly.")}`);
|
|
@@ -7531,13 +7611,13 @@ function registerSkillTestCommands(skills) {
|
|
|
7531
7611
|
|
|
7532
7612
|
// src/commands/skills.ts
|
|
7533
7613
|
async function runClaudeMcpAdd(connectionName, mcpUrl) {
|
|
7534
|
-
return new Promise((
|
|
7614
|
+
return new Promise((resolve5) => {
|
|
7535
7615
|
const child = spawn3("claude", ["mcp", "add", "--transport", "http", connectionName, mcpUrl], {
|
|
7536
7616
|
stdio: "inherit",
|
|
7537
7617
|
shell: false
|
|
7538
7618
|
});
|
|
7539
|
-
child.on("error", () =>
|
|
7540
|
-
child.on("exit", (code) =>
|
|
7619
|
+
child.on("error", () => resolve5(127));
|
|
7620
|
+
child.on("exit", (code) => resolve5(code ?? 1));
|
|
7541
7621
|
});
|
|
7542
7622
|
}
|
|
7543
7623
|
function registerSkillCommands(program2) {
|
|
@@ -7854,9 +7934,9 @@ function registerApiKeyCommands(program2) {
|
|
|
7854
7934
|
|
|
7855
7935
|
// src/commands/config.ts
|
|
7856
7936
|
import { homedir as homedir2 } from "node:os";
|
|
7857
|
-
import { join as
|
|
7937
|
+
import { join as join10 } from "node:path";
|
|
7858
7938
|
function configFilePath() {
|
|
7859
|
-
return
|
|
7939
|
+
return join10(homedir2(), ".mcpcloud", "config.json");
|
|
7860
7940
|
}
|
|
7861
7941
|
function previewKey2(key) {
|
|
7862
7942
|
if (!key)
|
|
@@ -8097,12 +8177,12 @@ function registerConfigCommands(program2) {
|
|
|
8097
8177
|
}
|
|
8098
8178
|
|
|
8099
8179
|
// src/commands/dev.ts
|
|
8100
|
-
import { existsSync as
|
|
8101
|
-
import { isAbsolute as
|
|
8180
|
+
import { existsSync as existsSync32 } from "node:fs";
|
|
8181
|
+
import { isAbsolute as isAbsolute3, relative as relative7, resolve as resolve7 } from "node:path";
|
|
8102
8182
|
|
|
8103
8183
|
// src/lib/mcp-invoke.ts
|
|
8104
8184
|
import { existsSync as existsSync12, readFileSync as readFileSync10 } from "node:fs";
|
|
8105
|
-
import { resolve as
|
|
8185
|
+
import { resolve as resolve5 } from "node:path";
|
|
8106
8186
|
var DEFAULT_TIMEOUT_MS3 = 60000;
|
|
8107
8187
|
var invokeRequestId = 1;
|
|
8108
8188
|
async function invokeMcpTool(args) {
|
|
@@ -8239,7 +8319,7 @@ async function parseToolArguments(opts) {
|
|
|
8239
8319
|
const reader = opts.readStdin ?? defaultStdinReader;
|
|
8240
8320
|
source = await reader();
|
|
8241
8321
|
} else if (raw.startsWith("@")) {
|
|
8242
|
-
const filePath =
|
|
8322
|
+
const filePath = resolve5(opts.cwd ?? process.cwd(), raw.slice(1));
|
|
8243
8323
|
if (!existsSync12(filePath)) {
|
|
8244
8324
|
return { ok: false, reason: "file-not-found", path: filePath };
|
|
8245
8325
|
}
|
|
@@ -8320,29 +8400,29 @@ function truncateJsonPreview(value) {
|
|
|
8320
8400
|
// src/lib/dev/sessions.ts
|
|
8321
8401
|
import {
|
|
8322
8402
|
existsSync as existsSync13,
|
|
8323
|
-
mkdirSync as
|
|
8403
|
+
mkdirSync as mkdirSync7,
|
|
8324
8404
|
readFileSync as readFileSync11,
|
|
8325
8405
|
readdirSync as readdirSync2,
|
|
8326
8406
|
unlinkSync,
|
|
8327
|
-
writeFileSync as
|
|
8407
|
+
writeFileSync as writeFileSync6
|
|
8328
8408
|
} from "node:fs";
|
|
8329
8409
|
import { homedir as homedir3 } from "node:os";
|
|
8330
|
-
import { join as
|
|
8410
|
+
import { join as join11 } from "node:path";
|
|
8331
8411
|
var SESSIONS_DIR_NAME = "dev-sessions";
|
|
8332
8412
|
function sessionsDir() {
|
|
8333
|
-
return
|
|
8413
|
+
return join11(homedir3(), ".mcpcloud", SESSIONS_DIR_NAME);
|
|
8334
8414
|
}
|
|
8335
8415
|
function sessionFile(pid) {
|
|
8336
|
-
return
|
|
8416
|
+
return join11(sessionsDir(), `${pid}.json`);
|
|
8337
8417
|
}
|
|
8338
8418
|
function ensureDir() {
|
|
8339
8419
|
const dir = sessionsDir();
|
|
8340
8420
|
if (!existsSync13(dir))
|
|
8341
|
-
|
|
8421
|
+
mkdirSync7(dir, { recursive: true, mode: 448 });
|
|
8342
8422
|
}
|
|
8343
8423
|
function recordSession(record) {
|
|
8344
8424
|
ensureDir();
|
|
8345
|
-
|
|
8425
|
+
writeFileSync6(sessionFile(record.pid), JSON.stringify(record, null, 2), {
|
|
8346
8426
|
encoding: "utf-8",
|
|
8347
8427
|
mode: 384
|
|
8348
8428
|
});
|
|
@@ -8363,7 +8443,7 @@ function listSessions() {
|
|
|
8363
8443
|
for (const entry of readdirSync2(dir)) {
|
|
8364
8444
|
if (!entry.endsWith(".json"))
|
|
8365
8445
|
continue;
|
|
8366
|
-
const path =
|
|
8446
|
+
const path = join11(dir, entry);
|
|
8367
8447
|
try {
|
|
8368
8448
|
const parsed = JSON.parse(readFileSync11(path, "utf-8"));
|
|
8369
8449
|
if (typeof parsed.pid !== "number") {
|
|
@@ -8558,27 +8638,27 @@ function invokeResultToJson(result) {
|
|
|
8558
8638
|
import {
|
|
8559
8639
|
appendFileSync as appendFileSync2,
|
|
8560
8640
|
existsSync as existsSync14,
|
|
8561
|
-
mkdirSync as
|
|
8641
|
+
mkdirSync as mkdirSync8,
|
|
8562
8642
|
readFileSync as readFileSync12,
|
|
8563
8643
|
readdirSync as readdirSync3,
|
|
8564
8644
|
renameSync,
|
|
8565
8645
|
statSync as statSync2
|
|
8566
8646
|
} from "node:fs";
|
|
8567
|
-
import { join as
|
|
8647
|
+
import { join as join12 } from "node:path";
|
|
8568
8648
|
var INSPECTOR_DIRNAME = "inspector";
|
|
8569
8649
|
var ACTIVE_FILE = "calls.ndjson";
|
|
8570
8650
|
var ROTATED_PREFIX = "calls.";
|
|
8571
8651
|
var ROTATED_SUFFIX = ".ndjson";
|
|
8572
8652
|
var ROTATION_LIMIT_BYTES = 50 * 1024 * 1024;
|
|
8573
8653
|
function inspectorDir(cwd) {
|
|
8574
|
-
return
|
|
8654
|
+
return join12(devRoot(cwd), INSPECTOR_DIRNAME);
|
|
8575
8655
|
}
|
|
8576
8656
|
function ensureInspectorDir(dir) {
|
|
8577
8657
|
if (!existsSync14(dir))
|
|
8578
|
-
|
|
8658
|
+
mkdirSync8(dir, { recursive: true });
|
|
8579
8659
|
}
|
|
8580
8660
|
function activeFilePath(dir) {
|
|
8581
|
-
return
|
|
8661
|
+
return join12(dir, ACTIVE_FILE);
|
|
8582
8662
|
}
|
|
8583
8663
|
function isRotatedName(name) {
|
|
8584
8664
|
return name !== ACTIVE_FILE && name.startsWith(ROTATED_PREFIX) && name.endsWith(ROTATED_SUFFIX);
|
|
@@ -8588,7 +8668,7 @@ function listRotatedFiles(dir) {
|
|
|
8588
8668
|
return [];
|
|
8589
8669
|
const rotated = readdirSync3(dir).filter(isRotatedName);
|
|
8590
8670
|
rotated.sort();
|
|
8591
|
-
return rotated.map((n) =>
|
|
8671
|
+
return rotated.map((n) => join12(dir, n));
|
|
8592
8672
|
}
|
|
8593
8673
|
function parseLines(text) {
|
|
8594
8674
|
if (!text)
|
|
@@ -8813,7 +8893,7 @@ import {
|
|
|
8813
8893
|
closeSync,
|
|
8814
8894
|
statSync as statSync3
|
|
8815
8895
|
} from "node:fs";
|
|
8816
|
-
import { join as
|
|
8896
|
+
import { join as join13 } from "node:path";
|
|
8817
8897
|
var DEFAULT_INTERVAL_MS = 250;
|
|
8818
8898
|
var MAX_READ_CHUNK = 64 * 1024;
|
|
8819
8899
|
function registerDevTailCommand(dev) {
|
|
@@ -8822,7 +8902,7 @@ function registerDevTailCommand(dev) {
|
|
|
8822
8902
|
async function runDevTail(opts) {
|
|
8823
8903
|
const cwd = process.cwd();
|
|
8824
8904
|
const dir = inspectorDir(cwd);
|
|
8825
|
-
const activePath =
|
|
8905
|
+
const activePath = join13(dir, ACTIVE_FILE);
|
|
8826
8906
|
const intervalMs = parseInterval(opts.intervalMs);
|
|
8827
8907
|
if (!existsSync15(dir)) {
|
|
8828
8908
|
printInfo(`No inspector history yet at ${dir}. Run \`mcp dev\` to start capturing.`);
|
|
@@ -8930,7 +9010,7 @@ function emitLines(text, filter) {
|
|
|
8930
9010
|
}
|
|
8931
9011
|
}
|
|
8932
9012
|
function sleep(ms) {
|
|
8933
|
-
return new Promise((
|
|
9013
|
+
return new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
8934
9014
|
}
|
|
8935
9015
|
|
|
8936
9016
|
// ../../node_modules/.bun/@clack+core@1.3.1/node_modules/@clack/core/dist/index.mjs
|
|
@@ -9556,6 +9636,24 @@ var Q = class extends m {
|
|
|
9556
9636
|
}
|
|
9557
9637
|
}
|
|
9558
9638
|
};
|
|
9639
|
+
|
|
9640
|
+
class X extends m {
|
|
9641
|
+
get cursor() {
|
|
9642
|
+
return this.value ? 0 : 1;
|
|
9643
|
+
}
|
|
9644
|
+
get _value() {
|
|
9645
|
+
return this.cursor === 0;
|
|
9646
|
+
}
|
|
9647
|
+
constructor(t) {
|
|
9648
|
+
super(t, false), this.value = !!t.initialValue, this.on("userInput", () => {
|
|
9649
|
+
this.value = this._value;
|
|
9650
|
+
}), this.on("confirm", (s) => {
|
|
9651
|
+
this.output.write(import_sisteransi.cursor.move(0, -1)), this.value = s, this.state = "submit", this.close();
|
|
9652
|
+
}), this.on("cursor", () => {
|
|
9653
|
+
this.value = !this.value;
|
|
9654
|
+
});
|
|
9655
|
+
}
|
|
9656
|
+
}
|
|
9559
9657
|
var Z = { Y: { type: "year", len: 4 }, M: { type: "month", len: 2 }, D: { type: "day", len: 2 } };
|
|
9560
9658
|
function P(r) {
|
|
9561
9659
|
return [...r].map((t) => Z[t]);
|
|
@@ -9992,6 +10090,33 @@ var F2 = ({ cursor: t, options: i, style: s, output: r = process.stdout, maxItem
|
|
|
9992
10090
|
S2.push(G2);
|
|
9993
10091
|
return h2 && S2.push(l), S2;
|
|
9994
10092
|
};
|
|
10093
|
+
var ue = (t) => {
|
|
10094
|
+
const i = t.active ?? "Yes", s = t.inactive ?? "No";
|
|
10095
|
+
return new X({ active: i, inactive: s, signal: t.signal, input: t.input, output: t.output, initialValue: t.initialValue ?? true, render() {
|
|
10096
|
+
const r = t.withGuide ?? h.withGuide, u = `${P2(this.state)} `, n = r ? `${e("gray", $2)} ` : "", a = W(t.output, t.message, n, u), c2 = `${r ? `${e("gray", $2)}
|
|
10097
|
+
` : ""}${a}
|
|
10098
|
+
`, o = this.value ? i : s;
|
|
10099
|
+
switch (this.state) {
|
|
10100
|
+
case "submit": {
|
|
10101
|
+
const l = r ? `${e("gray", $2)} ` : "";
|
|
10102
|
+
return `${c2}${l}${e("dim", o)}`;
|
|
10103
|
+
}
|
|
10104
|
+
case "cancel": {
|
|
10105
|
+
const l = r ? `${e("gray", $2)} ` : "";
|
|
10106
|
+
return `${c2}${l}${e(["strikethrough", "dim"], o)}${r ? `
|
|
10107
|
+
${e("gray", $2)}` : ""}`;
|
|
10108
|
+
}
|
|
10109
|
+
default: {
|
|
10110
|
+
const l = r ? `${e("cyan", $2)} ` : "", d = r ? e("cyan", x2) : "";
|
|
10111
|
+
return `${c2}${l}${this.value ? `${e("green", z2)} ${i}` : `${e("dim", U2)} ${e("dim", i)}`}${t.vertical ? r ? `
|
|
10112
|
+
${e("cyan", $2)} ` : `
|
|
10113
|
+
` : ` ${e("dim", "/")} `}${this.value ? `${e("dim", U2)} ${e("dim", s)}` : `${e("green", z2)} ${s}`}
|
|
10114
|
+
${d}
|
|
10115
|
+
`;
|
|
10116
|
+
}
|
|
10117
|
+
}
|
|
10118
|
+
} }).prompt();
|
|
10119
|
+
};
|
|
9995
10120
|
var me = (t = "", i) => {
|
|
9996
10121
|
const s = i?.output ?? process.stdout, r = i?.withGuide ?? h.withGuide ? `${e("gray", x2)} ` : "";
|
|
9997
10122
|
s.write(`${r}${e("red", t)}
|
|
@@ -10092,6 +10217,18 @@ async function resolveDevTarget(deps) {
|
|
|
10092
10217
|
}
|
|
10093
10218
|
};
|
|
10094
10219
|
}
|
|
10220
|
+
async function promptResumeOrSwitch(args) {
|
|
10221
|
+
const label = args.serverName ? `${args.serverName} ${c.dim(`(${args.serverId})`)}` : args.serverId;
|
|
10222
|
+
const resume = await ue({
|
|
10223
|
+
message: `Resume ${label}?`,
|
|
10224
|
+
active: "Resume",
|
|
10225
|
+
inactive: "Pick a different server",
|
|
10226
|
+
initialValue: true
|
|
10227
|
+
});
|
|
10228
|
+
if (q(resume))
|
|
10229
|
+
return "cancel";
|
|
10230
|
+
return resume ? "resume" : "switch";
|
|
10231
|
+
}
|
|
10095
10232
|
function canRunInteractive() {
|
|
10096
10233
|
if (isCiMode())
|
|
10097
10234
|
return false;
|
|
@@ -10160,12 +10297,12 @@ async function runInteractiveBootstrap() {
|
|
|
10160
10297
|
// src/lib/dev/bundle-sync.ts
|
|
10161
10298
|
import {
|
|
10162
10299
|
existsSync as existsSync16,
|
|
10163
|
-
mkdirSync as
|
|
10300
|
+
mkdirSync as mkdirSync9,
|
|
10164
10301
|
readFileSync as readFileSync13,
|
|
10165
10302
|
rmSync as rmSync2,
|
|
10166
|
-
writeFileSync as
|
|
10303
|
+
writeFileSync as writeFileSync7
|
|
10167
10304
|
} from "node:fs";
|
|
10168
|
-
import { dirname as
|
|
10305
|
+
import { dirname as dirname5, join as join14 } from "node:path";
|
|
10169
10306
|
async function resolveServerProject(args) {
|
|
10170
10307
|
const data = await api.get("/api/v1/server", {
|
|
10171
10308
|
organizationId: args.organizationId,
|
|
@@ -10177,6 +10314,20 @@ async function resolveServerProject(args) {
|
|
|
10177
10314
|
version: data.server.version
|
|
10178
10315
|
};
|
|
10179
10316
|
}
|
|
10317
|
+
async function resolveServerGitLink(args) {
|
|
10318
|
+
const data = await api.get("/api/v1/server/git-link", {
|
|
10319
|
+
organizationId: args.organizationId,
|
|
10320
|
+
serverId: args.serverId
|
|
10321
|
+
});
|
|
10322
|
+
return data.gitLink ?? null;
|
|
10323
|
+
}
|
|
10324
|
+
async function resolveServerGitPullStatus(args) {
|
|
10325
|
+
const data = await api.get("/api/v1/server/git-sync-status", {
|
|
10326
|
+
organizationId: args.organizationId,
|
|
10327
|
+
serverId: args.serverId
|
|
10328
|
+
});
|
|
10329
|
+
return data.pull ?? null;
|
|
10330
|
+
}
|
|
10180
10331
|
async function fetchBundle(args) {
|
|
10181
10332
|
requireApiKey();
|
|
10182
10333
|
const url = new URL("/api/v1/artifacts/download", getBaseUrl());
|
|
@@ -10208,7 +10359,7 @@ async function fetchBundle(args) {
|
|
|
10208
10359
|
}
|
|
10209
10360
|
function materializeBundle(destDir, bundle, options = {}) {
|
|
10210
10361
|
if (!existsSync16(destDir)) {
|
|
10211
|
-
|
|
10362
|
+
mkdirSync9(destDir, { recursive: true });
|
|
10212
10363
|
}
|
|
10213
10364
|
const preserve = options.preservePaths;
|
|
10214
10365
|
const kept = new Set;
|
|
@@ -10223,9 +10374,9 @@ function materializeBundle(destDir, bundle, options = {}) {
|
|
|
10223
10374
|
preservedCount += 1;
|
|
10224
10375
|
continue;
|
|
10225
10376
|
}
|
|
10226
|
-
const target =
|
|
10227
|
-
|
|
10228
|
-
|
|
10377
|
+
const target = join14(destDir, safePath);
|
|
10378
|
+
mkdirSync9(dirname5(target), { recursive: true });
|
|
10379
|
+
writeFileSync7(target, file.content, "utf-8");
|
|
10229
10380
|
writtenCount += 1;
|
|
10230
10381
|
}
|
|
10231
10382
|
let removed = 0;
|
|
@@ -10253,7 +10404,7 @@ function pruneStaleFiles(rootDir, currentDir, kept) {
|
|
|
10253
10404
|
const { relative: relative6 } = __require("node:path");
|
|
10254
10405
|
let removed = 0;
|
|
10255
10406
|
for (const entry of readdirSync4(currentDir)) {
|
|
10256
|
-
const abs =
|
|
10407
|
+
const abs = join14(currentDir, entry);
|
|
10257
10408
|
const stat = statSync4(abs);
|
|
10258
10409
|
if (stat.isDirectory()) {
|
|
10259
10410
|
removed += pruneStaleFiles(rootDir, abs, kept);
|
|
@@ -10274,10 +10425,53 @@ function pruneStaleFiles(rootDir, currentDir, kept) {
|
|
|
10274
10425
|
return removed;
|
|
10275
10426
|
}
|
|
10276
10427
|
|
|
10428
|
+
// src/lib/dev/git-pull-poll.ts
|
|
10429
|
+
var DEFAULT_INTERVAL_MS2 = 6000;
|
|
10430
|
+
function reducePollState(baseline, status) {
|
|
10431
|
+
if (!status)
|
|
10432
|
+
return { baseline, nudge: false };
|
|
10433
|
+
const sha = status.lastPulledGitSha;
|
|
10434
|
+
if (baseline === undefined)
|
|
10435
|
+
return { baseline: sha, nudge: false };
|
|
10436
|
+
if (sha && sha !== baseline)
|
|
10437
|
+
return { baseline: sha, nudge: true };
|
|
10438
|
+
return { baseline, nudge: false };
|
|
10439
|
+
}
|
|
10440
|
+
function startGitPullPoll(args) {
|
|
10441
|
+
const intervalMs = args.intervalMs ?? DEFAULT_INTERVAL_MS2;
|
|
10442
|
+
let baseline;
|
|
10443
|
+
let stopped = false;
|
|
10444
|
+
let timer = null;
|
|
10445
|
+
const tick = async () => {
|
|
10446
|
+
if (stopped)
|
|
10447
|
+
return;
|
|
10448
|
+
try {
|
|
10449
|
+
const status = await args.fetchStatus();
|
|
10450
|
+
const next = reducePollState(baseline, status);
|
|
10451
|
+
baseline = next.baseline;
|
|
10452
|
+
if (next.nudge && status)
|
|
10453
|
+
args.onChange(status);
|
|
10454
|
+
} catch (err) {
|
|
10455
|
+
args.onError?.(err);
|
|
10456
|
+
} finally {
|
|
10457
|
+
if (!stopped)
|
|
10458
|
+
timer = setTimeout(() => void tick(), intervalMs);
|
|
10459
|
+
}
|
|
10460
|
+
};
|
|
10461
|
+
tick();
|
|
10462
|
+
return {
|
|
10463
|
+
close() {
|
|
10464
|
+
stopped = true;
|
|
10465
|
+
if (timer)
|
|
10466
|
+
clearTimeout(timer);
|
|
10467
|
+
}
|
|
10468
|
+
};
|
|
10469
|
+
}
|
|
10470
|
+
|
|
10277
10471
|
// src/lib/dev/editor.ts
|
|
10278
10472
|
import { spawn as spawn4 } from "node:child_process";
|
|
10279
10473
|
import { existsSync as existsSync17, statSync as statSync4 } from "node:fs";
|
|
10280
|
-
import { delimiter as delimiter2, join as
|
|
10474
|
+
import { delimiter as delimiter2, join as join15 } from "node:path";
|
|
10281
10475
|
import { platform as platform3 } from "node:os";
|
|
10282
10476
|
var TARGET_LABEL = {
|
|
10283
10477
|
tools: "tool metadata",
|
|
@@ -10313,7 +10507,7 @@ function findOnPath2(command) {
|
|
|
10313
10507
|
if (!dir)
|
|
10314
10508
|
continue;
|
|
10315
10509
|
for (const ext of exts) {
|
|
10316
|
-
const candidate =
|
|
10510
|
+
const candidate = join15(dir, command + ext);
|
|
10317
10511
|
if (existsSync17(candidate) && isExecutable(candidate))
|
|
10318
10512
|
return candidate;
|
|
10319
10513
|
}
|
|
@@ -10595,7 +10789,7 @@ function parseOpenChoice(raw) {
|
|
|
10595
10789
|
|
|
10596
10790
|
// src/lib/dev/file-watcher.ts
|
|
10597
10791
|
import { existsSync as existsSync18, statSync as statSync5, watch as fsWatch2 } from "node:fs";
|
|
10598
|
-
import { join as
|
|
10792
|
+
import { join as join16 } from "node:path";
|
|
10599
10793
|
var DEFAULT_IGNORE = ["node_modules", ".git", "_mcpsh_host.mjs"];
|
|
10600
10794
|
function watchDir(options) {
|
|
10601
10795
|
if (!existsSync18(options.rootDir)) {
|
|
@@ -10634,7 +10828,7 @@ function watchDir(options) {
|
|
|
10634
10828
|
for (const entry of readdirSync4(options.rootDir)) {
|
|
10635
10829
|
if (ignore.has(entry))
|
|
10636
10830
|
continue;
|
|
10637
|
-
const sub =
|
|
10831
|
+
const sub = join16(options.rootDir, entry);
|
|
10638
10832
|
try {
|
|
10639
10833
|
const stat = statSync5(sub);
|
|
10640
10834
|
if (!stat.isDirectory())
|
|
@@ -10901,7 +11095,7 @@ async function handleOne2(args) {
|
|
|
10901
11095
|
// src/lib/dev/local-runtime.ts
|
|
10902
11096
|
import { spawn as spawn5 } from "node:child_process";
|
|
10903
11097
|
import { existsSync as existsSync21 } from "node:fs";
|
|
10904
|
-
import { join as
|
|
11098
|
+
import { join as join17 } from "node:path";
|
|
10905
11099
|
function detectRuntime(preferred = "auto") {
|
|
10906
11100
|
if (preferred === "bun" || preferred === "node")
|
|
10907
11101
|
return preferred;
|
|
@@ -10938,50 +11132,160 @@ function startRuntime(options) {
|
|
|
10938
11132
|
if (child.exitCode !== null)
|
|
10939
11133
|
return;
|
|
10940
11134
|
child.kill("SIGTERM");
|
|
10941
|
-
await new Promise((
|
|
11135
|
+
await new Promise((resolve6) => {
|
|
10942
11136
|
const timer = setTimeout(() => {
|
|
10943
11137
|
if (child.exitCode === null)
|
|
10944
11138
|
child.kill("SIGKILL");
|
|
10945
|
-
|
|
11139
|
+
resolve6();
|
|
10946
11140
|
}, 3000);
|
|
10947
11141
|
child.once("exit", () => {
|
|
10948
11142
|
clearTimeout(timer);
|
|
10949
|
-
|
|
11143
|
+
resolve6();
|
|
10950
11144
|
});
|
|
10951
11145
|
});
|
|
10952
11146
|
}
|
|
10953
11147
|
};
|
|
10954
11148
|
}
|
|
10955
11149
|
async function installDependencies(options) {
|
|
10956
|
-
const pkgPath =
|
|
11150
|
+
const pkgPath = join17(options.serverDir, "package.json");
|
|
10957
11151
|
if (!existsSync21(pkgPath))
|
|
10958
11152
|
return { ran: false, exitCode: null };
|
|
10959
|
-
const nodeModules =
|
|
11153
|
+
const nodeModules = join17(options.serverDir, "node_modules");
|
|
10960
11154
|
if (options.skipIfPresent !== false && existsSync21(nodeModules)) {
|
|
10961
11155
|
return { ran: false, exitCode: 0 };
|
|
10962
11156
|
}
|
|
10963
11157
|
options.onStep?.("Installing dependencies (bun install)…");
|
|
10964
|
-
return await new Promise((
|
|
11158
|
+
return await new Promise((resolve6) => {
|
|
10965
11159
|
const child = spawn5("bun", ["install", "--silent"], {
|
|
10966
11160
|
cwd: options.serverDir,
|
|
10967
11161
|
stdio: "inherit"
|
|
10968
11162
|
});
|
|
10969
|
-
child.on("error", () =>
|
|
10970
|
-
child.on("exit", (code) =>
|
|
11163
|
+
child.on("error", () => resolve6({ ran: true, exitCode: 127 }));
|
|
11164
|
+
child.on("exit", (code) => resolve6({ ran: true, exitCode: code }));
|
|
10971
11165
|
});
|
|
10972
11166
|
}
|
|
10973
11167
|
|
|
10974
11168
|
// src/lib/dev/prepare.ts
|
|
10975
|
-
import { existsSync as
|
|
10976
|
-
import { join as
|
|
11169
|
+
import { existsSync as existsSync25, writeFileSync as writeFileSync11 } from "node:fs";
|
|
11170
|
+
import { join as join21, relative as relative6 } from "node:path";
|
|
11171
|
+
|
|
11172
|
+
// src/lib/dev/git-clone.ts
|
|
11173
|
+
import { spawnSync } from "node:child_process";
|
|
11174
|
+
import {
|
|
11175
|
+
existsSync as existsSync22,
|
|
11176
|
+
mkdirSync as mkdirSync10,
|
|
11177
|
+
readFileSync as readFileSync14,
|
|
11178
|
+
writeFileSync as writeFileSync8
|
|
11179
|
+
} from "node:fs";
|
|
11180
|
+
import { dirname as dirname6, join as join18 } from "node:path";
|
|
11181
|
+
|
|
11182
|
+
class GitNotAvailableError extends Error {
|
|
11183
|
+
constructor() {
|
|
11184
|
+
super("git is not installed or not on your PATH. Install Git to use Git-native dev mode.");
|
|
11185
|
+
this.name = "GitNotAvailableError";
|
|
11186
|
+
}
|
|
11187
|
+
}
|
|
11188
|
+
|
|
11189
|
+
class GitCloneError extends Error {
|
|
11190
|
+
constructor(message) {
|
|
11191
|
+
super(message);
|
|
11192
|
+
this.name = "GitCloneError";
|
|
11193
|
+
}
|
|
11194
|
+
}
|
|
11195
|
+
var GIT_LOCAL_EXCLUDES = [
|
|
11196
|
+
"_mcpsh_*",
|
|
11197
|
+
"node_modules/",
|
|
11198
|
+
"bun.lock"
|
|
11199
|
+
];
|
|
11200
|
+
var EXCLUDE_HEADER = "# added by `mcp dev` (Git-native mode)";
|
|
11201
|
+
function gitCloneArgs(httpsCloneUrl, branch, repoDir) {
|
|
11202
|
+
return [
|
|
11203
|
+
"clone",
|
|
11204
|
+
"--branch",
|
|
11205
|
+
branch,
|
|
11206
|
+
"--single-branch",
|
|
11207
|
+
httpsCloneUrl,
|
|
11208
|
+
repoDir
|
|
11209
|
+
];
|
|
11210
|
+
}
|
|
11211
|
+
function gitFetchArgs(branch) {
|
|
11212
|
+
return ["fetch", "--quiet", "origin", branch];
|
|
11213
|
+
}
|
|
11214
|
+
function mergeLocalExcludes(existing) {
|
|
11215
|
+
const lines = existing.split(`
|
|
11216
|
+
`);
|
|
11217
|
+
const present = new Set(lines.map((l) => l.trim()));
|
|
11218
|
+
const missing = GIT_LOCAL_EXCLUDES.filter((pattern) => !present.has(pattern));
|
|
11219
|
+
if (missing.length === 0)
|
|
11220
|
+
return null;
|
|
11221
|
+
const prefix = existing.length > 0 && !existing.endsWith(`
|
|
11222
|
+
`) ? `
|
|
11223
|
+
` : "";
|
|
11224
|
+
const block = present.has(EXCLUDE_HEADER) ? missing.join(`
|
|
11225
|
+
`) : [EXCLUDE_HEADER, ...missing].join(`
|
|
11226
|
+
`);
|
|
11227
|
+
return `${existing}${prefix}${block}
|
|
11228
|
+
`;
|
|
11229
|
+
}
|
|
11230
|
+
function runGit(args, cwd) {
|
|
11231
|
+
const res = spawnSync("git", args, {
|
|
11232
|
+
cwd,
|
|
11233
|
+
encoding: "utf-8",
|
|
11234
|
+
stdio: ["inherit", "pipe", "pipe"]
|
|
11235
|
+
});
|
|
11236
|
+
if (res.error && res.error.code === "ENOENT") {
|
|
11237
|
+
throw new GitNotAvailableError;
|
|
11238
|
+
}
|
|
11239
|
+
return {
|
|
11240
|
+
status: res.status ?? 1,
|
|
11241
|
+
stdout: res.stdout ?? "",
|
|
11242
|
+
stderr: res.stderr ?? ""
|
|
11243
|
+
};
|
|
11244
|
+
}
|
|
11245
|
+
function isGitAvailable() {
|
|
11246
|
+
try {
|
|
11247
|
+
return runGit(["--version"]).status === 0;
|
|
11248
|
+
} catch {
|
|
11249
|
+
return false;
|
|
11250
|
+
}
|
|
11251
|
+
}
|
|
11252
|
+
function applyLocalExcludes(repoDir) {
|
|
11253
|
+
const excludePath = join18(repoDir, ".git", "info", "exclude");
|
|
11254
|
+
const existing = existsSync22(excludePath) ? readFileSync14(excludePath, "utf-8") : "";
|
|
11255
|
+
const next = mergeLocalExcludes(existing);
|
|
11256
|
+
if (next === null)
|
|
11257
|
+
return;
|
|
11258
|
+
if (!existsSync22(dirname6(excludePath))) {
|
|
11259
|
+
mkdirSync10(dirname6(excludePath), { recursive: true });
|
|
11260
|
+
}
|
|
11261
|
+
writeFileSync8(excludePath, next, "utf-8");
|
|
11262
|
+
}
|
|
11263
|
+
function ensureClone(opts) {
|
|
11264
|
+
if (!isGitAvailable()) {
|
|
11265
|
+
throw new GitNotAvailableError;
|
|
11266
|
+
}
|
|
11267
|
+
if (existsSync22(join18(opts.repoDir, ".git"))) {
|
|
11268
|
+
opts.onStep?.("Refreshing existing clone (git fetch)…");
|
|
11269
|
+
runGit(gitFetchArgs(opts.branch), opts.repoDir);
|
|
11270
|
+
applyLocalExcludes(opts.repoDir);
|
|
11271
|
+
return { cloned: false };
|
|
11272
|
+
}
|
|
11273
|
+
opts.onStep?.(`Cloning ${opts.httpsCloneUrl} (branch ${opts.branch})…`);
|
|
11274
|
+
const res = runGit(gitCloneArgs(opts.httpsCloneUrl, opts.branch, opts.repoDir));
|
|
11275
|
+
if (res.status !== 0) {
|
|
11276
|
+
throw new GitCloneError(`git clone failed (exit ${res.status}). ${res.stderr.trim() || "Check that you have access to the repository with your local git credentials."}`);
|
|
11277
|
+
}
|
|
11278
|
+
applyLocalExcludes(opts.repoDir);
|
|
11279
|
+
return { cloned: true };
|
|
11280
|
+
}
|
|
10977
11281
|
|
|
10978
11282
|
// src/lib/dev/host-runtime.ts
|
|
10979
|
-
import { existsSync as
|
|
10980
|
-
import { dirname as
|
|
11283
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync12, writeFileSync as writeFileSync10 } from "node:fs";
|
|
11284
|
+
import { dirname as dirname7, join as join20 } from "node:path";
|
|
10981
11285
|
|
|
10982
11286
|
// src/lib/dev/inspector-module.ts
|
|
10983
|
-
import { existsSync as
|
|
10984
|
-
import { join as
|
|
11287
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync11, writeFileSync as writeFileSync9 } from "node:fs";
|
|
11288
|
+
import { join as join19 } from "node:path";
|
|
10985
11289
|
|
|
10986
11290
|
// src/lib/dev/inspector-assets.ts
|
|
10987
11291
|
var INSPECTOR_HTML = `<!doctype html>
|
|
@@ -11482,7 +11786,7 @@ function requireReact_production() {
|
|
|
11482
11786
|
react_production.useTransition = function() {
|
|
11483
11787
|
return ReactSharedInternals.H.useTransition();
|
|
11484
11788
|
};
|
|
11485
|
-
react_production.version = "19.2.
|
|
11789
|
+
react_production.version = "19.2.7";
|
|
11486
11790
|
return react_production;
|
|
11487
11791
|
}
|
|
11488
11792
|
var hasRequiredReact;
|
|
@@ -11909,7 +12213,7 @@ function requireReactDom_production() {
|
|
|
11909
12213
|
reactDom_production.useFormStatus = function() {
|
|
11910
12214
|
return ReactSharedInternals.H.useHostTransitionStatus();
|
|
11911
12215
|
};
|
|
11912
|
-
reactDom_production.version = "19.2.
|
|
12216
|
+
reactDom_production.version = "19.2.7";
|
|
11913
12217
|
return reactDom_production;
|
|
11914
12218
|
}
|
|
11915
12219
|
var hasRequiredReactDom;
|
|
@@ -23353,12 +23657,12 @@ function requireReactDomClient_production() {
|
|
|
23353
23657
|
}
|
|
23354
23658
|
};
|
|
23355
23659
|
var isomorphicReactPackageVersion$jscomp$inline_1840 = React.version;
|
|
23356
|
-
if ("19.2.
|
|
23660
|
+
if ("19.2.7" !== isomorphicReactPackageVersion$jscomp$inline_1840)
|
|
23357
23661
|
throw Error(
|
|
23358
23662
|
formatProdErrorMessage(
|
|
23359
23663
|
527,
|
|
23360
23664
|
isomorphicReactPackageVersion$jscomp$inline_1840,
|
|
23361
|
-
"19.2.
|
|
23665
|
+
"19.2.7"
|
|
23362
23666
|
)
|
|
23363
23667
|
);
|
|
23364
23668
|
ReactDOMSharedInternals.findDOMNode = function(componentOrElement) {
|
|
@@ -23376,10 +23680,10 @@ function requireReactDomClient_production() {
|
|
|
23376
23680
|
};
|
|
23377
23681
|
var internals$jscomp$inline_2347 = {
|
|
23378
23682
|
bundleType: 0,
|
|
23379
|
-
version: "19.2.
|
|
23683
|
+
version: "19.2.7",
|
|
23380
23684
|
rendererPackageName: "react-dom",
|
|
23381
23685
|
currentDispatcherRef: ReactSharedInternals,
|
|
23382
|
-
reconcilerVersion: "19.2.
|
|
23686
|
+
reconcilerVersion: "19.2.7"
|
|
23383
23687
|
};
|
|
23384
23688
|
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
|
|
23385
23689
|
var hook$jscomp$inline_2348 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|
@@ -23446,7 +23750,7 @@ function requireReactDomClient_production() {
|
|
|
23446
23750
|
listenToAllSupportedEvents(container);
|
|
23447
23751
|
return new ReactDOMHydrationRoot(initialChildren);
|
|
23448
23752
|
};
|
|
23449
|
-
reactDomClient_production.version = "19.2.
|
|
23753
|
+
reactDomClient_production.version = "19.2.7";
|
|
23450
23754
|
return reactDomClient_production;
|
|
23451
23755
|
}
|
|
23452
23756
|
var hasRequiredClient;
|
|
@@ -26116,10 +26420,10 @@ function buildInspectorModule() {
|
|
|
26116
26420
|
`);
|
|
26117
26421
|
}
|
|
26118
26422
|
function writeInspectorModule(serverDir2) {
|
|
26119
|
-
if (!
|
|
26120
|
-
|
|
26121
|
-
const target =
|
|
26122
|
-
|
|
26423
|
+
if (!existsSync23(serverDir2))
|
|
26424
|
+
mkdirSync11(serverDir2, { recursive: true });
|
|
26425
|
+
const target = join19(serverDir2, INSPECTOR_FILE_NAME);
|
|
26426
|
+
writeFileSync9(target, buildInspectorModule(), "utf-8");
|
|
26123
26427
|
return target;
|
|
26124
26428
|
}
|
|
26125
26429
|
|
|
@@ -26298,11 +26602,11 @@ function buildHostScript(options) {
|
|
|
26298
26602
|
`);
|
|
26299
26603
|
}
|
|
26300
26604
|
function writeHostScript(serverDir2, options) {
|
|
26301
|
-
if (!
|
|
26302
|
-
|
|
26303
|
-
const target =
|
|
26304
|
-
|
|
26305
|
-
|
|
26605
|
+
if (!existsSync24(serverDir2))
|
|
26606
|
+
mkdirSync12(serverDir2, { recursive: true });
|
|
26607
|
+
const target = join20(serverDir2, HOST_FILE_NAME);
|
|
26608
|
+
mkdirSync12(dirname7(target), { recursive: true });
|
|
26609
|
+
writeFileSync10(target, buildHostScript(options), "utf-8");
|
|
26306
26610
|
if (options.inspector) {
|
|
26307
26611
|
writeInspectorModule(serverDir2);
|
|
26308
26612
|
}
|
|
@@ -26314,12 +26618,50 @@ var ENTRY_CANDIDATES = ["src/worker.ts", "src/server.ts", "src/index.ts"];
|
|
|
26314
26618
|
async function prepareDev(opts) {
|
|
26315
26619
|
const cwd = opts.cwd;
|
|
26316
26620
|
ensureGitignore(cwd);
|
|
26317
|
-
|
|
26621
|
+
let dest = serverDir(cwd);
|
|
26318
26622
|
const env = envFile(cwd);
|
|
26319
26623
|
const backups = backupsDir(cwd);
|
|
26320
26624
|
const cached2 = readState(cwd);
|
|
26321
26625
|
let projectId = cached2?.projectId;
|
|
26322
|
-
|
|
26626
|
+
let serverName = cached2?.serverName;
|
|
26627
|
+
let gitNative = false;
|
|
26628
|
+
let repoDir;
|
|
26629
|
+
const gitLink = opts.offline ? null : await resolveServerGitLink({
|
|
26630
|
+
serverId: opts.serverId,
|
|
26631
|
+
organizationId: opts.organizationId
|
|
26632
|
+
}).catch(() => null);
|
|
26633
|
+
if (gitLink) {
|
|
26634
|
+
gitNative = true;
|
|
26635
|
+
projectId = projectId ?? gitLink.projectId;
|
|
26636
|
+
serverName = `${gitLink.owner}/${gitLink.name}`;
|
|
26637
|
+
repoDir = gitCloneDir(cwd, gitLink.owner, gitLink.name);
|
|
26638
|
+
dest = gitLink.pathPrefix ? join21(repoDir, gitLink.pathPrefix) : repoDir;
|
|
26639
|
+
printStep(`Git-linked server — cloning ${gitLink.owner}/${gitLink.name}@${gitLink.branch}…`);
|
|
26640
|
+
try {
|
|
26641
|
+
const result = ensureClone({
|
|
26642
|
+
repoDir,
|
|
26643
|
+
httpsCloneUrl: gitLink.httpsCloneUrl,
|
|
26644
|
+
branch: gitLink.branch,
|
|
26645
|
+
onStep: (m2) => printStep(m2)
|
|
26646
|
+
});
|
|
26647
|
+
printSuccess(result.cloned ? `Cloned to ${relative6(cwd, repoDir)}` : `Using existing clone at ${relative6(cwd, repoDir)}`);
|
|
26648
|
+
} catch (err) {
|
|
26649
|
+
if (err instanceof GitNotAvailableError) {
|
|
26650
|
+
printError(err.message);
|
|
26651
|
+
} else if (err instanceof GitCloneError) {
|
|
26652
|
+
printError(err.message);
|
|
26653
|
+
} else {
|
|
26654
|
+
printError(`Git clone failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
26655
|
+
}
|
|
26656
|
+
process.exit(1);
|
|
26657
|
+
}
|
|
26658
|
+
if (!existsSync25(env)) {
|
|
26659
|
+
writeFileSync11(env, `{}
|
|
26660
|
+
`, "utf-8");
|
|
26661
|
+
printInfo(` → seeded ${relative6(cwd, env)} (empty; fill secrets here)`);
|
|
26662
|
+
}
|
|
26663
|
+
printInfo(` ${"Open the clone in your editor; commit + push to sync (config under mcpcloud/ is read-only)."}`);
|
|
26664
|
+
} else if (!opts.offline) {
|
|
26323
26665
|
if (!projectId) {
|
|
26324
26666
|
printStep(`Looking up server ${opts.serverId}…`);
|
|
26325
26667
|
const lookup = await resolveServerProject({
|
|
@@ -26327,6 +26669,7 @@ async function prepareDev(opts) {
|
|
|
26327
26669
|
organizationId: opts.organizationId
|
|
26328
26670
|
});
|
|
26329
26671
|
projectId = lookup.projectId;
|
|
26672
|
+
serverName = lookup.serverName;
|
|
26330
26673
|
printInfo(` → ${lookup.serverName} (project ${projectId})`);
|
|
26331
26674
|
}
|
|
26332
26675
|
printStep("Fetching bundle…");
|
|
@@ -26337,8 +26680,8 @@ async function prepareDev(opts) {
|
|
|
26337
26680
|
});
|
|
26338
26681
|
const result = materializeBundle(dest, bundle, { prune: true });
|
|
26339
26682
|
printInfo(` → wrote ${result.filesWritten} files (pruned ${result.filesRemoved})`);
|
|
26340
|
-
if (!
|
|
26341
|
-
|
|
26683
|
+
if (!existsSync25(env)) {
|
|
26684
|
+
writeFileSync11(env, `{}
|
|
26342
26685
|
`, "utf-8");
|
|
26343
26686
|
printInfo(` → seeded ${relative6(cwd, env)} (empty; fill secrets here)`);
|
|
26344
26687
|
}
|
|
@@ -26355,7 +26698,7 @@ async function prepareDev(opts) {
|
|
|
26355
26698
|
});
|
|
26356
26699
|
printInfo(` → wrote ${toolsResult.filesWritten} editable tool file${toolsResult.filesWritten === 1 ? "" : "s"}${toolsResult.filesRemoved > 0 ? ` (pruned ${toolsResult.filesRemoved})` : ""}`);
|
|
26357
26700
|
} else {
|
|
26358
|
-
if (!
|
|
26701
|
+
if (!existsSync25(dest)) {
|
|
26359
26702
|
printError("No cached bundle found. Run once without --offline first.");
|
|
26360
26703
|
process.exit(1);
|
|
26361
26704
|
}
|
|
@@ -26364,7 +26707,7 @@ async function prepareDev(opts) {
|
|
|
26364
26707
|
process.exit(1);
|
|
26365
26708
|
}
|
|
26366
26709
|
}
|
|
26367
|
-
const entryRel = ENTRY_CANDIDATES.find((c2) =>
|
|
26710
|
+
const entryRel = ENTRY_CANDIDATES.find((c2) => existsSync25(join21(dest, c2)));
|
|
26368
26711
|
if (!entryRel) {
|
|
26369
26712
|
printError("Could not locate an entry file (expected src/worker.ts).");
|
|
26370
26713
|
process.exit(1);
|
|
@@ -26385,7 +26728,8 @@ async function prepareDev(opts) {
|
|
|
26385
26728
|
bundleVersion: 0,
|
|
26386
26729
|
lastSyncedAt: Date.now(),
|
|
26387
26730
|
transports: opts.transports,
|
|
26388
|
-
port: opts.port
|
|
26731
|
+
port: opts.port,
|
|
26732
|
+
...serverName !== undefined ? { serverName } : {}
|
|
26389
26733
|
};
|
|
26390
26734
|
writeState(cwd, state);
|
|
26391
26735
|
const storePath = inspectorDir(cwd);
|
|
@@ -26403,7 +26747,9 @@ async function prepareDev(opts) {
|
|
|
26403
26747
|
destDir: dest,
|
|
26404
26748
|
envPath: env,
|
|
26405
26749
|
backupsPath: backups,
|
|
26406
|
-
state
|
|
26750
|
+
state,
|
|
26751
|
+
gitNative,
|
|
26752
|
+
...repoDir !== undefined ? { repoDir } : {}
|
|
26407
26753
|
};
|
|
26408
26754
|
}
|
|
26409
26755
|
|
|
@@ -26519,73 +26865,73 @@ function createBurstGuard() {
|
|
|
26519
26865
|
}
|
|
26520
26866
|
|
|
26521
26867
|
// src/lib/dev/agent-connectors/claude-code.ts
|
|
26522
|
-
import { existsSync as
|
|
26868
|
+
import { existsSync as existsSync27, unlinkSync as unlinkSync2, writeFileSync as writeFileSync13 } from "node:fs";
|
|
26523
26869
|
import { homedir as homedir4 } from "node:os";
|
|
26524
|
-
import { join as
|
|
26870
|
+
import { join as join23 } from "node:path";
|
|
26525
26871
|
|
|
26526
26872
|
// src/lib/dev/agent-connectors/json-config-utils.ts
|
|
26527
|
-
import { existsSync as
|
|
26528
|
-
import { dirname as
|
|
26873
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync13, readFileSync as readFileSync15, writeFileSync as writeFileSync12 } from "node:fs";
|
|
26874
|
+
import { dirname as dirname8, join as join22, basename } from "node:path";
|
|
26529
26875
|
function readJsonFile(path) {
|
|
26530
|
-
if (!
|
|
26876
|
+
if (!existsSync26(path))
|
|
26531
26877
|
return { ok: true, value: {} };
|
|
26532
26878
|
try {
|
|
26533
|
-
const raw =
|
|
26879
|
+
const raw = readFileSync15(path, "utf-8");
|
|
26534
26880
|
if (!raw.trim())
|
|
26535
26881
|
return { ok: true, value: {} };
|
|
26536
26882
|
return { ok: true, value: JSON.parse(raw) };
|
|
26537
26883
|
} catch {
|
|
26538
26884
|
try {
|
|
26539
|
-
return { ok: false, raw:
|
|
26885
|
+
return { ok: false, raw: readFileSync15(path, "utf-8") };
|
|
26540
26886
|
} catch {
|
|
26541
26887
|
return { ok: false };
|
|
26542
26888
|
}
|
|
26543
26889
|
}
|
|
26544
26890
|
}
|
|
26545
26891
|
function atomicWriteJson(path, value) {
|
|
26546
|
-
|
|
26892
|
+
mkdirSync13(dirname8(path), { recursive: true });
|
|
26547
26893
|
const tmp = `${path}.mcpsh-${process.pid}-${Date.now()}.tmp`;
|
|
26548
|
-
|
|
26894
|
+
writeFileSync12(tmp, JSON.stringify(value, null, 2) + `
|
|
26549
26895
|
`, "utf-8");
|
|
26550
26896
|
const { renameSync: renameSync2 } = __require("node:fs");
|
|
26551
26897
|
renameSync2(tmp, path);
|
|
26552
26898
|
}
|
|
26553
26899
|
function backupConfig(args) {
|
|
26554
|
-
if (!
|
|
26900
|
+
if (!existsSync26(args.configPath)) {
|
|
26555
26901
|
return { backupPath: null, existed: false };
|
|
26556
26902
|
}
|
|
26557
|
-
|
|
26903
|
+
mkdirSync13(args.backupsDir, { recursive: true });
|
|
26558
26904
|
const filename = `${args.agentId}__${basename(args.configPath)}.bak`;
|
|
26559
|
-
const backupPath =
|
|
26560
|
-
if (!
|
|
26561
|
-
const raw =
|
|
26562
|
-
|
|
26905
|
+
const backupPath = join22(args.backupsDir, filename);
|
|
26906
|
+
if (!existsSync26(backupPath)) {
|
|
26907
|
+
const raw = readFileSync15(args.configPath, "utf-8");
|
|
26908
|
+
writeFileSync12(backupPath, raw, "utf-8");
|
|
26563
26909
|
}
|
|
26564
26910
|
return { backupPath, existed: true };
|
|
26565
26911
|
}
|
|
26566
26912
|
function restoreFromBackup(args) {
|
|
26567
26913
|
const filename = `${args.agentId}__${basename(args.configPath)}.bak`;
|
|
26568
|
-
const backupPath =
|
|
26569
|
-
if (!
|
|
26914
|
+
const backupPath = join22(args.backupsDir, filename);
|
|
26915
|
+
if (!existsSync26(backupPath)) {
|
|
26570
26916
|
return { restored: false };
|
|
26571
26917
|
}
|
|
26572
|
-
const raw =
|
|
26573
|
-
|
|
26574
|
-
|
|
26918
|
+
const raw = readFileSync15(backupPath, "utf-8");
|
|
26919
|
+
mkdirSync13(dirname8(args.configPath), { recursive: true });
|
|
26920
|
+
writeFileSync12(args.configPath, raw, "utf-8");
|
|
26575
26921
|
return { restored: true };
|
|
26576
26922
|
}
|
|
26577
26923
|
|
|
26578
26924
|
// src/lib/dev/agent-connectors/claude-code.ts
|
|
26579
26925
|
function configPath() {
|
|
26580
|
-
return
|
|
26926
|
+
return join23(homedir4(), ".claude.json");
|
|
26581
26927
|
}
|
|
26582
26928
|
function legacyConfigPath() {
|
|
26583
|
-
return
|
|
26929
|
+
return join23(homedir4(), ".claude", "mcp.json");
|
|
26584
26930
|
}
|
|
26585
26931
|
function resolveConfigPath() {
|
|
26586
|
-
if (
|
|
26932
|
+
if (existsSync27(configPath()))
|
|
26587
26933
|
return configPath();
|
|
26588
|
-
if (
|
|
26934
|
+
if (existsSync27(legacyConfigPath()))
|
|
26589
26935
|
return legacyConfigPath();
|
|
26590
26936
|
return configPath();
|
|
26591
26937
|
}
|
|
@@ -26595,8 +26941,8 @@ var claudeCodeConnector = {
|
|
|
26595
26941
|
hotkey: "c",
|
|
26596
26942
|
describeLocation: () => resolveConfigPath(),
|
|
26597
26943
|
async detect() {
|
|
26598
|
-
const claudeDir =
|
|
26599
|
-
if (
|
|
26944
|
+
const claudeDir = join23(homedir4(), ".claude");
|
|
26945
|
+
if (existsSync27(configPath()) || existsSync27(legacyConfigPath()) || existsSync27(claudeDir)) {
|
|
26600
26946
|
return { installed: true, note: "Found Claude Code config" };
|
|
26601
26947
|
}
|
|
26602
26948
|
return { installed: false };
|
|
@@ -26634,7 +26980,7 @@ var claudeCodeConnector = {
|
|
|
26634
26980
|
backupsDir: args.backupsDir,
|
|
26635
26981
|
agentId: this.id
|
|
26636
26982
|
});
|
|
26637
|
-
if (!restored.restored &&
|
|
26983
|
+
if (!restored.restored && existsSync27(path)) {
|
|
26638
26984
|
const after = readJsonFile(path);
|
|
26639
26985
|
if (after.ok && after.value && typeof after.value === "object") {
|
|
26640
26986
|
const root = after.value;
|
|
@@ -26644,7 +26990,7 @@ var claudeCodeConnector = {
|
|
|
26644
26990
|
try {
|
|
26645
26991
|
unlinkSync2(path);
|
|
26646
26992
|
} catch {
|
|
26647
|
-
|
|
26993
|
+
writeFileSync13(path, `{}
|
|
26648
26994
|
`, "utf-8");
|
|
26649
26995
|
}
|
|
26650
26996
|
}
|
|
@@ -26656,16 +27002,16 @@ var claudeCodeConnector = {
|
|
|
26656
27002
|
|
|
26657
27003
|
// src/lib/dev/agent-connectors/codex.ts
|
|
26658
27004
|
import {
|
|
26659
|
-
existsSync as
|
|
26660
|
-
mkdirSync as
|
|
26661
|
-
readFileSync as
|
|
27005
|
+
existsSync as existsSync28,
|
|
27006
|
+
mkdirSync as mkdirSync14,
|
|
27007
|
+
readFileSync as readFileSync16,
|
|
26662
27008
|
unlinkSync as unlinkSync3,
|
|
26663
|
-
writeFileSync as
|
|
27009
|
+
writeFileSync as writeFileSync14
|
|
26664
27010
|
} from "node:fs";
|
|
26665
27011
|
import { homedir as homedir5 } from "node:os";
|
|
26666
|
-
import { dirname as
|
|
27012
|
+
import { dirname as dirname9, join as join24 } from "node:path";
|
|
26667
27013
|
function configPath2() {
|
|
26668
|
-
return
|
|
27014
|
+
return join24(homedir5(), ".codex", "config.toml");
|
|
26669
27015
|
}
|
|
26670
27016
|
var SECTION_PREFIX = "mcp_servers.";
|
|
26671
27017
|
function buildSection(name, url) {
|
|
@@ -26703,7 +27049,7 @@ var codexConnector = {
|
|
|
26703
27049
|
hotkey: "x",
|
|
26704
27050
|
describeLocation: () => configPath2(),
|
|
26705
27051
|
async detect() {
|
|
26706
|
-
if (
|
|
27052
|
+
if (existsSync28(join24(homedir5(), ".codex")) || existsSync28(configPath2())) {
|
|
26707
27053
|
return { installed: true, note: "Found ~/.codex/" };
|
|
26708
27054
|
}
|
|
26709
27055
|
return { installed: false };
|
|
@@ -26716,8 +27062,8 @@ var codexConnector = {
|
|
|
26716
27062
|
agentId: this.id
|
|
26717
27063
|
});
|
|
26718
27064
|
let existing = "";
|
|
26719
|
-
if (
|
|
26720
|
-
existing =
|
|
27065
|
+
if (existsSync28(path)) {
|
|
27066
|
+
existing = readFileSync16(path, "utf-8");
|
|
26721
27067
|
}
|
|
26722
27068
|
const conflict = findSectionRange(existing, args.name) !== null;
|
|
26723
27069
|
let next = existing;
|
|
@@ -26732,18 +27078,18 @@ var codexConnector = {
|
|
|
26732
27078
|
next += `
|
|
26733
27079
|
`;
|
|
26734
27080
|
next += buildSection(args.name, args.url);
|
|
26735
|
-
|
|
26736
|
-
|
|
27081
|
+
mkdirSync14(dirname9(path), { recursive: true });
|
|
27082
|
+
writeFileSync14(path, next, "utf-8");
|
|
26737
27083
|
return { added: !conflict, conflict };
|
|
26738
27084
|
},
|
|
26739
27085
|
async remove(args) {
|
|
26740
27086
|
const path = configPath2();
|
|
26741
|
-
if (
|
|
26742
|
-
const existing =
|
|
27087
|
+
if (existsSync28(path)) {
|
|
27088
|
+
const existing = readFileSync16(path, "utf-8");
|
|
26743
27089
|
const range = findSectionRange(existing, args.name);
|
|
26744
27090
|
if (range) {
|
|
26745
27091
|
const next = existing.slice(0, range.start) + existing.slice(range.end);
|
|
26746
|
-
|
|
27092
|
+
writeFileSync14(path, next, "utf-8");
|
|
26747
27093
|
}
|
|
26748
27094
|
}
|
|
26749
27095
|
const restored = restoreFromBackup({
|
|
@@ -26751,8 +27097,8 @@ var codexConnector = {
|
|
|
26751
27097
|
backupsDir: args.backupsDir,
|
|
26752
27098
|
agentId: this.id
|
|
26753
27099
|
});
|
|
26754
|
-
if (!restored.restored &&
|
|
26755
|
-
const after =
|
|
27100
|
+
if (!restored.restored && existsSync28(path)) {
|
|
27101
|
+
const after = readFileSync16(path, "utf-8");
|
|
26756
27102
|
if (!after.trim()) {
|
|
26757
27103
|
try {
|
|
26758
27104
|
unlinkSync3(path);
|
|
@@ -26764,11 +27110,11 @@ var codexConnector = {
|
|
|
26764
27110
|
};
|
|
26765
27111
|
|
|
26766
27112
|
// src/lib/dev/agent-connectors/continue.ts
|
|
26767
|
-
import { existsSync as
|
|
27113
|
+
import { existsSync as existsSync29, unlinkSync as unlinkSync4, writeFileSync as writeFileSync15 } from "node:fs";
|
|
26768
27114
|
import { homedir as homedir6 } from "node:os";
|
|
26769
|
-
import { join as
|
|
27115
|
+
import { join as join25 } from "node:path";
|
|
26770
27116
|
function configPath3() {
|
|
26771
|
-
return
|
|
27117
|
+
return join25(homedir6(), ".continue", "config.json");
|
|
26772
27118
|
}
|
|
26773
27119
|
function isContinueServerEntry(value) {
|
|
26774
27120
|
return Boolean(value && typeof value === "object" && typeof value.name === "string");
|
|
@@ -26779,8 +27125,8 @@ var continueConnector = {
|
|
|
26779
27125
|
hotkey: "n",
|
|
26780
27126
|
describeLocation: () => configPath3(),
|
|
26781
27127
|
async detect() {
|
|
26782
|
-
const dir =
|
|
26783
|
-
if (
|
|
27128
|
+
const dir = join25(homedir6(), ".continue");
|
|
27129
|
+
if (existsSync29(dir) || existsSync29(configPath3())) {
|
|
26784
27130
|
return { installed: true, note: "Found ~/.continue/" };
|
|
26785
27131
|
}
|
|
26786
27132
|
return { installed: false };
|
|
@@ -26818,7 +27164,7 @@ var continueConnector = {
|
|
|
26818
27164
|
backupsDir: args.backupsDir,
|
|
26819
27165
|
agentId: this.id
|
|
26820
27166
|
});
|
|
26821
|
-
if (!restored.restored &&
|
|
27167
|
+
if (!restored.restored && existsSync29(path)) {
|
|
26822
27168
|
const after = readJsonFile(path);
|
|
26823
27169
|
if (after.ok && after.value && typeof after.value === "object") {
|
|
26824
27170
|
const root = after.value;
|
|
@@ -26828,7 +27174,7 @@ var continueConnector = {
|
|
|
26828
27174
|
try {
|
|
26829
27175
|
unlinkSync4(path);
|
|
26830
27176
|
} catch {
|
|
26831
|
-
|
|
27177
|
+
writeFileSync15(path, `{}
|
|
26832
27178
|
`, "utf-8");
|
|
26833
27179
|
}
|
|
26834
27180
|
}
|
|
@@ -26839,11 +27185,11 @@ var continueConnector = {
|
|
|
26839
27185
|
};
|
|
26840
27186
|
|
|
26841
27187
|
// src/lib/dev/agent-connectors/cursor.ts
|
|
26842
|
-
import { existsSync as
|
|
27188
|
+
import { existsSync as existsSync30, unlinkSync as unlinkSync5, writeFileSync as writeFileSync16 } from "node:fs";
|
|
26843
27189
|
import { homedir as homedir7 } from "node:os";
|
|
26844
|
-
import { join as
|
|
27190
|
+
import { join as join26 } from "node:path";
|
|
26845
27191
|
function globalConfigPath() {
|
|
26846
|
-
return
|
|
27192
|
+
return join26(homedir7(), ".cursor", "mcp.json");
|
|
26847
27193
|
}
|
|
26848
27194
|
var cursorConnector = {
|
|
26849
27195
|
id: "cursor",
|
|
@@ -26851,9 +27197,9 @@ var cursorConnector = {
|
|
|
26851
27197
|
hotkey: "u",
|
|
26852
27198
|
describeLocation: () => globalConfigPath(),
|
|
26853
27199
|
async detect() {
|
|
26854
|
-
const cursorDir =
|
|
26855
|
-
const macAppSupport =
|
|
26856
|
-
if (
|
|
27200
|
+
const cursorDir = join26(homedir7(), ".cursor");
|
|
27201
|
+
const macAppSupport = join26(homedir7(), "Library", "Application Support", "Cursor");
|
|
27202
|
+
if (existsSync30(cursorDir) || existsSync30(macAppSupport)) {
|
|
26857
27203
|
return { installed: true, note: "Found Cursor config dir" };
|
|
26858
27204
|
}
|
|
26859
27205
|
return { installed: false };
|
|
@@ -26891,7 +27237,7 @@ var cursorConnector = {
|
|
|
26891
27237
|
backupsDir: args.backupsDir,
|
|
26892
27238
|
agentId: this.id
|
|
26893
27239
|
});
|
|
26894
|
-
if (!restored.restored &&
|
|
27240
|
+
if (!restored.restored && existsSync30(path)) {
|
|
26895
27241
|
const after = readJsonFile(path);
|
|
26896
27242
|
if (after.ok && after.value && typeof after.value === "object") {
|
|
26897
27243
|
const root = after.value;
|
|
@@ -26901,7 +27247,7 @@ var cursorConnector = {
|
|
|
26901
27247
|
try {
|
|
26902
27248
|
unlinkSync5(path);
|
|
26903
27249
|
} catch {
|
|
26904
|
-
|
|
27250
|
+
writeFileSync16(path, `{}
|
|
26905
27251
|
`, "utf-8");
|
|
26906
27252
|
}
|
|
26907
27253
|
}
|
|
@@ -26912,30 +27258,30 @@ var cursorConnector = {
|
|
|
26912
27258
|
};
|
|
26913
27259
|
|
|
26914
27260
|
// src/lib/dev/agent-connectors/vscode-copilot.ts
|
|
26915
|
-
import { existsSync as
|
|
27261
|
+
import { existsSync as existsSync31, unlinkSync as unlinkSync6, writeFileSync as writeFileSync17 } from "node:fs";
|
|
26916
27262
|
import { homedir as homedir8, platform as platform4 } from "node:os";
|
|
26917
|
-
import { join as
|
|
27263
|
+
import { join as join27, resolve as resolve6 } from "node:path";
|
|
26918
27264
|
function userLevelConfigPath() {
|
|
26919
27265
|
const home = homedir8();
|
|
26920
27266
|
const p2 = platform4();
|
|
26921
27267
|
if (p2 === "darwin")
|
|
26922
|
-
return
|
|
27268
|
+
return join27(home, "Library", "Application Support", "Code", "User", "mcp.json");
|
|
26923
27269
|
if (p2 === "win32") {
|
|
26924
|
-
const appData = process.env["APPDATA"] ??
|
|
26925
|
-
return
|
|
27270
|
+
const appData = process.env["APPDATA"] ?? join27(home, "AppData", "Roaming");
|
|
27271
|
+
return join27(appData, "Code", "User", "mcp.json");
|
|
26926
27272
|
}
|
|
26927
|
-
const xdg = process.env["XDG_CONFIG_HOME"] ??
|
|
26928
|
-
return
|
|
27273
|
+
const xdg = process.env["XDG_CONFIG_HOME"] ?? join27(home, ".config");
|
|
27274
|
+
return join27(xdg, "Code", "User", "mcp.json");
|
|
26929
27275
|
}
|
|
26930
27276
|
function isHomeDir(cwd) {
|
|
26931
|
-
return
|
|
27277
|
+
return resolve6(cwd) === resolve6(homedir8());
|
|
26932
27278
|
}
|
|
26933
27279
|
function hasWorkspaceVscode(cwd) {
|
|
26934
|
-
return
|
|
27280
|
+
return existsSync31(join27(cwd, ".vscode"));
|
|
26935
27281
|
}
|
|
26936
27282
|
function configPath4(cwd) {
|
|
26937
27283
|
if (!isHomeDir(cwd) && hasWorkspaceVscode(cwd)) {
|
|
26938
|
-
return
|
|
27284
|
+
return join27(cwd, ".vscode", "mcp.json");
|
|
26939
27285
|
}
|
|
26940
27286
|
return userLevelConfigPath();
|
|
26941
27287
|
}
|
|
@@ -26949,7 +27295,7 @@ var vscodeCopilotConnector = {
|
|
|
26949
27295
|
if (!isHomeDir(here) && hasWorkspaceVscode(here)) {
|
|
26950
27296
|
return { installed: true, note: "Found .vscode/ in current dir" };
|
|
26951
27297
|
}
|
|
26952
|
-
if (
|
|
27298
|
+
if (existsSync31(userLevelConfigPath().replace(/mcp\.json$/, ""))) {
|
|
26953
27299
|
return { installed: true, note: "Found VS Code user profile" };
|
|
26954
27300
|
}
|
|
26955
27301
|
return { installed: false };
|
|
@@ -26987,7 +27333,7 @@ var vscodeCopilotConnector = {
|
|
|
26987
27333
|
backupsDir: args.backupsDir,
|
|
26988
27334
|
agentId: this.id
|
|
26989
27335
|
});
|
|
26990
|
-
if (!restored.restored &&
|
|
27336
|
+
if (!restored.restored && existsSync31(path)) {
|
|
26991
27337
|
const after = readJsonFile(path);
|
|
26992
27338
|
if (after.ok && after.value && typeof after.value === "object") {
|
|
26993
27339
|
const root = after.value;
|
|
@@ -26997,7 +27343,7 @@ var vscodeCopilotConnector = {
|
|
|
26997
27343
|
try {
|
|
26998
27344
|
unlinkSync6(path);
|
|
26999
27345
|
} catch {
|
|
27000
|
-
|
|
27346
|
+
writeFileSync17(path, `{}
|
|
27001
27347
|
`, "utf-8");
|
|
27002
27348
|
}
|
|
27003
27349
|
}
|
|
@@ -27141,7 +27487,7 @@ function parseGraceMs(value, fallbackMs) {
|
|
|
27141
27487
|
return Math.round(n * 1000);
|
|
27142
27488
|
}
|
|
27143
27489
|
function resolveSpecPath(cwd, raw) {
|
|
27144
|
-
return
|
|
27490
|
+
return isAbsolute3(raw) ? raw : resolve7(cwd, raw);
|
|
27145
27491
|
}
|
|
27146
27492
|
function buildConnectionName(serverId) {
|
|
27147
27493
|
const slug = serverId.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 24) || "mcp-server";
|
|
@@ -27149,6 +27495,16 @@ function buildConnectionName(serverId) {
|
|
|
27149
27495
|
}
|
|
27150
27496
|
function registerDevCommand(program2) {
|
|
27151
27497
|
const dev = program2.command("dev").description("Run a deployed MCP server locally with hot reload + agent auto-wire").option("--server <serverId>", "Server ID (default: from .mcpcloud/state.json)").option("--org <organizationId>", "Organization ID (overrides default)").option("--port <number>", "Local port (default 8787)").option("--transport <list>", "Comma-separated: http,sse,stdio (default http,sse)").option("--stdio", "Shorthand for --transport stdio").option("--offline", "Use cached bundle, skip Convex sync").option("--no-watch", "Disable file watcher").option("--no-connect", "Disable agent auto-wire prompts").option("--auto-connect <agent>", "Auto-wire the named agent (claude-code, cursor, codex, vscode-copilot, continue)").option("--no-inspector", "Disable the inspector UI (default: enabled at /__inspect)").option("--spec <path>", "Watch this OpenAPI spec file and regenerate the bundle on change").option("--no-auto-regen", "Show spec drift but do not auto-apply (manual file edit still works)").option("--regen-grace <seconds>", "Grace period before auto-applying spec changes (default 5)").option("--enrich-on-change", "Preserve per-tool enrichment when the spec changes").option("--open <choice>", "Open an editor on startup: yes | always | no | never | ask").option("--open-target <choice>", "What to open: tools (.mcpcloud/tools/<server>/) | bundle (read-only .mcpcloud/server/) | all (both). When omitted on first run, mcp dev prompts.").option("--editor <command>", "Editor binary for this invocation (e.g. cursor, code, windsurf). Overrides saved editorCommand and the multi-editor picker.").option("--verbose", "Verbose logs").action(runAction(runDev));
|
|
27498
|
+
dev.addHelpText("after", `
|
|
27499
|
+
Git-native mode (automatic):
|
|
27500
|
+
If the chosen server is linked to a GitHub repo, mcp dev clones it under
|
|
27501
|
+
.mcpcloud/git/ and runs against the working copy — no flag needed. Edit in
|
|
27502
|
+
your own editor, then commit + push: the webhook reconciles src/** back into
|
|
27503
|
+
MCPCloud. The CLI's tool/handler PATCH watchers are off in this mode (native
|
|
27504
|
+
git is the version control). Config under mcpcloud/ is a read-only mirror —
|
|
27505
|
+
change it in the dashboard. Deploy when ready with \`mcp deploy\` or the UI.
|
|
27506
|
+
An unlinked server uses the default flow (download bundle, edit via dashboard).
|
|
27507
|
+
`);
|
|
27152
27508
|
dev.command("list").description("List running mcp dev sessions on this machine").action(runAction(runDevList));
|
|
27153
27509
|
dev.command("kill <pid>").description('Stop a running mcp dev session by PID (or "all" to stop every session)').action(runAction(runDevKill));
|
|
27154
27510
|
dev.command("init").description("Interactively pick an organization + server and write .mcpcloud/state.json (forces the picker even if state already exists)").action(runAction(runDevInit));
|
|
@@ -27178,7 +27534,8 @@ async function runDevInit() {
|
|
|
27178
27534
|
bundleVersion: 0,
|
|
27179
27535
|
lastSyncedAt: 0,
|
|
27180
27536
|
transports: ["http", "sse"],
|
|
27181
|
-
port: 8787
|
|
27537
|
+
port: 8787,
|
|
27538
|
+
serverName: result.target.serverName
|
|
27182
27539
|
});
|
|
27183
27540
|
printSuccess(`Saved .mcpcloud/state.json — run ${c.bold("mcp dev")} to start.`);
|
|
27184
27541
|
}
|
|
@@ -27234,6 +27591,21 @@ async function runDev(opts) {
|
|
|
27234
27591
|
const cached2 = readState(cwd);
|
|
27235
27592
|
let serverId = opts.server ?? cached2?.serverId;
|
|
27236
27593
|
let organizationId = opts.org ?? cached2?.organizationId ?? undefined;
|
|
27594
|
+
if (!opts.server && cached2?.serverId && canRunInteractive()) {
|
|
27595
|
+
const choice = await promptResumeOrSwitch({
|
|
27596
|
+
serverId: cached2.serverId,
|
|
27597
|
+
...cached2.serverName !== undefined ? { serverName: cached2.serverName } : {}
|
|
27598
|
+
});
|
|
27599
|
+
if (choice === "cancel")
|
|
27600
|
+
process.exit(1);
|
|
27601
|
+
if (choice === "switch") {
|
|
27602
|
+
const result = await runInteractiveBootstrap();
|
|
27603
|
+
if (result.kind !== "resolved")
|
|
27604
|
+
process.exit(1);
|
|
27605
|
+
serverId = result.target.serverId;
|
|
27606
|
+
organizationId = result.target.organizationId;
|
|
27607
|
+
}
|
|
27608
|
+
}
|
|
27237
27609
|
if (!serverId) {
|
|
27238
27610
|
if (!canRunInteractive()) {
|
|
27239
27611
|
printError("No server selected. Pass --server <serverId>, run `mcp dev init` to pick one interactively, or run from a directory with .mcpcloud/state.json.");
|
|
@@ -27274,7 +27646,7 @@ async function runDev(opts) {
|
|
|
27274
27646
|
let specPath = null;
|
|
27275
27647
|
if (opts.spec) {
|
|
27276
27648
|
const resolved = resolveSpecPath(cwd, opts.spec);
|
|
27277
|
-
if (!
|
|
27649
|
+
if (!existsSync32(resolved)) {
|
|
27278
27650
|
printError(`Spec file not found: ${resolved}`);
|
|
27279
27651
|
process.exit(1);
|
|
27280
27652
|
}
|
|
@@ -27303,7 +27675,8 @@ async function runDev(opts) {
|
|
|
27303
27675
|
enrichOnChange,
|
|
27304
27676
|
openChoice,
|
|
27305
27677
|
openTargetOverride,
|
|
27306
|
-
editorOverride
|
|
27678
|
+
editorOverride,
|
|
27679
|
+
gitNative: prepared.gitNative
|
|
27307
27680
|
});
|
|
27308
27681
|
}
|
|
27309
27682
|
async function emitStaleHandlerOverrideWarning(args) {
|
|
@@ -27338,6 +27711,21 @@ async function runWithRuntime(args) {
|
|
|
27338
27711
|
}
|
|
27339
27712
|
});
|
|
27340
27713
|
runtime = launch();
|
|
27714
|
+
let fullShutdownReady = false;
|
|
27715
|
+
const killRuntimeNow = () => {
|
|
27716
|
+
try {
|
|
27717
|
+
runtime?.child.kill("SIGKILL");
|
|
27718
|
+
} catch {}
|
|
27719
|
+
};
|
|
27720
|
+
process.once("exit", killRuntimeNow);
|
|
27721
|
+
const earlySignal = (signal) => {
|
|
27722
|
+
if (fullShutdownReady)
|
|
27723
|
+
return;
|
|
27724
|
+
killRuntimeNow();
|
|
27725
|
+
process.exit(signal === "SIGINT" ? 130 : 143);
|
|
27726
|
+
};
|
|
27727
|
+
process.on("SIGINT", () => earlySignal("SIGINT"));
|
|
27728
|
+
process.on("SIGTERM", () => earlySignal("SIGTERM"));
|
|
27341
27729
|
const baseUrl = `http://127.0.0.1:${args.port}`;
|
|
27342
27730
|
const mcpUrl = `${baseUrl}/mcp`;
|
|
27343
27731
|
const connectionName = buildConnectionName(args.serverId);
|
|
@@ -27354,7 +27742,14 @@ async function runWithRuntime(args) {
|
|
|
27354
27742
|
});
|
|
27355
27743
|
wired.push(...selected);
|
|
27356
27744
|
}
|
|
27357
|
-
if (args.transports[0] !== "stdio") {
|
|
27745
|
+
if (args.transports[0] !== "stdio" && args.gitNative) {
|
|
27746
|
+
await maybeOpenEditor({
|
|
27747
|
+
dir: args.prepared.repoDir ?? args.prepared.destDir,
|
|
27748
|
+
target: "bundle",
|
|
27749
|
+
override: args.openChoice,
|
|
27750
|
+
editorOverride: args.editorOverride
|
|
27751
|
+
});
|
|
27752
|
+
} else if (args.transports[0] !== "stdio") {
|
|
27358
27753
|
const savedTarget = readConfig().editorOpenTarget;
|
|
27359
27754
|
let target = resolveOpenTarget({
|
|
27360
27755
|
override: args.openTargetOverride,
|
|
@@ -27405,11 +27800,16 @@ async function runWithRuntime(args) {
|
|
|
27405
27800
|
const watcher = args.watch ? watchDir({
|
|
27406
27801
|
rootDir: args.prepared.destDir,
|
|
27407
27802
|
debounceMs: 250,
|
|
27408
|
-
ignore: [
|
|
27803
|
+
ignore: [
|
|
27804
|
+
"node_modules",
|
|
27805
|
+
"_mcpsh_host.mjs",
|
|
27806
|
+
"_mcpsh_inspector.mjs",
|
|
27807
|
+
".git"
|
|
27808
|
+
],
|
|
27409
27809
|
onChange: async (path) => {
|
|
27410
27810
|
if (shuttingDown)
|
|
27411
27811
|
return;
|
|
27412
|
-
if (shouldWarnAboutBundleEdit({
|
|
27812
|
+
if (!args.gitNative && shouldWarnAboutBundleEdit({
|
|
27413
27813
|
changedPath: path,
|
|
27414
27814
|
alreadyWarned: bundleEditWarned,
|
|
27415
27815
|
suppressed: suppressBundleWarn,
|
|
@@ -27427,14 +27827,14 @@ async function runWithRuntime(args) {
|
|
|
27427
27827
|
runtime = launch();
|
|
27428
27828
|
}
|
|
27429
27829
|
}) : null;
|
|
27430
|
-
const toolsWatcher = args.watch ? startToolsWatcher({
|
|
27830
|
+
const toolsWatcher = args.watch && !args.gitNative ? startToolsWatcher({
|
|
27431
27831
|
cwd: args.cwd,
|
|
27432
27832
|
serverId: args.serverId,
|
|
27433
27833
|
organizationId: args.organizationId,
|
|
27434
27834
|
projectId: args.prepared.state.projectId,
|
|
27435
27835
|
devBaseUrl: args.inspector ? baseUrl : undefined
|
|
27436
27836
|
}) : null;
|
|
27437
|
-
const handlersWatcher = args.watch ? startHandlersWatcher({
|
|
27837
|
+
const handlersWatcher = args.watch && !args.gitNative ? startHandlersWatcher({
|
|
27438
27838
|
cwd: args.cwd,
|
|
27439
27839
|
serverId: args.serverId,
|
|
27440
27840
|
organizationId: args.organizationId,
|
|
@@ -27443,7 +27843,7 @@ async function runWithRuntime(args) {
|
|
|
27443
27843
|
let specWatcher = null;
|
|
27444
27844
|
let pendingApply = null;
|
|
27445
27845
|
let regenInFlight = false;
|
|
27446
|
-
if (args.specPath) {
|
|
27846
|
+
if (args.specPath && !args.gitNative) {
|
|
27447
27847
|
const initial = readSpecFile(args.specPath);
|
|
27448
27848
|
let lastAppliedHash = initial.hash;
|
|
27449
27849
|
specWatcher = watchSpecFile({
|
|
@@ -27524,20 +27924,60 @@ async function runWithRuntime(args) {
|
|
|
27524
27924
|
printInfo(` ${c.dim("Inspector: ")} ${c.cyan(`${baseUrl}/__inspect`)}`);
|
|
27525
27925
|
}
|
|
27526
27926
|
printInfo(` ${c.dim(`Edit files in ${relative7(args.cwd, args.prepared.destDir)}/ to hot-reload.`)}`);
|
|
27927
|
+
if (args.gitNative && args.prepared.repoDir) {
|
|
27928
|
+
printInfo(` ${c.dim(`Git-native: edit + commit + push in ${relative7(args.cwd, args.prepared.repoDir)}/ — the webhook reconciles src/**.`)}`);
|
|
27929
|
+
printInfo(` ${c.dim("Config under mcpcloud/ is read-only; change it in the dashboard. Deploy when ready (mcp deploy).")}`);
|
|
27930
|
+
}
|
|
27527
27931
|
if (toolsWatcher) {
|
|
27528
27932
|
printInfo(` ${c.dim(`Edit tool metadata in .mcpcloud/tools/${args.serverId}/ (changes push to cloud on save).`)}`);
|
|
27529
27933
|
}
|
|
27530
27934
|
if (handlersWatcher) {
|
|
27531
27935
|
printInfo(` ${c.dim(`Edit tool handlers in .mcpcloud/server/src/tools/<slug>.ts (changes push to cloud on save).`)}`);
|
|
27532
27936
|
}
|
|
27533
|
-
if (
|
|
27937
|
+
if (specWatcher) {
|
|
27534
27938
|
printInfo(` ${c.dim(`Spec watch: ${relative7(args.cwd, args.specPath)}`)}`);
|
|
27535
27939
|
}
|
|
27536
|
-
|
|
27537
|
-
|
|
27538
|
-
|
|
27539
|
-
|
|
27540
|
-
|
|
27940
|
+
const gitPullPoller = args.gitNative ? startGitPullPoll({
|
|
27941
|
+
fetchStatus: () => resolveServerGitPullStatus({
|
|
27942
|
+
serverId: args.serverId,
|
|
27943
|
+
organizationId: args.organizationId
|
|
27944
|
+
}),
|
|
27945
|
+
onChange: (status) => {
|
|
27946
|
+
if (shuttingDown)
|
|
27947
|
+
return;
|
|
27948
|
+
const sha = status.lastPulledGitSha ? status.lastPulledGitSha.slice(0, 7) : "HEAD";
|
|
27949
|
+
if (status.rejectedFileCount > 0) {
|
|
27950
|
+
printWarn(`Push ${sha}: MCPCloud rejected ${status.rejectedFileCount} file${status.rejectedFileCount === 1 ? "" : "s"} (unsafe handler code).`);
|
|
27951
|
+
if (status.error)
|
|
27952
|
+
printInfo(` ${c.dim(status.error)}`);
|
|
27953
|
+
printInfo(` ${c.dim("Fix the flagged file(s) and push again.")}`);
|
|
27954
|
+
return;
|
|
27955
|
+
}
|
|
27956
|
+
if (status.status === "error") {
|
|
27957
|
+
printWarn(`Push ${sha}: MCPCloud could not reconcile — ${status.error ?? "unknown error"}.`);
|
|
27958
|
+
return;
|
|
27959
|
+
}
|
|
27960
|
+
if (status.status !== "pulled") {
|
|
27961
|
+
printInfo(`Push ${sha}: ${status.hadConfigDrift ? "touched the read-only mcpcloud/ config — ignored." : "touched only generated files outside src/** — nothing synced."}`);
|
|
27962
|
+
printInfo(` ${c.dim("Only src/ round-trips; edit there to sync. README / mcpcloud/ are generated.")}`);
|
|
27963
|
+
return;
|
|
27964
|
+
}
|
|
27965
|
+
const parts = [`${status.pulledFileCount} updated`];
|
|
27966
|
+
if (status.deletedFileCount > 0) {
|
|
27967
|
+
parts.push(`${status.deletedFileCount} deleted`);
|
|
27968
|
+
}
|
|
27969
|
+
printSuccess(`Push ${sha} reconciled (${parts.join(", ")}) — changes ready for deployment.`);
|
|
27970
|
+
printInfo(` ${c.dim("Deploy with")} ${c.bold("mcp deploy")} ${c.dim("or the dashboard — no preview needed, this session is your local server.")}`);
|
|
27971
|
+
},
|
|
27972
|
+
onError: () => {}
|
|
27973
|
+
}) : null;
|
|
27974
|
+
if (!args.gitNative) {
|
|
27975
|
+
await emitStaleHandlerOverrideWarning({
|
|
27976
|
+
organizationId: args.organizationId,
|
|
27977
|
+
projectId: args.prepared.state.projectId,
|
|
27978
|
+
serverId: args.serverId
|
|
27979
|
+
});
|
|
27980
|
+
}
|
|
27541
27981
|
printInfo(` ${c.dim("Ctrl+C to stop.")}`);
|
|
27542
27982
|
if (isJsonMode()) {
|
|
27543
27983
|
printJson({
|
|
@@ -27576,6 +28016,9 @@ async function runWithRuntime(args) {
|
|
|
27576
28016
|
try {
|
|
27577
28017
|
specWatcher?.close();
|
|
27578
28018
|
} catch {}
|
|
28019
|
+
try {
|
|
28020
|
+
gitPullPoller?.close();
|
|
28021
|
+
} catch {}
|
|
27579
28022
|
if (pendingApply) {
|
|
27580
28023
|
clearTimeout(pendingApply);
|
|
27581
28024
|
pendingApply = null;
|
|
@@ -27595,6 +28038,7 @@ async function runWithRuntime(args) {
|
|
|
27595
28038
|
printSuccess("Bye.");
|
|
27596
28039
|
process.exit(0);
|
|
27597
28040
|
};
|
|
28041
|
+
fullShutdownReady = true;
|
|
27598
28042
|
process.on("SIGINT", () => void shutdown("SIGINT"));
|
|
27599
28043
|
process.on("SIGTERM", () => void shutdown("SIGTERM"));
|
|
27600
28044
|
shortcuts = startShortcuts({
|
|
@@ -28018,13 +28462,13 @@ async function confirmRollback(deploymentId) {
|
|
|
28018
28462
|
}
|
|
28019
28463
|
|
|
28020
28464
|
// src/commands/doctor.ts
|
|
28021
|
-
import { existsSync as
|
|
28465
|
+
import { existsSync as existsSync34, statSync as statSync6, readFileSync as readFileSync18 } from "node:fs";
|
|
28022
28466
|
import { homedir as homedir9, platform as platform6 } from "node:os";
|
|
28023
|
-
import { join as
|
|
28467
|
+
import { join as join29, delimiter as delimiter3 } from "node:path";
|
|
28024
28468
|
|
|
28025
28469
|
// src/lib/version-check.ts
|
|
28026
|
-
import { existsSync as
|
|
28027
|
-
import { join as
|
|
28470
|
+
import { existsSync as existsSync33, mkdirSync as mkdirSync15, readFileSync as readFileSync17, writeFileSync as writeFileSync18 } from "node:fs";
|
|
28471
|
+
import { join as join28 } from "node:path";
|
|
28028
28472
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
28029
28473
|
var FETCH_TIMEOUT_MS2 = 2000;
|
|
28030
28474
|
var REGISTRY_URL = "https://registry.npmjs.org/@mcpcloud/cli/latest";
|
|
@@ -28032,13 +28476,13 @@ function cacheDir() {
|
|
|
28032
28476
|
return configDir();
|
|
28033
28477
|
}
|
|
28034
28478
|
function cacheFile() {
|
|
28035
|
-
return
|
|
28479
|
+
return join28(cacheDir(), "version-check.json");
|
|
28036
28480
|
}
|
|
28037
28481
|
function readCache() {
|
|
28038
|
-
if (!
|
|
28482
|
+
if (!existsSync33(cacheFile()))
|
|
28039
28483
|
return null;
|
|
28040
28484
|
try {
|
|
28041
|
-
const raw = JSON.parse(
|
|
28485
|
+
const raw = JSON.parse(readFileSync17(cacheFile(), "utf-8"));
|
|
28042
28486
|
if (typeof raw.latest !== "string" || typeof raw.fetchedAt !== "number")
|
|
28043
28487
|
return null;
|
|
28044
28488
|
return { latest: raw.latest, fetchedAt: raw.fetchedAt };
|
|
@@ -28048,9 +28492,9 @@ function readCache() {
|
|
|
28048
28492
|
}
|
|
28049
28493
|
function writeCache(entry) {
|
|
28050
28494
|
try {
|
|
28051
|
-
if (!
|
|
28052
|
-
|
|
28053
|
-
|
|
28495
|
+
if (!existsSync33(cacheDir()))
|
|
28496
|
+
mkdirSync15(cacheDir(), { recursive: true, mode: 448 });
|
|
28497
|
+
writeFileSync18(cacheFile(), JSON.stringify(entry, null, 2), { mode: 384 });
|
|
28054
28498
|
} catch {}
|
|
28055
28499
|
}
|
|
28056
28500
|
function compareVersions(a, b) {
|
|
@@ -28161,8 +28605,8 @@ function checkNode() {
|
|
|
28161
28605
|
}
|
|
28162
28606
|
function checkConfigFile() {
|
|
28163
28607
|
const t0 = Date.now();
|
|
28164
|
-
const path =
|
|
28165
|
-
if (!
|
|
28608
|
+
const path = join29(homedir9(), ".mcpcloud", "config.json");
|
|
28609
|
+
if (!existsSync34(path)) {
|
|
28166
28610
|
return {
|
|
28167
28611
|
name: "Config file",
|
|
28168
28612
|
status: "warn",
|
|
@@ -28184,7 +28628,7 @@ function checkConfigFile() {
|
|
|
28184
28628
|
} catch {}
|
|
28185
28629
|
}
|
|
28186
28630
|
try {
|
|
28187
|
-
JSON.parse(
|
|
28631
|
+
JSON.parse(readFileSync18(path, "utf-8"));
|
|
28188
28632
|
} catch (err) {
|
|
28189
28633
|
return {
|
|
28190
28634
|
name: "Config file",
|
|
@@ -28319,8 +28763,8 @@ function checkClaudeCli() {
|
|
|
28319
28763
|
for (const dir of PATH.split(delimiter3)) {
|
|
28320
28764
|
if (!dir)
|
|
28321
28765
|
continue;
|
|
28322
|
-
const candidate =
|
|
28323
|
-
if (
|
|
28766
|
+
const candidate = join29(dir, exe);
|
|
28767
|
+
if (existsSync34(candidate)) {
|
|
28324
28768
|
return {
|
|
28325
28769
|
name: "claude CLI",
|
|
28326
28770
|
status: "pass",
|
|
@@ -28659,10 +29103,10 @@ function prompt2(question) {
|
|
|
28659
29103
|
throw new Error("Interactive prompts are disabled in CI mode. Pass --org / --project / --name explicitly or run `mcp init` outside CI.");
|
|
28660
29104
|
}
|
|
28661
29105
|
const rl = createInterface2({ input: process.stdin, output: process.stderr });
|
|
28662
|
-
return new Promise((
|
|
29106
|
+
return new Promise((resolve8) => {
|
|
28663
29107
|
rl.question(question, (answer) => {
|
|
28664
29108
|
rl.close();
|
|
28665
|
-
|
|
29109
|
+
resolve8(answer.trim());
|
|
28666
29110
|
});
|
|
28667
29111
|
});
|
|
28668
29112
|
}
|
|
@@ -29261,38 +29705,38 @@ function registerOAuthCommands(program2) {
|
|
|
29261
29705
|
// src/lib/plugins.ts
|
|
29262
29706
|
import { spawn as spawn7 } from "node:child_process";
|
|
29263
29707
|
import {
|
|
29264
|
-
existsSync as
|
|
29265
|
-
mkdirSync as
|
|
29708
|
+
existsSync as existsSync35,
|
|
29709
|
+
mkdirSync as mkdirSync16,
|
|
29266
29710
|
readdirSync as readdirSync4,
|
|
29267
|
-
readFileSync as
|
|
29711
|
+
readFileSync as readFileSync19,
|
|
29268
29712
|
rmSync as rmSync3,
|
|
29269
29713
|
statSync as statSync7
|
|
29270
29714
|
} from "node:fs";
|
|
29271
|
-
import { join as
|
|
29715
|
+
import { join as join30 } from "node:path";
|
|
29272
29716
|
import { pathToFileURL } from "node:url";
|
|
29273
29717
|
function pluginsDir() {
|
|
29274
|
-
return
|
|
29718
|
+
return join30(configDir(), "plugins");
|
|
29275
29719
|
}
|
|
29276
29720
|
function ensureDir2() {
|
|
29277
29721
|
const dir = pluginsDir();
|
|
29278
|
-
if (!
|
|
29279
|
-
|
|
29722
|
+
if (!existsSync35(dir))
|
|
29723
|
+
mkdirSync16(dir, { recursive: true, mode: 448 });
|
|
29280
29724
|
}
|
|
29281
29725
|
function readManifestFromPackageDir(packageDir) {
|
|
29282
|
-
const pkgPath =
|
|
29283
|
-
if (!
|
|
29726
|
+
const pkgPath = join30(packageDir, "package.json");
|
|
29727
|
+
if (!existsSync35(pkgPath))
|
|
29284
29728
|
return null;
|
|
29285
29729
|
let pkg;
|
|
29286
29730
|
try {
|
|
29287
|
-
pkg = JSON.parse(
|
|
29731
|
+
pkg = JSON.parse(readFileSync19(pkgPath, "utf-8"));
|
|
29288
29732
|
} catch {
|
|
29289
29733
|
return null;
|
|
29290
29734
|
}
|
|
29291
29735
|
if (!pkg.name)
|
|
29292
29736
|
return null;
|
|
29293
29737
|
const relEntry = pkg.mcpsh?.register ?? pkg.main ?? "index.js";
|
|
29294
|
-
const entry =
|
|
29295
|
-
if (!
|
|
29738
|
+
const entry = join30(packageDir, relEntry);
|
|
29739
|
+
if (!existsSync35(entry))
|
|
29296
29740
|
return null;
|
|
29297
29741
|
return {
|
|
29298
29742
|
name: pkg.name,
|
|
@@ -29303,11 +29747,11 @@ function readManifestFromPackageDir(packageDir) {
|
|
|
29303
29747
|
}
|
|
29304
29748
|
function listInstalledPlugins() {
|
|
29305
29749
|
const dir = pluginsDir();
|
|
29306
|
-
if (!
|
|
29750
|
+
if (!existsSync35(dir))
|
|
29307
29751
|
return [];
|
|
29308
29752
|
const out = [];
|
|
29309
29753
|
for (const entry of readdirSync4(dir)) {
|
|
29310
|
-
const full =
|
|
29754
|
+
const full = join30(dir, entry);
|
|
29311
29755
|
let s;
|
|
29312
29756
|
try {
|
|
29313
29757
|
s = statSync7(full);
|
|
@@ -29318,7 +29762,7 @@ function listInstalledPlugins() {
|
|
|
29318
29762
|
continue;
|
|
29319
29763
|
if (entry.startsWith("@")) {
|
|
29320
29764
|
for (const child of readdirSync4(full)) {
|
|
29321
|
-
const m2 = readManifestFromPackageDir(
|
|
29765
|
+
const m2 = readManifestFromPackageDir(join30(full, child));
|
|
29322
29766
|
if (m2)
|
|
29323
29767
|
out.push(m2);
|
|
29324
29768
|
}
|
|
@@ -29351,26 +29795,43 @@ async function loadPlugins(program2) {
|
|
|
29351
29795
|
}
|
|
29352
29796
|
}
|
|
29353
29797
|
}
|
|
29354
|
-
|
|
29355
|
-
|
|
29356
|
-
|
|
29357
|
-
|
|
29358
|
-
child
|
|
29798
|
+
var PACKAGE_SPEC_PATTERN = /^(@[a-z0-9~-][\w.~-]*\/)?[a-z0-9~-][\w.~-]*(@[-\w.^~><=*+|]+)?$/i;
|
|
29799
|
+
function runNpm(args, cwd) {
|
|
29800
|
+
return new Promise((resolve8) => {
|
|
29801
|
+
const isWindows = process.platform === "win32";
|
|
29802
|
+
const child = spawn7(isWindows ? "npm.cmd" : "npm", args, {
|
|
29803
|
+
shell: isWindows,
|
|
29804
|
+
cwd,
|
|
29805
|
+
stdio: "inherit"
|
|
29806
|
+
});
|
|
29807
|
+
child.on("close", (code) => resolve8(code ?? 1));
|
|
29808
|
+
child.on("error", () => resolve8(1));
|
|
29359
29809
|
});
|
|
29360
29810
|
}
|
|
29361
29811
|
async function installPlugin(packageSpec) {
|
|
29812
|
+
if (!PACKAGE_SPEC_PATTERN.test(packageSpec)) {
|
|
29813
|
+
throw new Error(`"${packageSpec}" is not a plain name@version npm package spec. URL, git, and path specs are not supported for plugins.`);
|
|
29814
|
+
}
|
|
29362
29815
|
ensureDir2();
|
|
29363
29816
|
const dir = pluginsDir();
|
|
29364
|
-
const code = await
|
|
29817
|
+
const code = await runNpm([
|
|
29818
|
+
"install",
|
|
29819
|
+
"--prefix",
|
|
29820
|
+
dir,
|
|
29821
|
+
"--no-save",
|
|
29822
|
+
"--silent",
|
|
29823
|
+
"--ignore-scripts",
|
|
29824
|
+
packageSpec
|
|
29825
|
+
], dir);
|
|
29365
29826
|
if (code !== 0) {
|
|
29366
29827
|
throw new Error(`npm install ${packageSpec} exited with code ${code}.`);
|
|
29367
29828
|
}
|
|
29368
|
-
const nm =
|
|
29369
|
-
if (!
|
|
29829
|
+
const nm = join30(dir, "node_modules");
|
|
29830
|
+
if (!existsSync35(nm)) {
|
|
29370
29831
|
throw new Error(`npm install ran but produced no node_modules under ${dir}.`);
|
|
29371
29832
|
}
|
|
29372
29833
|
const baseName = packageSpec.replace(/@[^@/]+$/, "");
|
|
29373
|
-
const candidatePath = baseName.startsWith("@") ?
|
|
29834
|
+
const candidatePath = baseName.startsWith("@") ? join30(nm, baseName.split("/")[0], baseName.split("/")[1] ?? "") : join30(nm, baseName);
|
|
29374
29835
|
const manifest = readManifestFromPackageDir(candidatePath);
|
|
29375
29836
|
if (!manifest) {
|
|
29376
29837
|
throw new Error(`Installed but could not read plugin manifest at ${candidatePath}.`);
|
|
@@ -29378,26 +29839,26 @@ async function installPlugin(packageSpec) {
|
|
|
29378
29839
|
return { name: manifest.name, entry: manifest.entry };
|
|
29379
29840
|
}
|
|
29380
29841
|
function removePlugin(name) {
|
|
29381
|
-
const nm =
|
|
29382
|
-
if (!
|
|
29842
|
+
const nm = join30(pluginsDir(), "node_modules");
|
|
29843
|
+
if (!existsSync35(nm))
|
|
29383
29844
|
return false;
|
|
29384
|
-
const target = name.startsWith("@") ?
|
|
29385
|
-
if (!
|
|
29845
|
+
const target = name.startsWith("@") ? join30(nm, name.split("/")[0], name.split("/")[1] ?? "") : join30(nm, name);
|
|
29846
|
+
if (!existsSync35(target))
|
|
29386
29847
|
return false;
|
|
29387
29848
|
rmSync3(target, { recursive: true, force: true });
|
|
29388
29849
|
return true;
|
|
29389
29850
|
}
|
|
29390
29851
|
function listInstalledPluginsCombined() {
|
|
29391
29852
|
const direct = listInstalledPlugins();
|
|
29392
|
-
const nm =
|
|
29393
|
-
if (!
|
|
29853
|
+
const nm = join30(pluginsDir(), "node_modules");
|
|
29854
|
+
if (!existsSync35(nm))
|
|
29394
29855
|
return direct;
|
|
29395
29856
|
const seen = new Set(direct.map((p2) => p2.name));
|
|
29396
29857
|
const out = [...direct];
|
|
29397
29858
|
for (const entry of readdirSync4(nm)) {
|
|
29398
29859
|
if (entry === ".bin" || entry === ".package-lock.json")
|
|
29399
29860
|
continue;
|
|
29400
|
-
const full =
|
|
29861
|
+
const full = join30(nm, entry);
|
|
29401
29862
|
let s;
|
|
29402
29863
|
try {
|
|
29403
29864
|
s = statSync7(full);
|
|
@@ -29408,7 +29869,7 @@ function listInstalledPluginsCombined() {
|
|
|
29408
29869
|
continue;
|
|
29409
29870
|
if (entry.startsWith("@")) {
|
|
29410
29871
|
for (const child of readdirSync4(full)) {
|
|
29411
|
-
const m2 = readManifestFromPackageDir(
|
|
29872
|
+
const m2 = readManifestFromPackageDir(join30(full, child));
|
|
29412
29873
|
if (m2 && !seen.has(m2.name)) {
|
|
29413
29874
|
seen.add(m2.name);
|
|
29414
29875
|
out.push(m2);
|
|
@@ -29864,7 +30325,7 @@ function clipboardCandidatesFor(platform7, waylandDisplay) {
|
|
|
29864
30325
|
return linux;
|
|
29865
30326
|
}
|
|
29866
30327
|
async function defaultIsExecutable(binary) {
|
|
29867
|
-
return await new Promise((
|
|
30328
|
+
return await new Promise((resolve8) => {
|
|
29868
30329
|
const isWin = process.platform === "win32";
|
|
29869
30330
|
const cmd = isWin ? "where" : "command";
|
|
29870
30331
|
const args = isWin ? [binary] : ["-v", binary];
|
|
@@ -29872,8 +30333,8 @@ async function defaultIsExecutable(binary) {
|
|
|
29872
30333
|
shell: !isWin,
|
|
29873
30334
|
stdio: "ignore"
|
|
29874
30335
|
});
|
|
29875
|
-
child.on("error", () =>
|
|
29876
|
-
child.on("exit", (code) =>
|
|
30336
|
+
child.on("error", () => resolve8(false));
|
|
30337
|
+
child.on("exit", (code) => resolve8(code === 0));
|
|
29877
30338
|
});
|
|
29878
30339
|
}
|
|
29879
30340
|
async function detectClipboard(probe) {
|
|
@@ -29895,7 +30356,7 @@ async function copyToClipboard(text, probe) {
|
|
|
29895
30356
|
};
|
|
29896
30357
|
}
|
|
29897
30358
|
const spawnImpl = probe?.spawnImpl ?? spawn8;
|
|
29898
|
-
return await new Promise((
|
|
30359
|
+
return await new Promise((resolve8) => {
|
|
29899
30360
|
const child = spawnImpl(candidate.binary, candidate.args, {
|
|
29900
30361
|
stdio: ["pipe", "ignore", "pipe"]
|
|
29901
30362
|
});
|
|
@@ -29904,7 +30365,7 @@ async function copyToClipboard(text, probe) {
|
|
|
29904
30365
|
stderr += chunk.toString("utf-8");
|
|
29905
30366
|
});
|
|
29906
30367
|
child.on("error", (err) => {
|
|
29907
|
-
|
|
30368
|
+
resolve8({
|
|
29908
30369
|
kind: "failed",
|
|
29909
30370
|
binary: candidate.binary,
|
|
29910
30371
|
message: err instanceof Error ? err.message : String(err)
|
|
@@ -29912,9 +30373,9 @@ async function copyToClipboard(text, probe) {
|
|
|
29912
30373
|
});
|
|
29913
30374
|
child.on("exit", (code) => {
|
|
29914
30375
|
if (code === 0) {
|
|
29915
|
-
|
|
30376
|
+
resolve8({ kind: "ok", binary: candidate.binary });
|
|
29916
30377
|
} else {
|
|
29917
|
-
|
|
30378
|
+
resolve8({
|
|
29918
30379
|
kind: "failed",
|
|
29919
30380
|
binary: candidate.binary,
|
|
29920
30381
|
message: stderr.trim() || `${candidate.binary} exited with status ${code ?? "?"}`
|
|
@@ -29924,7 +30385,7 @@ async function copyToClipboard(text, probe) {
|
|
|
29924
30385
|
try {
|
|
29925
30386
|
child.stdin?.end(text);
|
|
29926
30387
|
} catch (err) {
|
|
29927
|
-
|
|
30388
|
+
resolve8({
|
|
29928
30389
|
kind: "failed",
|
|
29929
30390
|
binary: candidate.binary,
|
|
29930
30391
|
message: err instanceof Error ? err.message : String(err)
|
|
@@ -30994,12 +31455,12 @@ function displayValueFor(field, value) {
|
|
|
30994
31455
|
|
|
30995
31456
|
// src/lib/tui/state-store.ts
|
|
30996
31457
|
import {
|
|
30997
|
-
existsSync as
|
|
30998
|
-
mkdirSync as
|
|
30999
|
-
readFileSync as
|
|
31000
|
-
writeFileSync as
|
|
31458
|
+
existsSync as existsSync36,
|
|
31459
|
+
mkdirSync as mkdirSync17,
|
|
31460
|
+
readFileSync as readFileSync20,
|
|
31461
|
+
writeFileSync as writeFileSync19
|
|
31001
31462
|
} from "node:fs";
|
|
31002
|
-
import { join as
|
|
31463
|
+
import { join as join31 } from "node:path";
|
|
31003
31464
|
var ALL_TABS = [
|
|
31004
31465
|
"servers",
|
|
31005
31466
|
"projects",
|
|
@@ -31009,7 +31470,7 @@ var ALL_TABS = [
|
|
|
31009
31470
|
"devSessions"
|
|
31010
31471
|
];
|
|
31011
31472
|
function tuiStateFile() {
|
|
31012
|
-
return
|
|
31473
|
+
return join31(configDir(), "tui-state.json");
|
|
31013
31474
|
}
|
|
31014
31475
|
function isTab(v2) {
|
|
31015
31476
|
return typeof v2 === "string" && ALL_TABS.includes(v2);
|
|
@@ -31074,11 +31535,11 @@ function sanitizeTuiState(input) {
|
|
|
31074
31535
|
}
|
|
31075
31536
|
function readTuiState() {
|
|
31076
31537
|
const path = tuiStateFile();
|
|
31077
|
-
if (!
|
|
31538
|
+
if (!existsSync36(path))
|
|
31078
31539
|
return {};
|
|
31079
31540
|
let parsed;
|
|
31080
31541
|
try {
|
|
31081
|
-
parsed = JSON.parse(
|
|
31542
|
+
parsed = JSON.parse(readFileSync20(path, "utf-8"));
|
|
31082
31543
|
} catch {
|
|
31083
31544
|
return {};
|
|
31084
31545
|
}
|
|
@@ -31086,10 +31547,10 @@ function readTuiState() {
|
|
|
31086
31547
|
}
|
|
31087
31548
|
function writeTuiState(state) {
|
|
31088
31549
|
try {
|
|
31089
|
-
if (!
|
|
31090
|
-
|
|
31550
|
+
if (!existsSync36(configDir())) {
|
|
31551
|
+
mkdirSync17(configDir(), { recursive: true, mode: 448 });
|
|
31091
31552
|
}
|
|
31092
|
-
|
|
31553
|
+
writeFileSync19(tuiStateFile(), JSON.stringify(state, null, 2), {
|
|
31093
31554
|
encoding: "utf-8",
|
|
31094
31555
|
mode: 384
|
|
31095
31556
|
});
|
|
@@ -33094,7 +33555,7 @@ function registerUiCommand(program2) {
|
|
|
33094
33555
|
}
|
|
33095
33556
|
return false;
|
|
33096
33557
|
};
|
|
33097
|
-
await new Promise((
|
|
33558
|
+
await new Promise((resolve8) => {
|
|
33098
33559
|
const flushAndExit = () => {
|
|
33099
33560
|
try {
|
|
33100
33561
|
if (persistTimer) {
|
|
@@ -33104,7 +33565,7 @@ function registerUiCommand(program2) {
|
|
|
33104
33565
|
writeTuiState(snapshotForPersist());
|
|
33105
33566
|
} catch {}
|
|
33106
33567
|
shutdown(handles);
|
|
33107
|
-
|
|
33568
|
+
resolve8();
|
|
33108
33569
|
};
|
|
33109
33570
|
const executePaletteCommand = (id) => {
|
|
33110
33571
|
if (id.startsWith("tab:")) {
|
|
@@ -33713,7 +34174,7 @@ function registerUiCommand(program2) {
|
|
|
33713
34174
|
|
|
33714
34175
|
// src/commands/update.ts
|
|
33715
34176
|
import { spawn as spawn9 } from "node:child_process";
|
|
33716
|
-
import { dirname as
|
|
34177
|
+
import { dirname as dirname10 } from "node:path";
|
|
33717
34178
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
33718
34179
|
function detectInstallContext() {
|
|
33719
34180
|
const override = process.env["MCPSH_PACKAGE_MANAGER"]?.trim().toLowerCase();
|
|
@@ -33726,7 +34187,7 @@ function detectInstallContext() {
|
|
|
33726
34187
|
}
|
|
33727
34188
|
const here = (() => {
|
|
33728
34189
|
try {
|
|
33729
|
-
return
|
|
34190
|
+
return dirname10(fileURLToPath2(import.meta.url));
|
|
33730
34191
|
} catch {
|
|
33731
34192
|
return process.argv[1] ?? "";
|
|
33732
34193
|
}
|
|
@@ -33773,11 +34234,11 @@ function buildCommand(manager) {
|
|
|
33773
34234
|
return "npm install -g @mcpcloud/cli@latest";
|
|
33774
34235
|
}
|
|
33775
34236
|
}
|
|
33776
|
-
function
|
|
33777
|
-
return new Promise((
|
|
34237
|
+
function runShell(command) {
|
|
34238
|
+
return new Promise((resolve8) => {
|
|
33778
34239
|
const child = spawn9(command, { shell: true, stdio: "inherit" });
|
|
33779
|
-
child.on("close", (code) =>
|
|
33780
|
-
child.on("error", () =>
|
|
34240
|
+
child.on("close", (code) => resolve8(code ?? 1));
|
|
34241
|
+
child.on("error", () => resolve8(1));
|
|
33781
34242
|
});
|
|
33782
34243
|
}
|
|
33783
34244
|
function registerUpdateCommand(program2) {
|
|
@@ -33821,7 +34282,7 @@ function registerUpdateCommand(program2) {
|
|
|
33821
34282
|
const ctx = detectInstallContext();
|
|
33822
34283
|
printStep(`Running: ${c.cyan(ctx.command)}`);
|
|
33823
34284
|
printInfo(c.dim(` (${ctx.rationale}; override with MCPSH_PACKAGE_MANAGER)`));
|
|
33824
|
-
const code = await
|
|
34285
|
+
const code = await runShell(ctx.command);
|
|
33825
34286
|
if (code !== 0) {
|
|
33826
34287
|
if (isJsonMode())
|
|
33827
34288
|
printJson({
|