repoburg 1.3.161 → 1.3.163
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/backend/dist/src/app.module.js +2 -0
- package/backend/dist/src/app.module.js.map +1 -1
- package/backend/dist/src/application-state/application-state.service.d.ts +4 -0
- package/backend/dist/src/application-state/application-state.service.js +26 -2
- package/backend/dist/src/application-state/application-state.service.js.map +1 -1
- package/backend/dist/src/llm-orchestration/tool-schema.service.js +2 -24
- package/backend/dist/src/llm-orchestration/tool-schema.service.js.map +1 -1
- package/backend/dist/src/llm-provider/ollama-llm.provider.js +1 -0
- package/backend/dist/src/llm-provider/ollama-llm.provider.js.map +1 -1
- package/backend/dist/src/mcp-server/mcp-internal.controller.d.ts +20 -0
- package/backend/dist/src/mcp-server/mcp-internal.controller.js +50 -0
- package/backend/dist/src/mcp-server/mcp-internal.controller.js.map +1 -0
- package/backend/dist/src/mcp-server/mcp-server.bootstrap.d.ts +2 -0
- package/backend/dist/src/mcp-server/mcp-server.bootstrap.js +160 -0
- package/backend/dist/src/mcp-server/mcp-server.bootstrap.js.map +1 -0
- package/backend/dist/src/mcp-server/mcp-server.constants.d.ts +7 -0
- package/backend/dist/src/mcp-server/mcp-server.constants.js +19 -0
- package/backend/dist/src/mcp-server/mcp-server.constants.js.map +1 -0
- package/backend/dist/src/mcp-server/mcp-server.controller.d.ts +27 -0
- package/backend/dist/src/mcp-server/mcp-server.controller.js +72 -0
- package/backend/dist/src/mcp-server/mcp-server.controller.js.map +1 -0
- package/backend/dist/src/mcp-server/mcp-server.module.d.ts +2 -0
- package/backend/dist/src/mcp-server/mcp-server.module.js +33 -0
- package/backend/dist/src/mcp-server/mcp-server.module.js.map +1 -0
- package/backend/dist/src/mcp-server/mcp-server.process.service.d.ts +23 -0
- package/backend/dist/src/mcp-server/mcp-server.process.service.js +130 -0
- package/backend/dist/src/mcp-server/mcp-server.process.service.js.map +1 -0
- package/backend/dist/src/mcp-server/mcp-server.service.d.ts +38 -0
- package/backend/dist/src/mcp-server/mcp-server.service.js +257 -0
- package/backend/dist/src/mcp-server/mcp-server.service.js.map +1 -0
- package/backend/dist/src/seeding/data/agents/control-repoburg.d.ts +1 -1
- package/backend/dist/src/seeding/data/agents/control-repoburg.js +1 -1
- package/backend/dist/src/seeding/data/agents/control-repoburg.js.map +1 -1
- package/backend/dist/src/seeding/data/agents/default-agent.d.ts +1 -1
- package/backend/dist/src/seeding/data/agents/default-agent.js +1 -1
- package/backend/dist/src/seeding/data/agents/default-agent.js.map +1 -1
- package/backend/dist/src/utils/index.d.ts +1 -0
- package/backend/dist/src/utils/index.js +1 -0
- package/backend/dist/src/utils/index.js.map +1 -1
- package/backend/dist/src/utils/tool-schema-converter.d.ts +8 -0
- package/backend/dist/src/utils/tool-schema-converter.js +33 -0
- package/backend/dist/src/utils/tool-schema-converter.js.map +1 -0
- package/backend/dist/tsconfig.build.tsbuildinfo +1 -1
- package/backend/package.json +2 -0
- package/daemon/dist/index.js +25 -0
- package/daemon/dist/mcp-gateway.js +381 -0
- package/package.json +1 -1
- package/backend/.env +0 -27
- package/backend/database.sqlite +0 -0
- package/backend/section-modified.txt +0 -1
- package/daemon/dist/api/system.js +0 -25
- package/daemon/dist/daemon.blob +0 -0
- package/visual-editor-proxy/.repoburg/data.sqlite +0 -0
package/backend/package.json
CHANGED
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
"start:dev": "mkdir -p ../.repoburg/logs && NODE_ENV=production script -q ../.repoburg/logs/backend.log nest start --watch",
|
|
13
13
|
"start:debug": "NODE_ENV=production nest start --debug --watch",
|
|
14
14
|
"start:prod": "NODE_ENV=production node dist/main",
|
|
15
|
+
"mcp-server": "ts-node -r tsconfig-paths/register src/mcp-server/mcp-server.bootstrap.ts",
|
|
16
|
+
"mcp-server:prod": "NODE_ENV=production node dist/mcp-server/mcp-server.bootstrap.js",
|
|
15
17
|
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
|
16
18
|
"test": "NODE_ENV=development NODE_OPTIONS=--experimental-vm-modules jest --bail",
|
|
17
19
|
"test:watch": "jest --watch",
|
package/daemon/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ const serviceManager_1 = require("./serviceManager");
|
|
|
12
12
|
const services_1 = __importDefault(require("./api/services"));
|
|
13
13
|
const auth_1 = __importDefault(require("./api/auth"));
|
|
14
14
|
const registry_1 = __importDefault(require("./api/registry"));
|
|
15
|
+
const mcp_gateway_1 = require("./mcp-gateway");
|
|
15
16
|
const PORT = 9998;
|
|
16
17
|
async function main() {
|
|
17
18
|
await serviceManager_1.serviceManager.connect();
|
|
@@ -28,6 +29,20 @@ async function main() {
|
|
|
28
29
|
app.use('/services', services_1.default);
|
|
29
30
|
app.use('/auth', (0, auth_1.default)(wss));
|
|
30
31
|
app.use('/registry', registry_1.default);
|
|
32
|
+
// MCP Gateway — single MCP entry point managing all instances (streamableHttp).
|
|
33
|
+
app.post('/mcp', (req, res) => mcp_gateway_1.mcpGateway.handleExpressRequest(req, res));
|
|
34
|
+
app.get('/mcp', (req, res) => mcp_gateway_1.mcpGateway.handleExpressRequest(req, res));
|
|
35
|
+
app.delete('/mcp', (req, res) => mcp_gateway_1.mcpGateway.handleExpressRequest(req, res));
|
|
36
|
+
// MCP Gateway info endpoint for the management UI.
|
|
37
|
+
app.get('/mcp-gateway-info', async (req, res) => {
|
|
38
|
+
try {
|
|
39
|
+
const info = await mcp_gateway_1.mcpGateway.getGatewayInfo();
|
|
40
|
+
res.json(info);
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
res.status(500).json({ error: err.message });
|
|
44
|
+
}
|
|
45
|
+
});
|
|
31
46
|
app.get('/health', (req, res) => {
|
|
32
47
|
res.status(200).json({ status: 'ok' });
|
|
33
48
|
});
|
|
@@ -38,6 +53,16 @@ async function main() {
|
|
|
38
53
|
});
|
|
39
54
|
server.listen(PORT, () => {
|
|
40
55
|
console.log(`Repoburg Platform Daemon listening on http://localhost:${PORT}`);
|
|
56
|
+
console.log(`MCP Gateway available at http://localhost:${PORT}/mcp`);
|
|
57
|
+
});
|
|
58
|
+
// Clean up MCP gateway sessions on shutdown.
|
|
59
|
+
process.on('SIGINT', async () => {
|
|
60
|
+
await mcp_gateway_1.mcpGateway.closeAllSessions();
|
|
61
|
+
process.exit(0);
|
|
62
|
+
});
|
|
63
|
+
process.on('SIGTERM', async () => {
|
|
64
|
+
await mcp_gateway_1.mcpGateway.closeAllSessions();
|
|
65
|
+
process.exit(0);
|
|
41
66
|
});
|
|
42
67
|
}
|
|
43
68
|
main().catch(err => {
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* MCP Gateway — a single MCP server entry point hosted in the daemon that
|
|
4
|
+
* manages **all** Repoburg instances.
|
|
5
|
+
*
|
|
6
|
+
* MCP clients (ChatGPT tunnel, Claude Desktop) connect once to
|
|
7
|
+
* `http://localhost:9998/mcp`. Each `manage_*` tool call includes an
|
|
8
|
+
* `instance_name` parameter; the gateway looks up the instance's backend port
|
|
9
|
+
* from `stateManager` and proxies the call to that instance's
|
|
10
|
+
* `POST /mcp-internal/execute-tool` endpoint.
|
|
11
|
+
*
|
|
12
|
+
* The gateway also exposes daemon-level tools (`list_instances`,
|
|
13
|
+
* `start_instance`, `stop_instance`) that operate on the daemon's own PM2
|
|
14
|
+
* process registry — no instance routing needed.
|
|
15
|
+
*
|
|
16
|
+
* Tool definitions for `manage_*` are fetched once from any running instance's
|
|
17
|
+
* `GET /mcp-internal/tools` endpoint, augmented with an `instance_name`
|
|
18
|
+
* required parameter, and cached.
|
|
19
|
+
*/
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.mcpGateway = void 0;
|
|
22
|
+
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
23
|
+
const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
24
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
25
|
+
const crypto_1 = require("crypto");
|
|
26
|
+
const stateManager_1 = require("./stateManager");
|
|
27
|
+
const serviceManager_1 = require("./serviceManager");
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// Gateway
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
const GATEWAY_INFO = { name: 'repoburg-mcp-gateway', version: '1.0.0' };
|
|
32
|
+
const INSTANCE_NAME_PARAM = {
|
|
33
|
+
type: 'string',
|
|
34
|
+
description: 'Name of the Repoburg instance to manage. Use the list_instances tool to see available instances.',
|
|
35
|
+
};
|
|
36
|
+
class McpGateway {
|
|
37
|
+
constructor() {
|
|
38
|
+
this.sessions = new Map();
|
|
39
|
+
this.toolCache = null;
|
|
40
|
+
this.isRefreshing = false;
|
|
41
|
+
}
|
|
42
|
+
// -------------------------------------------------------------------------
|
|
43
|
+
// Express route handler — mounted at app.post/get/delete('/mcp', ...)
|
|
44
|
+
// -------------------------------------------------------------------------
|
|
45
|
+
/**
|
|
46
|
+
* Express-compatible handler for the `/mcp` route. Manages stateful
|
|
47
|
+
* streamableHttp sessions: a new transport + Server is created on the
|
|
48
|
+
* `initialize` request and reused for subsequent requests on that session.
|
|
49
|
+
*/
|
|
50
|
+
async handleExpressRequest(req, res) {
|
|
51
|
+
try {
|
|
52
|
+
if (req.method === 'POST') {
|
|
53
|
+
const body = req.body;
|
|
54
|
+
const sessionId = req.headers['mcp-session-id'];
|
|
55
|
+
const existing = sessionId ? this.sessions.get(sessionId) : undefined;
|
|
56
|
+
if (existing) {
|
|
57
|
+
await existing.handleRequest(req, res, body);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (body && (0, types_js_1.isInitializeRequest)(body)) {
|
|
61
|
+
const transport = new streamableHttp_js_1.StreamableHTTPServerTransport({
|
|
62
|
+
sessionIdGenerator: () => (0, crypto_1.randomUUID)(),
|
|
63
|
+
onsessioninitialized: (sid) => {
|
|
64
|
+
this.sessions.set(sid, transport);
|
|
65
|
+
},
|
|
66
|
+
onsessionclosed: (sid) => {
|
|
67
|
+
this.sessions.delete(sid);
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
transport.onclose = () => {
|
|
71
|
+
const sid = transport.sessionId;
|
|
72
|
+
if (sid)
|
|
73
|
+
this.sessions.delete(sid);
|
|
74
|
+
};
|
|
75
|
+
const server = this.createMcpServerInstance();
|
|
76
|
+
await server.connect(transport);
|
|
77
|
+
await transport.handleRequest(req, res, body);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
// No valid session — reject.
|
|
81
|
+
res.status(400).json({
|
|
82
|
+
jsonrpc: '2.0',
|
|
83
|
+
error: {
|
|
84
|
+
code: -32000,
|
|
85
|
+
message: 'Bad Request: no valid session ID provided',
|
|
86
|
+
},
|
|
87
|
+
id: null,
|
|
88
|
+
});
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (req.method === 'GET' || req.method === 'DELETE') {
|
|
92
|
+
const sessionId = req.headers['mcp-session-id'];
|
|
93
|
+
const transport = sessionId ? this.sessions.get(sessionId) : undefined;
|
|
94
|
+
if (!transport) {
|
|
95
|
+
res.status(400).send('Invalid or missing session ID');
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
await transport.handleRequest(req, res);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
res.status(405).send('Method not allowed');
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
console.error(`[McpGateway] Error handling MCP request: ${err.message}`);
|
|
105
|
+
if (!res.headersSent) {
|
|
106
|
+
res.status(500).send('Internal server error');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// -------------------------------------------------------------------------
|
|
111
|
+
// MCP Server factory
|
|
112
|
+
// -------------------------------------------------------------------------
|
|
113
|
+
/**
|
|
114
|
+
* Creates a fresh MCP `Server` wired with ListTools + CallTool handlers.
|
|
115
|
+
* One Server per client session.
|
|
116
|
+
*/
|
|
117
|
+
createMcpServerInstance() {
|
|
118
|
+
const server = new index_js_1.Server(GATEWAY_INFO, { capabilities: { tools: {} } });
|
|
119
|
+
server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
|
|
120
|
+
// Best-effort cache refresh (non-blocking if already refreshing).
|
|
121
|
+
await this.refreshToolCache();
|
|
122
|
+
const daemonTools = this.buildDaemonTools();
|
|
123
|
+
const manageTools = this.toolCache ?? [];
|
|
124
|
+
return { tools: [...daemonTools, ...manageTools] };
|
|
125
|
+
});
|
|
126
|
+
server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
127
|
+
const { name, arguments: args } = request.params;
|
|
128
|
+
const toolArgs = args ?? {};
|
|
129
|
+
// Daemon-level tools
|
|
130
|
+
if (name === 'list_instances') {
|
|
131
|
+
return this.handleListInstances();
|
|
132
|
+
}
|
|
133
|
+
if (name === 'start_instance') {
|
|
134
|
+
return this.handleStartInstance(toolArgs);
|
|
135
|
+
}
|
|
136
|
+
if (name === 'stop_instance') {
|
|
137
|
+
return this.handleStopInstance(toolArgs);
|
|
138
|
+
}
|
|
139
|
+
// manage_* tools — proxy to the target instance
|
|
140
|
+
return this.handleManageTool(name, toolArgs);
|
|
141
|
+
});
|
|
142
|
+
return server;
|
|
143
|
+
}
|
|
144
|
+
// -------------------------------------------------------------------------
|
|
145
|
+
// Tool cache management
|
|
146
|
+
// -------------------------------------------------------------------------
|
|
147
|
+
/**
|
|
148
|
+
* Fetches tool definitions from a running instance's
|
|
149
|
+
* `GET /mcp-internal/tools` endpoint, augments each with `instance_name`,
|
|
150
|
+
* and caches the result. Safe to call repeatedly — skips if already
|
|
151
|
+
* refreshing or cache is already populated.
|
|
152
|
+
*/
|
|
153
|
+
async refreshToolCache() {
|
|
154
|
+
if (this.isRefreshing || this.toolCache)
|
|
155
|
+
return;
|
|
156
|
+
this.isRefreshing = true;
|
|
157
|
+
try {
|
|
158
|
+
const runningInstance = this.findRunningInstance();
|
|
159
|
+
if (!runningInstance) {
|
|
160
|
+
// No instance running — cache stays null; daemon tools still available.
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const resp = await fetch(`http://localhost:${runningInstance.port}/mcp-internal/tools`);
|
|
164
|
+
if (!resp.ok) {
|
|
165
|
+
console.warn(`[McpGateway] Failed to fetch tools from instance '${runningInstance.name}' (${resp.status}).`);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const tools = (await resp.json());
|
|
169
|
+
this.toolCache = tools.map((tool) => this.augmentWithInstanceName(tool));
|
|
170
|
+
console.log(`[McpGateway] Cached ${this.toolCache.length} manage_* tools from instance '${runningInstance.name}'.`);
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
console.warn(`[McpGateway] Could not refresh tool cache: ${err.message}`);
|
|
174
|
+
}
|
|
175
|
+
finally {
|
|
176
|
+
this.isRefreshing = false;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Forces a cache refresh (called after an instance starts). Clears the cache
|
|
181
|
+
* first so the next ListTools re-fetches.
|
|
182
|
+
*/
|
|
183
|
+
invalidateToolCache() {
|
|
184
|
+
this.toolCache = null;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Adds `instance_name` as a required string parameter to a tool definition.
|
|
188
|
+
*/
|
|
189
|
+
augmentWithInstanceName(tool) {
|
|
190
|
+
const properties = { ...tool.inputSchema.properties, instance_name: INSTANCE_NAME_PARAM };
|
|
191
|
+
const required = [
|
|
192
|
+
...(tool.inputSchema.required ?? []),
|
|
193
|
+
'instance_name',
|
|
194
|
+
];
|
|
195
|
+
return {
|
|
196
|
+
name: tool.name,
|
|
197
|
+
description: tool.description,
|
|
198
|
+
inputSchema: {
|
|
199
|
+
type: 'object',
|
|
200
|
+
properties,
|
|
201
|
+
required,
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
// -------------------------------------------------------------------------
|
|
206
|
+
// Daemon-level tool handlers
|
|
207
|
+
// -------------------------------------------------------------------------
|
|
208
|
+
buildDaemonTools() {
|
|
209
|
+
return [
|
|
210
|
+
{
|
|
211
|
+
name: 'list_instances',
|
|
212
|
+
description: 'List all registered Repoburg instances with their name, project path, port, and running status.',
|
|
213
|
+
inputSchema: { type: 'object', properties: {} },
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
name: 'start_instance',
|
|
217
|
+
description: 'Start a Repoburg instance for a project path. The instance runs as a PM2-managed backend process with its own SQLite database.',
|
|
218
|
+
inputSchema: {
|
|
219
|
+
type: 'object',
|
|
220
|
+
properties: {
|
|
221
|
+
path: {
|
|
222
|
+
type: 'string',
|
|
223
|
+
description: 'Absolute path to the project directory.',
|
|
224
|
+
},
|
|
225
|
+
name: {
|
|
226
|
+
type: 'string',
|
|
227
|
+
description: 'Optional custom instance name (defaults to the directory basename).',
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
required: ['path'],
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
name: 'stop_instance',
|
|
235
|
+
description: 'Stop a running Repoburg instance by name.',
|
|
236
|
+
inputSchema: {
|
|
237
|
+
type: 'object',
|
|
238
|
+
properties: {
|
|
239
|
+
name: {
|
|
240
|
+
type: 'string',
|
|
241
|
+
description: 'Name of the instance to stop.',
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
required: ['name'],
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
];
|
|
248
|
+
}
|
|
249
|
+
handleListInstances() {
|
|
250
|
+
const services = stateManager_1.stateManager.getServices();
|
|
251
|
+
if (services.length === 0) {
|
|
252
|
+
return this.textResult('No Repoburg instances registered.');
|
|
253
|
+
}
|
|
254
|
+
const lines = services.map((s) => `- ${s.name}: ${s.status} (port ${s.port}, path: ${s.path})`);
|
|
255
|
+
return this.textResult(`Repoburg instances (${services.length}):\n${lines.join('\n')}`);
|
|
256
|
+
}
|
|
257
|
+
async handleStartInstance(args) {
|
|
258
|
+
const projectPath = args.path;
|
|
259
|
+
if (!projectPath) {
|
|
260
|
+
return this.errorResult('Missing required parameter: path');
|
|
261
|
+
}
|
|
262
|
+
try {
|
|
263
|
+
const service = await serviceManager_1.serviceManager.start(projectPath, args.name);
|
|
264
|
+
this.invalidateToolCache();
|
|
265
|
+
return this.textResult(`Instance '${service.name}' started on port ${service.port} (path: ${service.path}).`);
|
|
266
|
+
}
|
|
267
|
+
catch (err) {
|
|
268
|
+
return this.errorResult(`Failed to start instance: ${err.message}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
async handleStopInstance(args) {
|
|
272
|
+
const name = args.name;
|
|
273
|
+
if (!name) {
|
|
274
|
+
return this.errorResult('Missing required parameter: name');
|
|
275
|
+
}
|
|
276
|
+
try {
|
|
277
|
+
await serviceManager_1.serviceManager.stop(name);
|
|
278
|
+
return this.textResult(`Instance '${name}' stopped.`);
|
|
279
|
+
}
|
|
280
|
+
catch (err) {
|
|
281
|
+
return this.errorResult(`Failed to stop instance: ${err.message}`);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
// -------------------------------------------------------------------------
|
|
285
|
+
// manage_* tool proxy — routes to the target instance's backend
|
|
286
|
+
// -------------------------------------------------------------------------
|
|
287
|
+
async handleManageTool(toolName, args) {
|
|
288
|
+
const instanceName = args.instance_name;
|
|
289
|
+
if (!instanceName) {
|
|
290
|
+
return this.errorResult(`Missing required parameter: instance_name. Use the list_instances tool to see available instances.`);
|
|
291
|
+
}
|
|
292
|
+
const service = stateManager_1.stateManager.getService(instanceName);
|
|
293
|
+
if (!service) {
|
|
294
|
+
return this.errorResult(`Instance '${instanceName}' not found. Use the list_instances tool to see available instances.`);
|
|
295
|
+
}
|
|
296
|
+
if (service.status !== 'running') {
|
|
297
|
+
return this.errorResult(`Instance '${instanceName}' is not running (status: ${service.status}). Use the start_instance tool to start it.`);
|
|
298
|
+
}
|
|
299
|
+
// Strip instance_name — the backend doesn't know about it.
|
|
300
|
+
const { instance_name: _unused, ...toolArgs } = args;
|
|
301
|
+
try {
|
|
302
|
+
const resp = await fetch(`http://localhost:${service.port}/mcp-internal/execute-tool`, {
|
|
303
|
+
method: 'POST',
|
|
304
|
+
headers: { 'Content-Type': 'application/json' },
|
|
305
|
+
body: JSON.stringify({
|
|
306
|
+
tool_name: toolName,
|
|
307
|
+
arguments: toolArgs,
|
|
308
|
+
}),
|
|
309
|
+
});
|
|
310
|
+
if (!resp.ok) {
|
|
311
|
+
return this.errorResult(`Instance '${instanceName}' returned HTTP ${resp.status} for tool '${toolName}'.`);
|
|
312
|
+
}
|
|
313
|
+
const result = (await resp.json());
|
|
314
|
+
return this.toToolResult(result);
|
|
315
|
+
}
|
|
316
|
+
catch (err) {
|
|
317
|
+
return this.errorResult(`Failed to reach instance '${instanceName}': ${err.message}`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
// -------------------------------------------------------------------------
|
|
321
|
+
// Gateway info (for the management UI)
|
|
322
|
+
// -------------------------------------------------------------------------
|
|
323
|
+
/**
|
|
324
|
+
* Returns a descriptor of the gateway for the management UI: the gateway
|
|
325
|
+
* URL, available instances, tool list, and a ready-to-paste client config.
|
|
326
|
+
*/
|
|
327
|
+
async getGatewayInfo() {
|
|
328
|
+
await this.refreshToolCache();
|
|
329
|
+
const daemonTools = this.buildDaemonTools();
|
|
330
|
+
const manageTools = this.toolCache ?? [];
|
|
331
|
+
const instances = stateManager_1.stateManager.getServices().map((s) => ({
|
|
332
|
+
name: s.name,
|
|
333
|
+
path: s.path,
|
|
334
|
+
port: s.port,
|
|
335
|
+
status: s.status,
|
|
336
|
+
}));
|
|
337
|
+
return {
|
|
338
|
+
server_name: 'repoburg-gateway',
|
|
339
|
+
description: 'Single MCP entry point managing all Repoburg instances. Each manage_* tool requires an instance_name parameter to route to the correct instance.',
|
|
340
|
+
url: `http://localhost:9998/mcp`,
|
|
341
|
+
instances,
|
|
342
|
+
tools: [...daemonTools, ...manageTools],
|
|
343
|
+
client_config: JSON.stringify({ repoburg: { url: 'http://localhost:9998/mcp' } }, null, 2),
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
// -------------------------------------------------------------------------
|
|
347
|
+
// Session cleanup
|
|
348
|
+
// -------------------------------------------------------------------------
|
|
349
|
+
async closeAllSessions() {
|
|
350
|
+
await Promise.all(Array.from(this.sessions.values()).map((t) => t.close()));
|
|
351
|
+
this.sessions.clear();
|
|
352
|
+
}
|
|
353
|
+
// -------------------------------------------------------------------------
|
|
354
|
+
// Helpers
|
|
355
|
+
// -------------------------------------------------------------------------
|
|
356
|
+
findRunningInstance() {
|
|
357
|
+
return stateManager_1.stateManager
|
|
358
|
+
.getServices()
|
|
359
|
+
.find((s) => s.status === 'running');
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Maps an `ActionExecutionResult` (returned by the backend's
|
|
363
|
+
* `/mcp-internal/execute-tool`) to an MCP `CallToolResult`.
|
|
364
|
+
*/
|
|
365
|
+
toToolResult(result) {
|
|
366
|
+
const output = result.execution_log?.output || '';
|
|
367
|
+
const error = result.execution_log?.error_message || result.error_message;
|
|
368
|
+
const text = error ? `${output}\n\nError: ${error}`.trim() : output;
|
|
369
|
+
return {
|
|
370
|
+
content: [{ type: 'text', text }],
|
|
371
|
+
isError: result.status === 'FAILURE' ? true : undefined,
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
textResult(text) {
|
|
375
|
+
return { content: [{ type: 'text', text }] };
|
|
376
|
+
}
|
|
377
|
+
errorResult(message) {
|
|
378
|
+
return { content: [{ type: 'text', text: message }], isError: true };
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
exports.mcpGateway = new McpGateway();
|
package/package.json
CHANGED
package/backend/.env
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
#react project
|
|
2
|
-
#REPOBURG_PROJECT_PATH='/Users/celalertug/WebstormProjects/ttt1234'
|
|
3
|
-
#REPOBURG_PROJECT_PATH=/Users/celalertug/Desktop/tmp/repoburg-preview-boilerplate-main
|
|
4
|
-
|
|
5
|
-
#modern football
|
|
6
|
-
#REPOBURG_PROJECT_PATH=/Users/celalertug/WebstormProjects/fm1
|
|
7
|
-
|
|
8
|
-
# handbag
|
|
9
|
-
# REPOBURG_PROJECT_PATH=/Users/celalertug/tmp/handbag-main
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
# webcoder
|
|
13
|
-
# REPOBURG_PROJECT_PATH=/Users/celalertug/github/webcoder
|
|
14
|
-
|
|
15
|
-
REPOBURG_PROJECT_PATH=/Users/celalertug/tmp/randomproject
|
|
16
|
-
# REPOBURG_PROJECT_PATH=/Users/celalertug/repoburg-projects/rp42
|
|
17
|
-
# REPOBURG_PROJECT_PATH=/Users/celalertug/repoburg-projects/simple-erp
|
|
18
|
-
|
|
19
|
-
# appdev
|
|
20
|
-
# REPOBURG_PROJECT_PATH=/Users/celalertug/everest/tmpappdev2
|
|
21
|
-
# REPOBURG_PROJECT_PATH=/Users/celalertug/everest/tmpappdev2-session2
|
|
22
|
-
|
|
23
|
-
# REPOBURG_PROJECT_PATH=/Users/celalertug/repoburg-projects/isgorusmetakip
|
|
24
|
-
# REPOBURG_PROJECT_PATH=/Users/celalertug/repoburg-projects/rp36 # data
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
QUICK_EDIT_LOG_DIR=/Users/celalertug/.repoburg/quick-edit-logs
|
package/backend/database.sqlite
DELETED
|
File without changes
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
current
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const express_1 = require("express");
|
|
4
|
-
const child_process_1 = require("child_process");
|
|
5
|
-
const checkAuth_1 = require("../middleware/checkAuth");
|
|
6
|
-
const router = (0, express_1.Router)();
|
|
7
|
-
router.post('/update', checkAuth_1.checkAuth, (req, res) => {
|
|
8
|
-
try {
|
|
9
|
-
console.log('[DAEMON] Received request to start update process.');
|
|
10
|
-
// Using `spawn` with `detached: true` and `stdio: 'ignore'` allows the parent (daemon)
|
|
11
|
-
// to exit while the child (update script) continues running. `unref()` is crucial.
|
|
12
|
-
const child = (0, child_process_1.spawn)('repoburg', ['update'], {
|
|
13
|
-
detached: true,
|
|
14
|
-
stdio: 'ignore',
|
|
15
|
-
shell: true,
|
|
16
|
-
});
|
|
17
|
-
child.unref();
|
|
18
|
-
res.status(202).json({ message: 'Update process initiated. The daemon and services will restart shortly.' });
|
|
19
|
-
}
|
|
20
|
-
catch (error) {
|
|
21
|
-
console.error('Failed to spawn update process:', error);
|
|
22
|
-
res.status(500).json({ error: 'Failed to start update process.' });
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
exports.default = router;
|
package/daemon/dist/daemon.blob
DELETED
|
Binary file
|
|
Binary file
|