mcp-for-i 0.1.4 → 0.1.5
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/controlplane/runtimeService.js +150 -0
- package/dist/controlplane/server.js +8 -0
- package/dist/controlplane/ui.js +800 -499
- package/package.json +1 -1
|
@@ -19,6 +19,55 @@ export class RuntimeService {
|
|
|
19
19
|
getStatus() {
|
|
20
20
|
return this.state;
|
|
21
21
|
}
|
|
22
|
+
async getVersions(options) {
|
|
23
|
+
const mode = await this.detectExecutionModeQuiet();
|
|
24
|
+
const checkedAt = new Date().toISOString();
|
|
25
|
+
const mcpInstalled = await this.readLocalPackageVersion(this.rootDir);
|
|
26
|
+
const mcpLatest = await this.readLatestPublishedVersion();
|
|
27
|
+
const mcpStatus = mcpInstalled && mcpLatest
|
|
28
|
+
? (mcpInstalled === mcpLatest ? "latest" : "update-available")
|
|
29
|
+
: "unknown";
|
|
30
|
+
const configuredRepo = sanitizeRepoUrl(options?.repoUrl) || process.env.MCP_FOR_I_SKILLS_REPO || DEFAULT_SKILLS_REPO;
|
|
31
|
+
const configuredBranch = sanitizeBranch(options?.branch) || process.env.MCP_FOR_I_SKILLS_BRANCH || "main";
|
|
32
|
+
const skillsDir = path.join(this.rootDir, "skills");
|
|
33
|
+
const skillsGit = path.join(skillsDir, ".git");
|
|
34
|
+
const skills = {
|
|
35
|
+
configuredRepo,
|
|
36
|
+
configuredBranch,
|
|
37
|
+
status: "not-installed"
|
|
38
|
+
};
|
|
39
|
+
if (await pathExists(skillsDir)) {
|
|
40
|
+
if (await pathExists(skillsGit)) {
|
|
41
|
+
const localRepo = await this.getGitRemoteUrlQuiet(skillsDir);
|
|
42
|
+
const localBranch = await this.getGitBranchQuiet(skillsDir);
|
|
43
|
+
const localCommit = await this.getGitCommitQuiet(skillsDir, true);
|
|
44
|
+
const latestCommit = await this.getRemoteBranchCommitQuiet(configuredRepo, configuredBranch, true);
|
|
45
|
+
skills.localRepo = localRepo;
|
|
46
|
+
skills.localBranch = localBranch;
|
|
47
|
+
skills.localCommit = localCommit;
|
|
48
|
+
skills.latestCommit = latestCommit;
|
|
49
|
+
if (localCommit && latestCommit) {
|
|
50
|
+
skills.status = localCommit === latestCommit ? "latest" : "update-available";
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
skills.status = "unknown";
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
skills.status = "unknown";
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
checkedAt,
|
|
62
|
+
mcp: {
|
|
63
|
+
source: mode,
|
|
64
|
+
installedVersion: mcpInstalled,
|
|
65
|
+
latestVersion: mcpLatest,
|
|
66
|
+
status: mcpStatus
|
|
67
|
+
},
|
|
68
|
+
skills
|
|
69
|
+
};
|
|
70
|
+
}
|
|
22
71
|
async installOrRepair() {
|
|
23
72
|
return this.runJob("install", async (push) => {
|
|
24
73
|
const mode = await this.detectExecutionMode(push);
|
|
@@ -239,6 +288,10 @@ export class RuntimeService {
|
|
|
239
288
|
push("No git checkout found. Falling back to npm-based package management.");
|
|
240
289
|
return "package-install";
|
|
241
290
|
}
|
|
291
|
+
async detectExecutionModeQuiet() {
|
|
292
|
+
const gitDir = path.join(this.rootDir, ".git");
|
|
293
|
+
return await pathExists(gitDir) ? "git-checkout" : "package-install";
|
|
294
|
+
}
|
|
242
295
|
async buildIfPresent(push) {
|
|
243
296
|
const tsconfigPath = path.join(this.rootDir, "tsconfig.json");
|
|
244
297
|
if (!await pathExists(tsconfigPath)) {
|
|
@@ -304,6 +357,103 @@ export class RuntimeService {
|
|
|
304
357
|
push("Control plane will stop briefly, then restart automatically.");
|
|
305
358
|
push(`Background update log: ${logPath}`);
|
|
306
359
|
}
|
|
360
|
+
async readLocalPackageVersion(cwd) {
|
|
361
|
+
try {
|
|
362
|
+
const packagePath = path.join(cwd, "package.json");
|
|
363
|
+
const raw = await fs.readFile(packagePath, "utf8");
|
|
364
|
+
const parsed = JSON.parse(raw);
|
|
365
|
+
return typeof parsed.version === "string" ? parsed.version : undefined;
|
|
366
|
+
}
|
|
367
|
+
catch {
|
|
368
|
+
return undefined;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
async readLatestPublishedVersion() {
|
|
372
|
+
try {
|
|
373
|
+
const output = await this.runNpmCapture(["view", PACKAGE_NAME, "version", "--json"], this.rootDir);
|
|
374
|
+
const parsed = JSON.parse(output || "null");
|
|
375
|
+
if (typeof parsed === "string")
|
|
376
|
+
return parsed.trim();
|
|
377
|
+
if (parsed && typeof parsed.version === "string")
|
|
378
|
+
return parsed.version.trim();
|
|
379
|
+
return undefined;
|
|
380
|
+
}
|
|
381
|
+
catch {
|
|
382
|
+
return undefined;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
async getGitBranchQuiet(cwd) {
|
|
386
|
+
try {
|
|
387
|
+
const branch = await this.runCommandCaptureSilent("git", ["rev-parse", "--abbrev-ref", "HEAD"], cwd);
|
|
388
|
+
return branch || undefined;
|
|
389
|
+
}
|
|
390
|
+
catch {
|
|
391
|
+
return undefined;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
async getGitCommitQuiet(cwd, short = false) {
|
|
395
|
+
try {
|
|
396
|
+
const args = short ? ["rev-parse", "--short", "HEAD"] : ["rev-parse", "HEAD"];
|
|
397
|
+
const commit = await this.runCommandCaptureSilent("git", args, cwd);
|
|
398
|
+
return commit || undefined;
|
|
399
|
+
}
|
|
400
|
+
catch {
|
|
401
|
+
return undefined;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
async getGitRemoteUrlQuiet(cwd) {
|
|
405
|
+
try {
|
|
406
|
+
const value = await this.runCommandCaptureSilent("git", ["remote", "get-url", "origin"], cwd);
|
|
407
|
+
return value || undefined;
|
|
408
|
+
}
|
|
409
|
+
catch {
|
|
410
|
+
return undefined;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
async getRemoteBranchCommitQuiet(repoUrl, branch, short = false) {
|
|
414
|
+
try {
|
|
415
|
+
const out = await this.runCommandCaptureSilent("git", ["ls-remote", repoUrl, branch], this.rootDir);
|
|
416
|
+
const firstLine = out.split(/\r?\n/).find(Boolean);
|
|
417
|
+
if (!firstLine)
|
|
418
|
+
return undefined;
|
|
419
|
+
const [hash] = firstLine.trim().split(/\s+/);
|
|
420
|
+
if (!hash)
|
|
421
|
+
return undefined;
|
|
422
|
+
return short ? hash.slice(0, 7) : hash;
|
|
423
|
+
}
|
|
424
|
+
catch {
|
|
425
|
+
return undefined;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
async runNpmCapture(args, cwd) {
|
|
429
|
+
const npm = await resolveNpmLaunch();
|
|
430
|
+
if (npm.mode === "node-cli") {
|
|
431
|
+
return this.runCommandCaptureSilent(process.execPath, [npm.path, ...args], cwd);
|
|
432
|
+
}
|
|
433
|
+
return this.runCommandCaptureSilent(npm.path, args, cwd);
|
|
434
|
+
}
|
|
435
|
+
runCommandCaptureSilent(command, args, cwd) {
|
|
436
|
+
return new Promise((resolve, reject) => {
|
|
437
|
+
const child = spawn(command, args, {
|
|
438
|
+
cwd,
|
|
439
|
+
shell: process.platform === "win32"
|
|
440
|
+
});
|
|
441
|
+
const stdout = [];
|
|
442
|
+
const stderr = [];
|
|
443
|
+
child.stdout.on("data", chunk => stdout.push(String(chunk)));
|
|
444
|
+
child.stderr.on("data", chunk => stderr.push(String(chunk)));
|
|
445
|
+
child.on("error", reject);
|
|
446
|
+
child.on("exit", code => {
|
|
447
|
+
if (code === 0) {
|
|
448
|
+
resolve(stdout.join("").trim());
|
|
449
|
+
}
|
|
450
|
+
else {
|
|
451
|
+
const errText = stderr.join("").trim();
|
|
452
|
+
reject(new Error(errText || `Command failed (${code}): ${command} ${args.join(" ")}`));
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
});
|
|
456
|
+
}
|
|
307
457
|
}
|
|
308
458
|
function makeJob(id) {
|
|
309
459
|
return {
|
|
@@ -52,6 +52,14 @@ async function routeRequest(req, res, services) {
|
|
|
52
52
|
sendJson(res, 200, { jobs: services.runtime.getStatus() });
|
|
53
53
|
return;
|
|
54
54
|
}
|
|
55
|
+
if (method === "GET" && pathname === "/api/runtime/versions") {
|
|
56
|
+
const versions = await services.runtime.getVersions({
|
|
57
|
+
repoUrl: url.searchParams.get("repoUrl") || undefined,
|
|
58
|
+
branch: url.searchParams.get("branch") || undefined
|
|
59
|
+
});
|
|
60
|
+
sendJson(res, 200, versions);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
55
63
|
if (method === "POST" && pathname === "/api/runtime/install") {
|
|
56
64
|
const job = await services.runtime.installOrRepair();
|
|
57
65
|
sendJson(res, 202, { job });
|