@imenam/mcp-gui-interface 1.0.3
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/src/gui-launcher.d.ts +19 -0
- package/dist/src/gui-launcher.js +104 -0
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.js +4 -0
- package/dist/src/ipc-hub.d.ts +11 -0
- package/dist/src/ipc-hub.js +65 -0
- package/dist/src/logger.d.ts +3 -0
- package/dist/src/logger.js +69 -0
- package/dist/src/proxy-client.d.ts +11 -0
- package/dist/src/proxy-client.js +114 -0
- package/dist/src/types.d.ts +41 -0
- package/dist/src/types.js +1 -0
- package/package.json +43 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ChildProcess } from "child_process";
|
|
2
|
+
import { IpcHub } from "./ipc-hub.js";
|
|
3
|
+
import type { GuiLauncherOptions, IpcMessage } from "./types.js";
|
|
4
|
+
export type { GuiLauncherOptions };
|
|
5
|
+
export declare class GuiLauncher {
|
|
6
|
+
private readonly options;
|
|
7
|
+
private guiProcess;
|
|
8
|
+
private ipcHub;
|
|
9
|
+
private restartCount;
|
|
10
|
+
private readonly maxRestarts;
|
|
11
|
+
private readonly restartDelay;
|
|
12
|
+
constructor(options: GuiLauncherOptions);
|
|
13
|
+
private registerSignalHandlers;
|
|
14
|
+
start(): ChildProcess;
|
|
15
|
+
getProcess(): ChildProcess | null;
|
|
16
|
+
getIpcHub(): IpcHub;
|
|
17
|
+
send(message: Omit<IpcMessage, "timestamp">): boolean;
|
|
18
|
+
cleanup(): Promise<void>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { fork } from "child_process";
|
|
2
|
+
import { createLogger } from "./logger.js";
|
|
3
|
+
import { IpcHub } from "./ipc-hub.js";
|
|
4
|
+
const logger = createLogger("GuiLauncher");
|
|
5
|
+
export class GuiLauncher {
|
|
6
|
+
options;
|
|
7
|
+
guiProcess = null;
|
|
8
|
+
ipcHub = new IpcHub();
|
|
9
|
+
restartCount = 0;
|
|
10
|
+
maxRestarts;
|
|
11
|
+
restartDelay;
|
|
12
|
+
constructor(options) {
|
|
13
|
+
this.options = options;
|
|
14
|
+
this.maxRestarts = options.maxRestarts ?? 3;
|
|
15
|
+
this.restartDelay = options.restartDelay ?? 2000;
|
|
16
|
+
this.registerSignalHandlers();
|
|
17
|
+
}
|
|
18
|
+
registerSignalHandlers() {
|
|
19
|
+
const syncCleanup = () => {
|
|
20
|
+
this.guiProcess?.kill();
|
|
21
|
+
this.guiProcess = null;
|
|
22
|
+
};
|
|
23
|
+
process.on("exit", syncCleanup);
|
|
24
|
+
process.on("SIGINT", async () => {
|
|
25
|
+
await this.cleanup();
|
|
26
|
+
process.exit(0);
|
|
27
|
+
});
|
|
28
|
+
process.on("SIGTERM", async () => {
|
|
29
|
+
await this.cleanup();
|
|
30
|
+
process.exit(0);
|
|
31
|
+
});
|
|
32
|
+
process.stdin.on("close", async () => {
|
|
33
|
+
logger.info("stdin closed, shutting down.");
|
|
34
|
+
await this.cleanup();
|
|
35
|
+
process.exit(0);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
start() {
|
|
39
|
+
if (this.guiProcess) {
|
|
40
|
+
this.guiProcess.kill();
|
|
41
|
+
}
|
|
42
|
+
logger.info(`Starting GUI process: ${this.options.guiPath}`);
|
|
43
|
+
this.guiProcess = fork(this.options.guiPath, [], {
|
|
44
|
+
stdio: ["ignore", "pipe", "inherit", "ipc"],
|
|
45
|
+
env: { ...process.env, ...this.options.env },
|
|
46
|
+
});
|
|
47
|
+
this.guiProcess.stdout?.on("data", (data) => {
|
|
48
|
+
process.stderr.write(data);
|
|
49
|
+
});
|
|
50
|
+
this.ipcHub = new IpcHub(this.guiProcess);
|
|
51
|
+
if (this.options.onMessage) {
|
|
52
|
+
this.ipcHub.onMessage(this.options.onMessage);
|
|
53
|
+
}
|
|
54
|
+
this.guiProcess.on("exit", (code, signal) => {
|
|
55
|
+
logger.info(`GUI process exited (code=${code}, signal=${signal})`);
|
|
56
|
+
// Only auto-restart on unexpected crashes (code !== 0 and code !== null)
|
|
57
|
+
const shouldRestart = code !== 0 && code !== null;
|
|
58
|
+
if (shouldRestart && this.restartCount < this.maxRestarts) {
|
|
59
|
+
this.restartCount++;
|
|
60
|
+
logger.warn(`Restarting GUI (attempt ${this.restartCount}/${this.maxRestarts}) in ${this.restartDelay}ms...`);
|
|
61
|
+
setTimeout(() => this.start(), this.restartDelay);
|
|
62
|
+
}
|
|
63
|
+
else if (shouldRestart) {
|
|
64
|
+
logger.error(`GUI crashed ${this.maxRestarts} times. Giving up.`);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
this.restartCount = 0;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
this.guiProcess.on("error", (err) => {
|
|
71
|
+
logger.error(`GUI process error: ${err.message}`);
|
|
72
|
+
});
|
|
73
|
+
logger.info(`GUI process started (PID ${this.guiProcess.pid})`);
|
|
74
|
+
return this.guiProcess;
|
|
75
|
+
}
|
|
76
|
+
getProcess() {
|
|
77
|
+
return this.guiProcess;
|
|
78
|
+
}
|
|
79
|
+
getIpcHub() {
|
|
80
|
+
return this.ipcHub;
|
|
81
|
+
}
|
|
82
|
+
send(message) {
|
|
83
|
+
return this.ipcHub.send({ ...message, timestamp: new Date().toISOString() });
|
|
84
|
+
}
|
|
85
|
+
async cleanup() {
|
|
86
|
+
if (!this.guiProcess)
|
|
87
|
+
return;
|
|
88
|
+
logger.info("Cleaning up GUI process...");
|
|
89
|
+
const proc = this.guiProcess;
|
|
90
|
+
this.guiProcess = null;
|
|
91
|
+
await new Promise((resolve) => {
|
|
92
|
+
const timeout = setTimeout(() => {
|
|
93
|
+
proc.kill("SIGKILL");
|
|
94
|
+
resolve();
|
|
95
|
+
}, 3000);
|
|
96
|
+
proc.once("exit", () => {
|
|
97
|
+
clearTimeout(timeout);
|
|
98
|
+
resolve();
|
|
99
|
+
});
|
|
100
|
+
proc.kill("SIGTERM");
|
|
101
|
+
});
|
|
102
|
+
logger.info("GUI process cleaned up.");
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { GuiLauncher, type GuiLauncherOptions } from "./gui-launcher.js";
|
|
2
|
+
export { ProxyClient, type RegisterOptions, type RegisterResult } from "./proxy-client.js";
|
|
3
|
+
export { IpcHub, type IpcMessage } from "./ipc-hub.js";
|
|
4
|
+
export { setupLogging, createLogger } from "./logger.js";
|
|
5
|
+
export type { ProxyConfig, Logger, SetupLoggingOptions } from "./types.js";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ChildProcess } from "child_process";
|
|
2
|
+
import type { IpcMessage } from "./types.js";
|
|
3
|
+
export type { IpcMessage };
|
|
4
|
+
export declare class IpcHub {
|
|
5
|
+
private targetProcess;
|
|
6
|
+
constructor(targetProcess?: ChildProcess);
|
|
7
|
+
setTargetProcess(targetProcess: ChildProcess): void;
|
|
8
|
+
send(message: IpcMessage): boolean;
|
|
9
|
+
onMessage(callback: (msg: IpcMessage) => void): void;
|
|
10
|
+
request(message: Omit<IpcMessage, "timestamp" | "correlationId">, timeout?: number): Promise<IpcMessage>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { createLogger } from "./logger.js";
|
|
2
|
+
const logger = createLogger("IpcHub");
|
|
3
|
+
export class IpcHub {
|
|
4
|
+
targetProcess = null;
|
|
5
|
+
constructor(targetProcess) {
|
|
6
|
+
if (targetProcess) {
|
|
7
|
+
this.targetProcess = targetProcess;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
setTargetProcess(targetProcess) {
|
|
11
|
+
this.targetProcess = targetProcess;
|
|
12
|
+
}
|
|
13
|
+
send(message) {
|
|
14
|
+
if (this.targetProcess && this.targetProcess.connected) {
|
|
15
|
+
try {
|
|
16
|
+
return this.targetProcess.send(message) ?? false;
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
logger.error(`Failed to send message: ${err.message}`);
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
onMessage(callback) {
|
|
26
|
+
if (this.targetProcess) {
|
|
27
|
+
this.targetProcess.on("message", (msg) => {
|
|
28
|
+
if (msg && typeof msg === "object" && "type" in msg) {
|
|
29
|
+
callback(msg);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async request(message, timeout = 2000) {
|
|
35
|
+
if (!this.targetProcess || !this.targetProcess.connected) {
|
|
36
|
+
throw new Error("IPC target process not connected");
|
|
37
|
+
}
|
|
38
|
+
const correlationId = Math.random().toString(36).substring(7);
|
|
39
|
+
const fullMessage = {
|
|
40
|
+
...message,
|
|
41
|
+
correlationId,
|
|
42
|
+
timestamp: new Date().toISOString(),
|
|
43
|
+
};
|
|
44
|
+
return new Promise((resolve, reject) => {
|
|
45
|
+
const timer = setTimeout(() => {
|
|
46
|
+
this.targetProcess?.off("message", handler);
|
|
47
|
+
reject(new Error(`IPC request timed out after ${timeout}ms`));
|
|
48
|
+
}, timeout);
|
|
49
|
+
const handler = (msg) => {
|
|
50
|
+
if (msg && msg.correlationId === correlationId) {
|
|
51
|
+
clearTimeout(timer);
|
|
52
|
+
this.targetProcess?.off("message", handler);
|
|
53
|
+
resolve(msg);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
this.targetProcess?.on("message", handler);
|
|
57
|
+
const sent = this.send(fullMessage);
|
|
58
|
+
if (!sent) {
|
|
59
|
+
clearTimeout(timer);
|
|
60
|
+
this.targetProcess?.off("message", handler);
|
|
61
|
+
reject(new Error("Failed to send IPC message"));
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { inspect } from "util";
|
|
4
|
+
let loggingInitialized = false;
|
|
5
|
+
let currentProcessLabel = "PROCESS";
|
|
6
|
+
let logStream = null;
|
|
7
|
+
let consoleErrorPatched = false;
|
|
8
|
+
let originalConsoleError = console.error;
|
|
9
|
+
function serialize(value) {
|
|
10
|
+
if (typeof value === "string") {
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
return inspect(value, { depth: 5, breakLength: 120, compact: true });
|
|
14
|
+
}
|
|
15
|
+
function serializeArgs(args) {
|
|
16
|
+
return args.map((arg) => serialize(arg)).join(" ");
|
|
17
|
+
}
|
|
18
|
+
function writeToFile(message) {
|
|
19
|
+
if (!logStream)
|
|
20
|
+
return;
|
|
21
|
+
const entry = `[${new Date().toISOString()}] [PID ${process.pid}] [${currentProcessLabel}] ${message}\n`;
|
|
22
|
+
logStream.write(entry);
|
|
23
|
+
}
|
|
24
|
+
function ensureConsoleErrorPatched() {
|
|
25
|
+
if (consoleErrorPatched)
|
|
26
|
+
return;
|
|
27
|
+
originalConsoleError = console.error.bind(console);
|
|
28
|
+
console.error = (...args) => {
|
|
29
|
+
writeToFile(serializeArgs(args));
|
|
30
|
+
originalConsoleError(...args);
|
|
31
|
+
};
|
|
32
|
+
consoleErrorPatched = true;
|
|
33
|
+
}
|
|
34
|
+
function normalizeMessage(message, meta) {
|
|
35
|
+
if (meta.length === 0)
|
|
36
|
+
return message;
|
|
37
|
+
return `${message} ${serializeArgs(meta)}`;
|
|
38
|
+
}
|
|
39
|
+
export function setupLogging(options) {
|
|
40
|
+
if (loggingInitialized)
|
|
41
|
+
return;
|
|
42
|
+
currentProcessLabel = options.processLabel;
|
|
43
|
+
const resolvedLogDir = options.logDir ?? path.join(process.cwd(), ".mcp-gui", "logs");
|
|
44
|
+
fs.mkdirSync(resolvedLogDir, { recursive: true });
|
|
45
|
+
const logFilePath = path.join(resolvedLogDir, "server.log");
|
|
46
|
+
logStream = fs.createWriteStream(logFilePath, { flags: "a", encoding: "utf8" });
|
|
47
|
+
logStream.on("error", (error) => {
|
|
48
|
+
originalConsoleError(`Critical: Failed to write to log file: ${serialize(error)}`);
|
|
49
|
+
});
|
|
50
|
+
ensureConsoleErrorPatched();
|
|
51
|
+
// Redirect console.log/info/warn to console.error to protect stdout
|
|
52
|
+
// which is reserved for the MCP JSON-RPC protocol in the MASTER process.
|
|
53
|
+
console.log = (...args) => console.error(...args);
|
|
54
|
+
console.info = (...args) => console.error(...args);
|
|
55
|
+
console.warn = (...args) => console.error(...args);
|
|
56
|
+
loggingInitialized = true;
|
|
57
|
+
writeToFile("[INFO] [LOGGER] Logging system initialized");
|
|
58
|
+
}
|
|
59
|
+
export function createLogger(scope) {
|
|
60
|
+
const logWithLevel = (level, message, ...meta) => {
|
|
61
|
+
console.error(`[${level}] [${scope}] ${normalizeMessage(message, meta)}`);
|
|
62
|
+
};
|
|
63
|
+
return {
|
|
64
|
+
debug: (message, ...meta) => logWithLevel("DEBUG", message, ...meta),
|
|
65
|
+
info: (message, ...meta) => logWithLevel("INFO", message, ...meta),
|
|
66
|
+
warn: (message, ...meta) => logWithLevel("WARN", message, ...meta),
|
|
67
|
+
error: (message, ...meta) => logWithLevel("ERROR", message, ...meta),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RegisterOptions, RegisterResult } from "./types.js";
|
|
2
|
+
export type { RegisterOptions, RegisterResult };
|
|
3
|
+
export declare class ProxyClient {
|
|
4
|
+
private proxyUrl;
|
|
5
|
+
private registeredPath?;
|
|
6
|
+
private status;
|
|
7
|
+
constructor(proxyUrl: string);
|
|
8
|
+
getStatus(): "connected" | "fallback" | "error";
|
|
9
|
+
register(options: RegisterOptions, fallbackPort: number): Promise<RegisterResult>;
|
|
10
|
+
unregister(): Promise<boolean>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import http from "http";
|
|
2
|
+
import { createLogger } from "./logger.js";
|
|
3
|
+
const logger = createLogger("ProxyClient");
|
|
4
|
+
export class ProxyClient {
|
|
5
|
+
proxyUrl;
|
|
6
|
+
registeredPath;
|
|
7
|
+
status = "error";
|
|
8
|
+
constructor(proxyUrl) {
|
|
9
|
+
this.proxyUrl = proxyUrl;
|
|
10
|
+
}
|
|
11
|
+
getStatus() {
|
|
12
|
+
return this.status;
|
|
13
|
+
}
|
|
14
|
+
async register(options, fallbackPort) {
|
|
15
|
+
this.registeredPath = options.path;
|
|
16
|
+
return new Promise((resolve) => {
|
|
17
|
+
let url;
|
|
18
|
+
try {
|
|
19
|
+
url = new URL(`${this.proxyUrl}/proxy/register`.replace(/\/+/g, "/").replace(":/", "://"));
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
logger.error(`Invalid Proxy URL: ${this.proxyUrl}`);
|
|
23
|
+
this.status = "error";
|
|
24
|
+
return resolve({ success: false, port: fallbackPort, error: "Invalid Proxy URL" });
|
|
25
|
+
}
|
|
26
|
+
const timeout = 1000;
|
|
27
|
+
logger.info(`Sending registration request to ${url.href}...`);
|
|
28
|
+
const req = http.request({
|
|
29
|
+
hostname: url.hostname,
|
|
30
|
+
port: url.port || (url.protocol === "https:" ? 443 : 80),
|
|
31
|
+
path: url.pathname,
|
|
32
|
+
method: "POST",
|
|
33
|
+
headers: { "Content-Type": "application/json" },
|
|
34
|
+
timeout,
|
|
35
|
+
}, (res) => {
|
|
36
|
+
logger.info(`Received response: HTTP ${res.statusCode}`);
|
|
37
|
+
let data = "";
|
|
38
|
+
res.on("data", (chunk) => (data += chunk));
|
|
39
|
+
res.on("end", () => {
|
|
40
|
+
if (res.statusCode === 200 || res.statusCode === 201) {
|
|
41
|
+
try {
|
|
42
|
+
const raw = JSON.parse(data);
|
|
43
|
+
if (raw &&
|
|
44
|
+
typeof raw === "object" &&
|
|
45
|
+
typeof raw.port === "number") {
|
|
46
|
+
logger.info(`Registration verified. Allocated port: ${raw.port}`);
|
|
47
|
+
this.status = "connected";
|
|
48
|
+
resolve({ success: true, port: raw.port, url: raw.url });
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
logger.error(`Invalid proxy response schema: ${data}`);
|
|
52
|
+
this.status = "fallback";
|
|
53
|
+
resolve({ success: false, port: fallbackPort, error: "Invalid proxy response schema" });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
logger.error(`JSON parse error: ${data}`);
|
|
58
|
+
this.status = "fallback";
|
|
59
|
+
resolve({ success: false, port: fallbackPort, error: "Invalid JSON response" });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
logger.error(`Registration rejected with HTTP ${res.statusCode}`);
|
|
64
|
+
this.status = "fallback";
|
|
65
|
+
resolve({ success: false, port: fallbackPort, error: `HTTP ${res.statusCode}` });
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
req.on("error", (err) => {
|
|
70
|
+
logger.error(`Registration error: ${err.message}`);
|
|
71
|
+
this.status = "fallback";
|
|
72
|
+
resolve({ success: false, port: fallbackPort, error: err.message });
|
|
73
|
+
});
|
|
74
|
+
req.on("timeout", () => {
|
|
75
|
+
logger.error(`Registration timeout after ${timeout}ms`);
|
|
76
|
+
req.destroy();
|
|
77
|
+
this.status = "fallback";
|
|
78
|
+
resolve({ success: false, port: fallbackPort, error: "Timeout" });
|
|
79
|
+
});
|
|
80
|
+
req.write(JSON.stringify(options));
|
|
81
|
+
req.end();
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
async unregister() {
|
|
85
|
+
if (!this.registeredPath)
|
|
86
|
+
return true;
|
|
87
|
+
return new Promise((resolve) => {
|
|
88
|
+
let url;
|
|
89
|
+
try {
|
|
90
|
+
url = new URL(`${this.proxyUrl}/proxy/unregister`.replace(/\/+/g, "/").replace(":/", "://"));
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return resolve(false);
|
|
94
|
+
}
|
|
95
|
+
const req = http.request({
|
|
96
|
+
hostname: url.hostname,
|
|
97
|
+
port: url.port || (url.protocol === "https:" ? 443 : 80),
|
|
98
|
+
path: url.pathname,
|
|
99
|
+
method: "DELETE",
|
|
100
|
+
headers: { "Content-Type": "application/json" },
|
|
101
|
+
timeout: 2000,
|
|
102
|
+
}, (res) => {
|
|
103
|
+
logger.info(`Unregister response: HTTP ${res.statusCode}`);
|
|
104
|
+
resolve(res.statusCode === 200);
|
|
105
|
+
});
|
|
106
|
+
req.on("error", (err) => {
|
|
107
|
+
logger.error(`Unregister error: ${err.message}`);
|
|
108
|
+
resolve(false);
|
|
109
|
+
});
|
|
110
|
+
req.write(JSON.stringify({ path: this.registeredPath }));
|
|
111
|
+
req.end();
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface IpcMessage {
|
|
2
|
+
type: string;
|
|
3
|
+
correlationId?: string;
|
|
4
|
+
data?: any;
|
|
5
|
+
error?: string;
|
|
6
|
+
timestamp: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ProxyConfig {
|
|
9
|
+
proxyUrl?: string;
|
|
10
|
+
appPath: string;
|
|
11
|
+
appName: string;
|
|
12
|
+
fallbackPort: number;
|
|
13
|
+
}
|
|
14
|
+
export interface RegisterResult {
|
|
15
|
+
success: boolean;
|
|
16
|
+
port: number;
|
|
17
|
+
url?: string;
|
|
18
|
+
error?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface RegisterOptions {
|
|
21
|
+
path: string;
|
|
22
|
+
name?: string;
|
|
23
|
+
port?: number;
|
|
24
|
+
}
|
|
25
|
+
export interface Logger {
|
|
26
|
+
debug: (message: string, ...meta: unknown[]) => void;
|
|
27
|
+
info: (message: string, ...meta: unknown[]) => void;
|
|
28
|
+
warn: (message: string, ...meta: unknown[]) => void;
|
|
29
|
+
error: (message: string, ...meta: unknown[]) => void;
|
|
30
|
+
}
|
|
31
|
+
export interface SetupLoggingOptions {
|
|
32
|
+
processLabel: string;
|
|
33
|
+
logDir?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface GuiLauncherOptions {
|
|
36
|
+
guiPath: string;
|
|
37
|
+
env?: Record<string, string>;
|
|
38
|
+
maxRestarts?: number;
|
|
39
|
+
restartDelay?: number;
|
|
40
|
+
onMessage?: (msg: IpcMessage) => void;
|
|
41
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@imenam/mcp-gui-interface",
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "Shared library for MCP GUI lifecycle management — spawning, IPC, proxy registration, logging",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/src/index.js",
|
|
7
|
+
"types": "./dist/src/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/src/index.js",
|
|
11
|
+
"types": "./dist/src/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc",
|
|
20
|
+
"build:watch": "tsc --watch",
|
|
21
|
+
"release": "npm version patch && npm run build && npm publish --access public && git push origin master --follow-tags"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"mcp",
|
|
25
|
+
"model-context-protocol",
|
|
26
|
+
"gui",
|
|
27
|
+
"ipc",
|
|
28
|
+
"lifecycle",
|
|
29
|
+
"proxy",
|
|
30
|
+
"ai",
|
|
31
|
+
"agent"
|
|
32
|
+
],
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/Matthieu-Pesnot-Pin/mcp-gui-interface.git"
|
|
36
|
+
},
|
|
37
|
+
"author": "Matthieu Pesnot-Pin",
|
|
38
|
+
"license": "ISC",
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^25.4.0",
|
|
41
|
+
"typescript": "^5.9.3"
|
|
42
|
+
}
|
|
43
|
+
}
|