@kelceyp/caw-server 0.0.5 → 0.0.9
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/main.js +758 -130
- package/dist/main.js.map +12 -10
- package/dist/public_html/index.html +4 -144
- package/dist/public_html/main.js +177 -0
- package/dist/public_html/styles.css +2 -0
- package/package.json +3 -2
package/dist/main.js
CHANGED
|
@@ -44596,9 +44596,18 @@ var import_cors = __toESM(require_lib3(), 1);
|
|
|
44596
44596
|
import { createServer } from "http";
|
|
44597
44597
|
|
|
44598
44598
|
// src/common/Auth.js
|
|
44599
|
-
var
|
|
44600
|
-
|
|
44601
|
-
|
|
44599
|
+
var getProjectByToken = async (store, token) => {
|
|
44600
|
+
return store.getProjectByToken(token);
|
|
44601
|
+
};
|
|
44602
|
+
var validateToken = async (store, token) => {
|
|
44603
|
+
const project = await getProjectByToken(store, token);
|
|
44604
|
+
if (!project) {
|
|
44605
|
+
throw new Error("Invalid token");
|
|
44606
|
+
}
|
|
44607
|
+
return project.id;
|
|
44608
|
+
};
|
|
44609
|
+
var create = ({ store } = {}) => {
|
|
44610
|
+
const middleware = async (req, res, next) => {
|
|
44602
44611
|
const authHeader = req.headers.authorization;
|
|
44603
44612
|
if (!authHeader) {
|
|
44604
44613
|
return res.status(401).json({
|
|
@@ -44606,33 +44615,123 @@ var create = () => {
|
|
|
44606
44615
|
message: "Missing Authorization header"
|
|
44607
44616
|
});
|
|
44608
44617
|
}
|
|
44609
|
-
const token = authHeader.replace(/^Bearer\s
|
|
44610
|
-
if (
|
|
44618
|
+
const token = authHeader.replace(/^Bearer\s+/i, "");
|
|
44619
|
+
if (!store) {
|
|
44620
|
+
return res.status(500).json({
|
|
44621
|
+
error: "Internal server error",
|
|
44622
|
+
message: "Auth store not configured"
|
|
44623
|
+
});
|
|
44624
|
+
}
|
|
44625
|
+
try {
|
|
44626
|
+
const projectId = await validateToken(store, token);
|
|
44627
|
+
req.projectId = projectId;
|
|
44628
|
+
req.token = token;
|
|
44629
|
+
next();
|
|
44630
|
+
} catch (error) {
|
|
44611
44631
|
return res.status(401).json({
|
|
44612
44632
|
error: "Unauthorized",
|
|
44613
44633
|
message: "Invalid token"
|
|
44614
44634
|
});
|
|
44615
44635
|
}
|
|
44616
|
-
next();
|
|
44617
44636
|
};
|
|
44618
44637
|
return Object.freeze({
|
|
44619
44638
|
middleware
|
|
44620
44639
|
});
|
|
44621
44640
|
};
|
|
44622
|
-
var Auth_default = { create };
|
|
44641
|
+
var Auth_default = { create, getProjectByToken, validateToken };
|
|
44623
44642
|
|
|
44624
44643
|
// src/api/ApiRoutes.js
|
|
44625
|
-
var create2 = (app) => {
|
|
44626
|
-
const auth = Auth_default.create();
|
|
44644
|
+
var create2 = (app, { store } = {}) => {
|
|
44645
|
+
const auth = Auth_default.create({ store });
|
|
44627
44646
|
app.get("/api/hello", auth.middleware, (req, res) => {
|
|
44628
44647
|
res.json({
|
|
44629
|
-
message: "Hello from CAW"
|
|
44648
|
+
message: "Hello from CAW",
|
|
44649
|
+
projectId: req.projectId
|
|
44630
44650
|
});
|
|
44631
44651
|
});
|
|
44632
44652
|
return Object.freeze({});
|
|
44633
44653
|
};
|
|
44634
44654
|
var ApiRoutes_default = { create: create2 };
|
|
44635
44655
|
|
|
44656
|
+
// src/api/ProjectRoutes.js
|
|
44657
|
+
var create3 = (app, { store }) => {
|
|
44658
|
+
const auth = Auth_default.create({ store });
|
|
44659
|
+
app.get("/api/projects", async (req, res) => {
|
|
44660
|
+
try {
|
|
44661
|
+
const projects = await store.getProjects();
|
|
44662
|
+
res.json(projects);
|
|
44663
|
+
} catch (error) {
|
|
44664
|
+
console.error("[ProjectRoutes] Error listing projects:", error);
|
|
44665
|
+
res.status(500).json({
|
|
44666
|
+
error: "Internal server error",
|
|
44667
|
+
message: "Failed to list projects"
|
|
44668
|
+
});
|
|
44669
|
+
}
|
|
44670
|
+
});
|
|
44671
|
+
app.post("/api/projects", async (req, res) => {
|
|
44672
|
+
const { name, path } = req.body;
|
|
44673
|
+
if (!name || typeof name !== "string") {
|
|
44674
|
+
return res.status(400).json({
|
|
44675
|
+
error: "Bad request",
|
|
44676
|
+
message: "name is required and must be a string"
|
|
44677
|
+
});
|
|
44678
|
+
}
|
|
44679
|
+
if (!path || typeof path !== "string") {
|
|
44680
|
+
return res.status(400).json({
|
|
44681
|
+
error: "Bad request",
|
|
44682
|
+
message: "path is required and must be a string"
|
|
44683
|
+
});
|
|
44684
|
+
}
|
|
44685
|
+
try {
|
|
44686
|
+
const project = await store.createProject({ name, path });
|
|
44687
|
+
res.status(201).json(project);
|
|
44688
|
+
} catch (error) {
|
|
44689
|
+
console.error("[ProjectRoutes] Error creating project:", error);
|
|
44690
|
+
res.status(500).json({
|
|
44691
|
+
error: "Internal server error",
|
|
44692
|
+
message: "Failed to create project"
|
|
44693
|
+
});
|
|
44694
|
+
}
|
|
44695
|
+
});
|
|
44696
|
+
app.get("/api/projects/:projectId", auth.middleware, async (req, res) => {
|
|
44697
|
+
const projectId = parseInt(req.params.projectId, 10);
|
|
44698
|
+
if (isNaN(projectId)) {
|
|
44699
|
+
return res.status(400).json({
|
|
44700
|
+
error: "Bad request",
|
|
44701
|
+
message: "projectId must be an integer"
|
|
44702
|
+
});
|
|
44703
|
+
}
|
|
44704
|
+
if (req.projectId !== projectId) {
|
|
44705
|
+
return res.status(403).json({
|
|
44706
|
+
error: "Forbidden",
|
|
44707
|
+
message: "Token does not match requested project"
|
|
44708
|
+
});
|
|
44709
|
+
}
|
|
44710
|
+
try {
|
|
44711
|
+
const project = await store.getProject(projectId);
|
|
44712
|
+
if (!project) {
|
|
44713
|
+
return res.status(404).json({
|
|
44714
|
+
error: "Not found",
|
|
44715
|
+
message: "Project not found"
|
|
44716
|
+
});
|
|
44717
|
+
}
|
|
44718
|
+
const { token, ...projectWithoutToken } = project;
|
|
44719
|
+
res.json(projectWithoutToken);
|
|
44720
|
+
} catch (error) {
|
|
44721
|
+
console.error("[ProjectRoutes] Error getting project:", error);
|
|
44722
|
+
res.status(500).json({
|
|
44723
|
+
error: "Internal server error",
|
|
44724
|
+
message: "Failed to get project"
|
|
44725
|
+
});
|
|
44726
|
+
}
|
|
44727
|
+
});
|
|
44728
|
+
return Object.freeze({});
|
|
44729
|
+
};
|
|
44730
|
+
var ProjectRoutes_default = { create: create3 };
|
|
44731
|
+
|
|
44732
|
+
// src/mcp/McpRoutes.js
|
|
44733
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
44734
|
+
|
|
44636
44735
|
// node_modules/@modelcontextprotocol/sdk/node_modules/zod/v3/external.js
|
|
44637
44736
|
var exports_external = {};
|
|
44638
44737
|
__export(exports_external, {
|
|
@@ -56167,11 +56266,33 @@ var EMPTY_COMPLETION_RESULT = {
|
|
|
56167
56266
|
}
|
|
56168
56267
|
};
|
|
56169
56268
|
// src/mcp/McpServer.js
|
|
56170
|
-
var createMcpServer = ({ sessionManager } = {}) => {
|
|
56269
|
+
var createMcpServer = ({ sessionManager, projectId, token, getMcpSessionId, mcpSessions } = {}) => {
|
|
56171
56270
|
const server = new McpServer({
|
|
56172
56271
|
name: "caw-server",
|
|
56173
56272
|
version: "0.0.1"
|
|
56174
56273
|
});
|
|
56274
|
+
const context = {
|
|
56275
|
+
projectId,
|
|
56276
|
+
token,
|
|
56277
|
+
getMcpSessionId,
|
|
56278
|
+
mcpSessions
|
|
56279
|
+
};
|
|
56280
|
+
const getCurrentMcpSession = () => {
|
|
56281
|
+
const mcpSessionId = context.getMcpSessionId();
|
|
56282
|
+
if (!mcpSessionId || !context.mcpSessions)
|
|
56283
|
+
return null;
|
|
56284
|
+
return context.mcpSessions.get(mcpSessionId);
|
|
56285
|
+
};
|
|
56286
|
+
const verifyClaimedError = (pageId) => {
|
|
56287
|
+
const mcpSessionId = context.getMcpSessionId();
|
|
56288
|
+
if (!sessionManager || !mcpSessionId) {
|
|
56289
|
+
return { error: true, message: "Session context not available" };
|
|
56290
|
+
}
|
|
56291
|
+
if (!sessionManager.isClaimedBy(pageId, mcpSessionId)) {
|
|
56292
|
+
return { error: true, message: "MUST_CLAIM_FIRST: You must claim this session before sending commands" };
|
|
56293
|
+
}
|
|
56294
|
+
return { error: false };
|
|
56295
|
+
};
|
|
56175
56296
|
server.registerTool("list_sessions", {
|
|
56176
56297
|
title: "List Active Sessions",
|
|
56177
56298
|
description: "List all active WebSocket sessions with their pageIds and states",
|
|
@@ -56179,36 +56300,128 @@ var createMcpServer = ({ sessionManager } = {}) => {
|
|
|
56179
56300
|
}, async () => {
|
|
56180
56301
|
if (!sessionManager) {
|
|
56181
56302
|
return {
|
|
56182
|
-
content: [
|
|
56183
|
-
{
|
|
56184
|
-
type: "text",
|
|
56185
|
-
text: "Error: SessionManager not initialized"
|
|
56186
|
-
}
|
|
56187
|
-
],
|
|
56303
|
+
content: [{ type: "text", text: "Error: SessionManager not initialized" }],
|
|
56188
56304
|
isError: true
|
|
56189
56305
|
};
|
|
56190
56306
|
}
|
|
56191
56307
|
const sessions = sessionManager.getAllSessions();
|
|
56192
56308
|
if (sessions.length === 0) {
|
|
56193
56309
|
return {
|
|
56194
|
-
content: [
|
|
56195
|
-
{
|
|
56196
|
-
type: "text",
|
|
56197
|
-
text: "No active sessions"
|
|
56198
|
-
}
|
|
56199
|
-
]
|
|
56310
|
+
content: [{ type: "text", text: "No active sessions" }]
|
|
56200
56311
|
};
|
|
56201
56312
|
}
|
|
56202
|
-
const
|
|
56313
|
+
const mcpSessionId = context.getMcpSessionId();
|
|
56314
|
+
const sessionList = sessions.map((s) => {
|
|
56315
|
+
const claimStatus = s.claimedBy === null ? "unclaimed" : s.claimedBy === mcpSessionId ? "claimed by you" : "claimed by another";
|
|
56316
|
+
return `• ${s.pageId} (${s.state}, ${claimStatus})`;
|
|
56317
|
+
}).join(`
|
|
56203
56318
|
`);
|
|
56204
56319
|
return {
|
|
56205
|
-
content: [
|
|
56206
|
-
|
|
56207
|
-
|
|
56208
|
-
|
|
56209
|
-
|
|
56210
|
-
|
|
56211
|
-
|
|
56320
|
+
content: [{ type: "text", text: `Active sessions (${sessions.length}):
|
|
56321
|
+
${sessionList}` }]
|
|
56322
|
+
};
|
|
56323
|
+
});
|
|
56324
|
+
server.registerTool("claim_session", {
|
|
56325
|
+
title: "Claim Session",
|
|
56326
|
+
description: "Claim an unclaimed WebSocket session so you can send commands to it",
|
|
56327
|
+
inputSchema: exports_external2.object({
|
|
56328
|
+
pageId: exports_external2.string().describe("Page ID of the tab to claim")
|
|
56329
|
+
})
|
|
56330
|
+
}, async ({ pageId }) => {
|
|
56331
|
+
if (!sessionManager) {
|
|
56332
|
+
return {
|
|
56333
|
+
content: [{ type: "text", text: "Error: SessionManager not initialized" }],
|
|
56334
|
+
isError: true
|
|
56335
|
+
};
|
|
56336
|
+
}
|
|
56337
|
+
const mcpSessionId = context.getMcpSessionId();
|
|
56338
|
+
if (!mcpSessionId) {
|
|
56339
|
+
return {
|
|
56340
|
+
content: [{ type: "text", text: "Error: MCP session not initialized" }],
|
|
56341
|
+
isError: true
|
|
56342
|
+
};
|
|
56343
|
+
}
|
|
56344
|
+
const result = sessionManager.claimSession(pageId, mcpSessionId);
|
|
56345
|
+
if (!result.success) {
|
|
56346
|
+
return {
|
|
56347
|
+
content: [{ type: "text", text: `Error: ${result.error}` }],
|
|
56348
|
+
isError: true
|
|
56349
|
+
};
|
|
56350
|
+
}
|
|
56351
|
+
const mcpSession = getCurrentMcpSession();
|
|
56352
|
+
if (mcpSession) {
|
|
56353
|
+
mcpSession.claimedConnections.add(pageId);
|
|
56354
|
+
}
|
|
56355
|
+
return {
|
|
56356
|
+
content: [{ type: "text", text: `Successfully claimed session: ${pageId}` }]
|
|
56357
|
+
};
|
|
56358
|
+
});
|
|
56359
|
+
server.registerTool("release_session", {
|
|
56360
|
+
title: "Release Session",
|
|
56361
|
+
description: "Release a claimed WebSocket session so other MCP sessions can claim it",
|
|
56362
|
+
inputSchema: exports_external2.object({
|
|
56363
|
+
pageId: exports_external2.string().describe("Page ID of the tab to release")
|
|
56364
|
+
})
|
|
56365
|
+
}, async ({ pageId }) => {
|
|
56366
|
+
if (!sessionManager) {
|
|
56367
|
+
return {
|
|
56368
|
+
content: [{ type: "text", text: "Error: SessionManager not initialized" }],
|
|
56369
|
+
isError: true
|
|
56370
|
+
};
|
|
56371
|
+
}
|
|
56372
|
+
const mcpSessionId = context.getMcpSessionId();
|
|
56373
|
+
if (!mcpSessionId) {
|
|
56374
|
+
return {
|
|
56375
|
+
content: [{ type: "text", text: "Error: MCP session not initialized" }],
|
|
56376
|
+
isError: true
|
|
56377
|
+
};
|
|
56378
|
+
}
|
|
56379
|
+
const result = sessionManager.releaseSession(pageId, mcpSessionId);
|
|
56380
|
+
if (!result.success) {
|
|
56381
|
+
return {
|
|
56382
|
+
content: [{ type: "text", text: `Error: ${result.error}` }],
|
|
56383
|
+
isError: true
|
|
56384
|
+
};
|
|
56385
|
+
}
|
|
56386
|
+
const mcpSession = getCurrentMcpSession();
|
|
56387
|
+
if (mcpSession) {
|
|
56388
|
+
mcpSession.claimedConnections.delete(pageId);
|
|
56389
|
+
}
|
|
56390
|
+
return {
|
|
56391
|
+
content: [{ type: "text", text: `Successfully released session: ${pageId}` }]
|
|
56392
|
+
};
|
|
56393
|
+
});
|
|
56394
|
+
server.registerTool("list_my_sessions", {
|
|
56395
|
+
title: "List My Sessions",
|
|
56396
|
+
description: "List only the WebSocket sessions claimed by this MCP session",
|
|
56397
|
+
inputSchema: exports_external2.object({})
|
|
56398
|
+
}, async () => {
|
|
56399
|
+
if (!sessionManager) {
|
|
56400
|
+
return {
|
|
56401
|
+
content: [{ type: "text", text: "Error: SessionManager not initialized" }],
|
|
56402
|
+
isError: true
|
|
56403
|
+
};
|
|
56404
|
+
}
|
|
56405
|
+
const mcpSession = getCurrentMcpSession();
|
|
56406
|
+
if (!mcpSession) {
|
|
56407
|
+
return {
|
|
56408
|
+
content: [{ type: "text", text: "Error: MCP session not found" }],
|
|
56409
|
+
isError: true
|
|
56410
|
+
};
|
|
56411
|
+
}
|
|
56412
|
+
const claimedPageIds = Array.from(mcpSession.claimedConnections);
|
|
56413
|
+
if (claimedPageIds.length === 0) {
|
|
56414
|
+
return {
|
|
56415
|
+
content: [{ type: "text", text: "No claimed sessions" }]
|
|
56416
|
+
};
|
|
56417
|
+
}
|
|
56418
|
+
const allSessions = sessionManager.getAllSessions();
|
|
56419
|
+
const mySessions = allSessions.filter((s) => claimedPageIds.includes(s.pageId));
|
|
56420
|
+
const sessionList = mySessions.map((s) => `• ${s.pageId} (${s.state})`).join(`
|
|
56421
|
+
`);
|
|
56422
|
+
return {
|
|
56423
|
+
content: [{ type: "text", text: `Your claimed sessions (${mySessions.length}):
|
|
56424
|
+
${sessionList}` }]
|
|
56212
56425
|
};
|
|
56213
56426
|
});
|
|
56214
56427
|
server.registerTool("get_state", {
|
|
@@ -56220,33 +56433,25 @@ ${sessionList}`
|
|
|
56220
56433
|
}, async ({ pageId }) => {
|
|
56221
56434
|
if (!sessionManager) {
|
|
56222
56435
|
return {
|
|
56223
|
-
content: [
|
|
56224
|
-
|
|
56225
|
-
|
|
56226
|
-
|
|
56227
|
-
|
|
56228
|
-
|
|
56436
|
+
content: [{ type: "text", text: "Error: SessionManager not initialized" }],
|
|
56437
|
+
isError: true
|
|
56438
|
+
};
|
|
56439
|
+
}
|
|
56440
|
+
const claimCheck = verifyClaimedError(pageId);
|
|
56441
|
+
if (claimCheck.error) {
|
|
56442
|
+
return {
|
|
56443
|
+
content: [{ type: "text", text: `Error: ${claimCheck.message}` }],
|
|
56229
56444
|
isError: true
|
|
56230
56445
|
};
|
|
56231
56446
|
}
|
|
56232
56447
|
try {
|
|
56233
56448
|
const result = await sessionManager.sendCommand(pageId, "getState", {});
|
|
56234
56449
|
return {
|
|
56235
|
-
content: [
|
|
56236
|
-
{
|
|
56237
|
-
type: "text",
|
|
56238
|
-
text: `ChatGPT state: ${result.state}`
|
|
56239
|
-
}
|
|
56240
|
-
]
|
|
56450
|
+
content: [{ type: "text", text: `ChatGPT state: ${result.state}` }]
|
|
56241
56451
|
};
|
|
56242
56452
|
} catch (error) {
|
|
56243
56453
|
return {
|
|
56244
|
-
content: [
|
|
56245
|
-
{
|
|
56246
|
-
type: "text",
|
|
56247
|
-
text: `Error: ${error.message}`
|
|
56248
|
-
}
|
|
56249
|
-
],
|
|
56454
|
+
content: [{ type: "text", text: `Error: ${error.message}` }],
|
|
56250
56455
|
isError: true
|
|
56251
56456
|
};
|
|
56252
56457
|
}
|
|
@@ -56262,35 +56467,27 @@ ${sessionList}`
|
|
|
56262
56467
|
}, async ({ pageId, text, timeout }) => {
|
|
56263
56468
|
if (!sessionManager) {
|
|
56264
56469
|
return {
|
|
56265
|
-
content: [
|
|
56266
|
-
|
|
56267
|
-
|
|
56268
|
-
|
|
56269
|
-
|
|
56270
|
-
|
|
56470
|
+
content: [{ type: "text", text: "Error: SessionManager not initialized" }],
|
|
56471
|
+
isError: true
|
|
56472
|
+
};
|
|
56473
|
+
}
|
|
56474
|
+
const claimCheck = verifyClaimedError(pageId);
|
|
56475
|
+
if (claimCheck.error) {
|
|
56476
|
+
return {
|
|
56477
|
+
content: [{ type: "text", text: `Error: ${claimCheck.message}` }],
|
|
56271
56478
|
isError: true
|
|
56272
56479
|
};
|
|
56273
56480
|
}
|
|
56274
56481
|
try {
|
|
56275
56482
|
const result = await sessionManager.sendCommand(pageId, "sendMessage", { text, timeout }, timeout || 120000);
|
|
56276
56483
|
return {
|
|
56277
|
-
content: [
|
|
56278
|
-
{
|
|
56279
|
-
type: "text",
|
|
56280
|
-
text: `ChatGPT response (turn ${result.turn}, ${result.duration}ms):
|
|
56484
|
+
content: [{ type: "text", text: `ChatGPT response (turn ${result.turn}, ${result.duration}ms):
|
|
56281
56485
|
|
|
56282
|
-
${result.text}`
|
|
56283
|
-
}
|
|
56284
|
-
]
|
|
56486
|
+
${result.text}` }]
|
|
56285
56487
|
};
|
|
56286
56488
|
} catch (error) {
|
|
56287
56489
|
return {
|
|
56288
|
-
content: [
|
|
56289
|
-
{
|
|
56290
|
-
type: "text",
|
|
56291
|
-
text: `Error: ${error.message}`
|
|
56292
|
-
}
|
|
56293
|
-
],
|
|
56490
|
+
content: [{ type: "text", text: `Error: ${error.message}` }],
|
|
56294
56491
|
isError: true
|
|
56295
56492
|
};
|
|
56296
56493
|
}
|
|
@@ -56304,12 +56501,14 @@ ${result.text}`
|
|
|
56304
56501
|
}, async ({ pageId }) => {
|
|
56305
56502
|
if (!sessionManager) {
|
|
56306
56503
|
return {
|
|
56307
|
-
content: [
|
|
56308
|
-
|
|
56309
|
-
|
|
56310
|
-
|
|
56311
|
-
|
|
56312
|
-
|
|
56504
|
+
content: [{ type: "text", text: "Error: SessionManager not initialized" }],
|
|
56505
|
+
isError: true
|
|
56506
|
+
};
|
|
56507
|
+
}
|
|
56508
|
+
const claimCheck = verifyClaimedError(pageId);
|
|
56509
|
+
if (claimCheck.error) {
|
|
56510
|
+
return {
|
|
56511
|
+
content: [{ type: "text", text: `Error: ${claimCheck.message}` }],
|
|
56313
56512
|
isError: true
|
|
56314
56513
|
};
|
|
56315
56514
|
}
|
|
@@ -56317,31 +56516,205 @@ ${result.text}`
|
|
|
56317
56516
|
const result = await sessionManager.sendCommand(pageId, "getLastMessage", {});
|
|
56318
56517
|
if (result.text) {
|
|
56319
56518
|
return {
|
|
56320
|
-
content: [
|
|
56321
|
-
{
|
|
56322
|
-
type: "text",
|
|
56323
|
-
text: result.text
|
|
56324
|
-
}
|
|
56325
|
-
]
|
|
56519
|
+
content: [{ type: "text", text: result.text }]
|
|
56326
56520
|
};
|
|
56327
56521
|
} else {
|
|
56328
56522
|
return {
|
|
56329
|
-
content: [
|
|
56330
|
-
{
|
|
56331
|
-
type: "text",
|
|
56332
|
-
text: "No message found"
|
|
56333
|
-
}
|
|
56334
|
-
]
|
|
56523
|
+
content: [{ type: "text", text: "No message found" }]
|
|
56335
56524
|
};
|
|
56336
56525
|
}
|
|
56337
56526
|
} catch (error) {
|
|
56338
56527
|
return {
|
|
56339
|
-
content: [
|
|
56340
|
-
|
|
56341
|
-
|
|
56342
|
-
|
|
56343
|
-
|
|
56344
|
-
|
|
56528
|
+
content: [{ type: "text", text: `Error: ${error.message}` }],
|
|
56529
|
+
isError: true
|
|
56530
|
+
};
|
|
56531
|
+
}
|
|
56532
|
+
});
|
|
56533
|
+
server.registerTool("get_message_count", {
|
|
56534
|
+
title: "Get Message Count",
|
|
56535
|
+
description: "Get the total number of turns (message pairs) in the conversation",
|
|
56536
|
+
inputSchema: exports_external2.object({
|
|
56537
|
+
pageId: exports_external2.string().describe("Page ID of the ChatGPT tab")
|
|
56538
|
+
})
|
|
56539
|
+
}, async ({ pageId }) => {
|
|
56540
|
+
if (!sessionManager) {
|
|
56541
|
+
return {
|
|
56542
|
+
content: [{ type: "text", text: "Error: SessionManager not initialized" }],
|
|
56543
|
+
isError: true
|
|
56544
|
+
};
|
|
56545
|
+
}
|
|
56546
|
+
const claimCheck = verifyClaimedError(pageId);
|
|
56547
|
+
if (claimCheck.error) {
|
|
56548
|
+
return {
|
|
56549
|
+
content: [{ type: "text", text: `Error: ${claimCheck.message}` }],
|
|
56550
|
+
isError: true
|
|
56551
|
+
};
|
|
56552
|
+
}
|
|
56553
|
+
try {
|
|
56554
|
+
const result = await sessionManager.sendCommand(pageId, "getMessageCount", {});
|
|
56555
|
+
return {
|
|
56556
|
+
content: [{ type: "text", text: `Message count: ${result.count} turns` }]
|
|
56557
|
+
};
|
|
56558
|
+
} catch (error) {
|
|
56559
|
+
return {
|
|
56560
|
+
content: [{ type: "text", text: `Error: ${error.message}` }],
|
|
56561
|
+
isError: true
|
|
56562
|
+
};
|
|
56563
|
+
}
|
|
56564
|
+
});
|
|
56565
|
+
server.registerTool("get_message", {
|
|
56566
|
+
title: "Get Message",
|
|
56567
|
+
description: 'Get a specific message by turn number and role, or by address notation (e.g., "5u", "3a", "7u.5-10")',
|
|
56568
|
+
inputSchema: exports_external2.object({
|
|
56569
|
+
pageId: exports_external2.string().describe("Page ID of the ChatGPT tab"),
|
|
56570
|
+
turn: exports_external2.number().optional().describe("Turn number (1-based)"),
|
|
56571
|
+
role: exports_external2.enum(["user", "assistant"]).optional().describe("Message role"),
|
|
56572
|
+
address: exports_external2.string().optional().describe('Address notation (e.g., "5u", "3a", "7u.5-10")')
|
|
56573
|
+
})
|
|
56574
|
+
}, async ({ pageId, turn, role, address }) => {
|
|
56575
|
+
if (!sessionManager) {
|
|
56576
|
+
return {
|
|
56577
|
+
content: [{ type: "text", text: "Error: SessionManager not initialized" }],
|
|
56578
|
+
isError: true
|
|
56579
|
+
};
|
|
56580
|
+
}
|
|
56581
|
+
const claimCheck = verifyClaimedError(pageId);
|
|
56582
|
+
if (claimCheck.error) {
|
|
56583
|
+
return {
|
|
56584
|
+
content: [{ type: "text", text: `Error: ${claimCheck.message}` }],
|
|
56585
|
+
isError: true
|
|
56586
|
+
};
|
|
56587
|
+
}
|
|
56588
|
+
if (!address && (turn === undefined || !role)) {
|
|
56589
|
+
return {
|
|
56590
|
+
content: [{ type: "text", text: "Error: Either address or (turn + role) is required" }],
|
|
56591
|
+
isError: true
|
|
56592
|
+
};
|
|
56593
|
+
}
|
|
56594
|
+
try {
|
|
56595
|
+
const result = await sessionManager.sendCommand(pageId, "getMessage", { turn, role, address });
|
|
56596
|
+
if (result.error) {
|
|
56597
|
+
return {
|
|
56598
|
+
content: [{ type: "text", text: `Error: ${result.error}` }],
|
|
56599
|
+
isError: true
|
|
56600
|
+
};
|
|
56601
|
+
}
|
|
56602
|
+
if (result.text) {
|
|
56603
|
+
return {
|
|
56604
|
+
content: [{ type: "text", text: result.text }]
|
|
56605
|
+
};
|
|
56606
|
+
} else {
|
|
56607
|
+
return {
|
|
56608
|
+
content: [{ type: "text", text: "Message not found" }]
|
|
56609
|
+
};
|
|
56610
|
+
}
|
|
56611
|
+
} catch (error) {
|
|
56612
|
+
return {
|
|
56613
|
+
content: [{ type: "text", text: `Error: ${error.message}` }],
|
|
56614
|
+
isError: true
|
|
56615
|
+
};
|
|
56616
|
+
}
|
|
56617
|
+
});
|
|
56618
|
+
server.registerTool("get_conversation", {
|
|
56619
|
+
title: "Get Conversation",
|
|
56620
|
+
description: "Get the full conversation history as a structured array of messages",
|
|
56621
|
+
inputSchema: exports_external2.object({
|
|
56622
|
+
pageId: exports_external2.string().describe("Page ID of the ChatGPT tab")
|
|
56623
|
+
})
|
|
56624
|
+
}, async ({ pageId }) => {
|
|
56625
|
+
if (!sessionManager) {
|
|
56626
|
+
return {
|
|
56627
|
+
content: [{ type: "text", text: "Error: SessionManager not initialized" }],
|
|
56628
|
+
isError: true
|
|
56629
|
+
};
|
|
56630
|
+
}
|
|
56631
|
+
const claimCheck = verifyClaimedError(pageId);
|
|
56632
|
+
if (claimCheck.error) {
|
|
56633
|
+
return {
|
|
56634
|
+
content: [{ type: "text", text: `Error: ${claimCheck.message}` }],
|
|
56635
|
+
isError: true
|
|
56636
|
+
};
|
|
56637
|
+
}
|
|
56638
|
+
try {
|
|
56639
|
+
const result = await sessionManager.sendCommand(pageId, "getConversation", {});
|
|
56640
|
+
const messages = result.messages || [];
|
|
56641
|
+
if (messages.length === 0) {
|
|
56642
|
+
return {
|
|
56643
|
+
content: [{ type: "text", text: "No messages in conversation" }]
|
|
56644
|
+
};
|
|
56645
|
+
}
|
|
56646
|
+
const formatted = messages.map((m) => {
|
|
56647
|
+
const roleChar = m.role === "user" ? "u" : "a";
|
|
56648
|
+
const addr = `${m.turn}${roleChar}`;
|
|
56649
|
+
const preview = m.text.length > 100 ? m.text.substring(0, 100) + "..." : m.text;
|
|
56650
|
+
return `[${addr}] ${m.role}: ${preview}`;
|
|
56651
|
+
}).join(`
|
|
56652
|
+
|
|
56653
|
+
`);
|
|
56654
|
+
return {
|
|
56655
|
+
content: [{ type: "text", text: `Conversation (${messages.length} messages):
|
|
56656
|
+
|
|
56657
|
+
${formatted}` }]
|
|
56658
|
+
};
|
|
56659
|
+
} catch (error) {
|
|
56660
|
+
return {
|
|
56661
|
+
content: [{ type: "text", text: `Error: ${error.message}` }],
|
|
56662
|
+
isError: true
|
|
56663
|
+
};
|
|
56664
|
+
}
|
|
56665
|
+
});
|
|
56666
|
+
server.registerTool("find_message", {
|
|
56667
|
+
title: "Find Message",
|
|
56668
|
+
description: "Search conversation messages using a regex pattern",
|
|
56669
|
+
inputSchema: exports_external2.object({
|
|
56670
|
+
pageId: exports_external2.string().describe("Page ID of the ChatGPT tab"),
|
|
56671
|
+
pattern: exports_external2.string().describe("Regex pattern to search for"),
|
|
56672
|
+
role: exports_external2.enum(["user", "assistant", "all"]).optional().describe("Filter by role (default: all)"),
|
|
56673
|
+
firstMatch: exports_external2.boolean().optional().describe("Stop after first match (default: false)"),
|
|
56674
|
+
caseInsensitive: exports_external2.boolean().optional().describe("Case insensitive search (default: false)")
|
|
56675
|
+
})
|
|
56676
|
+
}, async ({ pageId, pattern, role, firstMatch, caseInsensitive }) => {
|
|
56677
|
+
if (!sessionManager) {
|
|
56678
|
+
return {
|
|
56679
|
+
content: [{ type: "text", text: "Error: SessionManager not initialized" }],
|
|
56680
|
+
isError: true
|
|
56681
|
+
};
|
|
56682
|
+
}
|
|
56683
|
+
const claimCheck = verifyClaimedError(pageId);
|
|
56684
|
+
if (claimCheck.error) {
|
|
56685
|
+
return {
|
|
56686
|
+
content: [{ type: "text", text: `Error: ${claimCheck.message}` }],
|
|
56687
|
+
isError: true
|
|
56688
|
+
};
|
|
56689
|
+
}
|
|
56690
|
+
try {
|
|
56691
|
+
const result = await sessionManager.sendCommand(pageId, "findMessage", {
|
|
56692
|
+
pattern,
|
|
56693
|
+
role: role || "all",
|
|
56694
|
+
firstMatch: firstMatch || false,
|
|
56695
|
+
caseInsensitive: caseInsensitive || false
|
|
56696
|
+
});
|
|
56697
|
+
if (result.error) {
|
|
56698
|
+
return {
|
|
56699
|
+
content: [{ type: "text", text: `Error: ${result.error}` }],
|
|
56700
|
+
isError: true
|
|
56701
|
+
};
|
|
56702
|
+
}
|
|
56703
|
+
const matches = result.matches || [];
|
|
56704
|
+
if (matches.length === 0) {
|
|
56705
|
+
return {
|
|
56706
|
+
content: [{ type: "text", text: "No matches found" }]
|
|
56707
|
+
};
|
|
56708
|
+
}
|
|
56709
|
+
const formatted = matches.map((m) => `• ${m.address} (turn ${m.turn}, ${m.role})`).join(`
|
|
56710
|
+
`);
|
|
56711
|
+
return {
|
|
56712
|
+
content: [{ type: "text", text: `Found ${result.count} match(es):
|
|
56713
|
+
${formatted}` }]
|
|
56714
|
+
};
|
|
56715
|
+
} catch (error) {
|
|
56716
|
+
return {
|
|
56717
|
+
content: [{ type: "text", text: `Error: ${error.message}` }],
|
|
56345
56718
|
isError: true
|
|
56346
56719
|
};
|
|
56347
56720
|
}
|
|
@@ -56351,41 +56724,160 @@ ${result.text}`
|
|
|
56351
56724
|
var McpServer_default = createMcpServer;
|
|
56352
56725
|
|
|
56353
56726
|
// src/mcp/McpRoutes.js
|
|
56354
|
-
var
|
|
56355
|
-
|
|
56356
|
-
|
|
56727
|
+
var IDLE_TIMEOUT_MS = 60 * 60 * 1000;
|
|
56728
|
+
var CLEANUP_INTERVAL_MS = 5 * 60 * 1000;
|
|
56729
|
+
var create4 = (app, { sessionManager, store } = {}) => {
|
|
56730
|
+
const auth = Auth_default.create({ store });
|
|
56731
|
+
const mcpSessions = new Map;
|
|
56732
|
+
const cleanupInterval = setInterval(() => {
|
|
56733
|
+
const now = Date.now();
|
|
56734
|
+
for (const [sessionId, session] of mcpSessions) {
|
|
56735
|
+
if (now - session.lastActivity > IDLE_TIMEOUT_MS) {
|
|
56736
|
+
console.log(`Cleaning up idle MCP session: ${sessionId}`);
|
|
56737
|
+
if (sessionManager) {
|
|
56738
|
+
sessionManager.releaseAllForMcpSession(sessionId);
|
|
56739
|
+
}
|
|
56740
|
+
session.transport.close();
|
|
56741
|
+
mcpSessions.delete(sessionId);
|
|
56742
|
+
}
|
|
56743
|
+
}
|
|
56744
|
+
}, CLEANUP_INTERVAL_MS);
|
|
56745
|
+
cleanupInterval.unref();
|
|
56746
|
+
const getSessionIdFromHeaders = (req) => {
|
|
56747
|
+
return req.headers["mcp-session-id"] || req.headers["Mcp-Session-Id"];
|
|
56748
|
+
};
|
|
56357
56749
|
app.post("/api/mcp", auth.middleware, async (req, res) => {
|
|
56750
|
+
const existingSessionId = getSessionIdFromHeaders(req);
|
|
56751
|
+
const { projectId, token } = req;
|
|
56752
|
+
if (existingSessionId && mcpSessions.has(existingSessionId)) {
|
|
56753
|
+
const session = mcpSessions.get(existingSessionId);
|
|
56754
|
+
if (session.token !== token) {
|
|
56755
|
+
return res.status(401).json({
|
|
56756
|
+
jsonrpc: "2.0",
|
|
56757
|
+
error: { code: -32001, message: "Token mismatch for session" },
|
|
56758
|
+
id: null
|
|
56759
|
+
});
|
|
56760
|
+
}
|
|
56761
|
+
console.log(`Reusing MCP session: ${existingSessionId}`);
|
|
56762
|
+
session.lastActivity = Date.now();
|
|
56763
|
+
res.setHeader("Mcp-Session-Id", existingSessionId);
|
|
56764
|
+
try {
|
|
56765
|
+
await session.transport.handleRequest(req, res, req.body);
|
|
56766
|
+
} catch (error) {
|
|
56767
|
+
console.error("Error handling MCP request:", error);
|
|
56768
|
+
if (!res.headersSent) {
|
|
56769
|
+
res.status(500).json({
|
|
56770
|
+
jsonrpc: "2.0",
|
|
56771
|
+
error: { code: -32603, message: "Internal server error" },
|
|
56772
|
+
id: null
|
|
56773
|
+
});
|
|
56774
|
+
}
|
|
56775
|
+
}
|
|
56776
|
+
return;
|
|
56777
|
+
}
|
|
56778
|
+
console.log(`Creating new MCP session for project: ${projectId}`);
|
|
56779
|
+
let newSessionId = null;
|
|
56780
|
+
const transport = new StreamableHTTPServerTransport({
|
|
56781
|
+
sessionIdGenerator: () => {
|
|
56782
|
+
newSessionId = randomUUID2();
|
|
56783
|
+
return newSessionId;
|
|
56784
|
+
},
|
|
56785
|
+
onsessioninitialized: (sessionId) => {
|
|
56786
|
+
console.log(`MCP session initialized: ${sessionId}`);
|
|
56787
|
+
res.setHeader("Mcp-Session-Id", sessionId);
|
|
56788
|
+
}
|
|
56789
|
+
});
|
|
56790
|
+
const server = McpServer_default({
|
|
56791
|
+
sessionManager,
|
|
56792
|
+
projectId,
|
|
56793
|
+
token,
|
|
56794
|
+
getMcpSessionId: () => newSessionId,
|
|
56795
|
+
mcpSessions
|
|
56796
|
+
});
|
|
56358
56797
|
try {
|
|
56359
|
-
|
|
56360
|
-
|
|
56361
|
-
|
|
56362
|
-
|
|
56363
|
-
|
|
56364
|
-
|
|
56798
|
+
await server.connect(transport);
|
|
56799
|
+
const session = {
|
|
56800
|
+
sessionId: newSessionId,
|
|
56801
|
+
transport,
|
|
56802
|
+
server,
|
|
56803
|
+
projectId,
|
|
56804
|
+
token,
|
|
56805
|
+
claimedConnections: new Set,
|
|
56806
|
+
createdAt: Date.now(),
|
|
56807
|
+
lastActivity: Date.now()
|
|
56808
|
+
};
|
|
56809
|
+
res.on("finish", () => {
|
|
56810
|
+
if (newSessionId && !mcpSessions.has(newSessionId)) {
|
|
56811
|
+
mcpSessions.set(newSessionId, session);
|
|
56812
|
+
console.log(`MCP session stored: ${newSessionId}`);
|
|
56813
|
+
}
|
|
56365
56814
|
});
|
|
56366
|
-
await mcpServer.connect(transport);
|
|
56367
56815
|
await transport.handleRequest(req, res, req.body);
|
|
56368
56816
|
} catch (error) {
|
|
56369
56817
|
console.error("Error handling MCP request:", error);
|
|
56370
56818
|
if (!res.headersSent) {
|
|
56371
56819
|
res.status(500).json({
|
|
56372
56820
|
jsonrpc: "2.0",
|
|
56373
|
-
error: {
|
|
56374
|
-
code: -32603,
|
|
56375
|
-
message: "Internal server error"
|
|
56376
|
-
},
|
|
56821
|
+
error: { code: -32603, message: "Internal server error" },
|
|
56377
56822
|
id: null
|
|
56378
56823
|
});
|
|
56379
56824
|
}
|
|
56380
56825
|
}
|
|
56381
56826
|
});
|
|
56827
|
+
app.get("/api/mcp", auth.middleware, async (req, res) => {
|
|
56828
|
+
const sessionId = req.query.sessionId || getSessionIdFromHeaders(req);
|
|
56829
|
+
if (!sessionId) {
|
|
56830
|
+
return res.status(400).json({
|
|
56831
|
+
error: "Session ID required"
|
|
56832
|
+
});
|
|
56833
|
+
}
|
|
56834
|
+
const session = mcpSessions.get(sessionId);
|
|
56835
|
+
if (!session) {
|
|
56836
|
+
return res.status(404).json({
|
|
56837
|
+
error: "Session not found"
|
|
56838
|
+
});
|
|
56839
|
+
}
|
|
56840
|
+
if (session.token !== req.token) {
|
|
56841
|
+
return res.status(401).json({
|
|
56842
|
+
error: "Token mismatch for session"
|
|
56843
|
+
});
|
|
56844
|
+
}
|
|
56845
|
+
session.lastActivity = Date.now();
|
|
56846
|
+
res.status(200).send("SSE endpoint stub");
|
|
56847
|
+
});
|
|
56848
|
+
app.delete("/api/mcp", auth.middleware, async (req, res) => {
|
|
56849
|
+
const sessionId = req.query.sessionId || getSessionIdFromHeaders(req);
|
|
56850
|
+
if (!sessionId) {
|
|
56851
|
+
return res.status(400).json({
|
|
56852
|
+
error: "Session ID required"
|
|
56853
|
+
});
|
|
56854
|
+
}
|
|
56855
|
+
const session = mcpSessions.get(sessionId);
|
|
56856
|
+
if (!session) {
|
|
56857
|
+
return res.status(404).json({
|
|
56858
|
+
error: "Session not found"
|
|
56859
|
+
});
|
|
56860
|
+
}
|
|
56861
|
+
if (session.token !== req.token) {
|
|
56862
|
+
return res.status(401).json({
|
|
56863
|
+
error: "Token mismatch for session"
|
|
56864
|
+
});
|
|
56865
|
+
}
|
|
56866
|
+
console.log(`Closing MCP session: ${sessionId}`);
|
|
56867
|
+
if (sessionManager) {
|
|
56868
|
+
sessionManager.releaseAllForMcpSession(sessionId);
|
|
56869
|
+
}
|
|
56870
|
+
await session.transport.close();
|
|
56871
|
+
mcpSessions.delete(sessionId);
|
|
56872
|
+
res.status(204).send();
|
|
56873
|
+
});
|
|
56382
56874
|
return Object.freeze({});
|
|
56383
56875
|
};
|
|
56384
|
-
var McpRoutes_default = { create:
|
|
56876
|
+
var McpRoutes_default = { create: create4 };
|
|
56385
56877
|
|
|
56386
56878
|
// src/static/StaticRoutes.js
|
|
56387
56879
|
var import_express = __toESM(require_express2(), 1);
|
|
56388
|
-
var
|
|
56880
|
+
var create5 = (app, options) => {
|
|
56389
56881
|
const { staticPath } = options;
|
|
56390
56882
|
const configure = () => {
|
|
56391
56883
|
app.use(import_express.default.static(staticPath, {
|
|
@@ -56395,10 +56887,10 @@ var create4 = (app, options) => {
|
|
|
56395
56887
|
configure();
|
|
56396
56888
|
return Object.freeze({});
|
|
56397
56889
|
};
|
|
56398
|
-
var StaticRoutes_default = { create:
|
|
56890
|
+
var StaticRoutes_default = { create: create5 };
|
|
56399
56891
|
|
|
56400
56892
|
// src/test/TestRoutes.js
|
|
56401
|
-
var
|
|
56893
|
+
var create6 = (app, { sessionManager }) => {
|
|
56402
56894
|
if (!sessionManager) {
|
|
56403
56895
|
console.warn("[TestRoutes] SessionManager not provided, skipping test routes");
|
|
56404
56896
|
return;
|
|
@@ -56432,7 +56924,7 @@ var create5 = (app, { sessionManager }) => {
|
|
|
56432
56924
|
});
|
|
56433
56925
|
console.log("[TestRoutes] Test endpoints registered: GET /test/sessions, POST /test/command");
|
|
56434
56926
|
};
|
|
56435
|
-
var TestRoutes_default = { create:
|
|
56927
|
+
var TestRoutes_default = { create: create6 };
|
|
56436
56928
|
|
|
56437
56929
|
// node_modules/ws/wrapper.mjs
|
|
56438
56930
|
var import_stream = __toESM(require_stream(), 1);
|
|
@@ -56566,7 +57058,7 @@ var MessageValidator_default = {
|
|
|
56566
57058
|
};
|
|
56567
57059
|
|
|
56568
57060
|
// src/wss/WebSocketServer.js
|
|
56569
|
-
var
|
|
57061
|
+
var create7 = ({ server, sessionManager }) => {
|
|
56570
57062
|
if (!server) {
|
|
56571
57063
|
throw new Error("WebSocketServer requires HTTP server instance");
|
|
56572
57064
|
}
|
|
@@ -56637,11 +57129,11 @@ var create6 = ({ server, sessionManager }) => {
|
|
|
56637
57129
|
stop
|
|
56638
57130
|
});
|
|
56639
57131
|
};
|
|
56640
|
-
var WebSocketServer_default = { create:
|
|
57132
|
+
var WebSocketServer_default = { create: create7 };
|
|
56641
57133
|
|
|
56642
57134
|
// src/Server.js
|
|
56643
|
-
var
|
|
56644
|
-
const { port = 3000, staticPath, sessionManager } = options;
|
|
57135
|
+
var create8 = (options = {}) => {
|
|
57136
|
+
const { port = 3000, staticPath, sessionManager, store } = options;
|
|
56645
57137
|
const app = import_express2.default();
|
|
56646
57138
|
const httpServer = createServer(app);
|
|
56647
57139
|
app.use(import_cors.default());
|
|
@@ -56652,8 +57144,11 @@ var create7 = (options = {}) => {
|
|
|
56652
57144
|
timestamp: new Date().toISOString()
|
|
56653
57145
|
});
|
|
56654
57146
|
});
|
|
56655
|
-
ApiRoutes_default.create(app);
|
|
56656
|
-
|
|
57147
|
+
ApiRoutes_default.create(app, { store });
|
|
57148
|
+
if (store) {
|
|
57149
|
+
ProjectRoutes_default.create(app, { store });
|
|
57150
|
+
}
|
|
57151
|
+
McpRoutes_default.create(app, { sessionManager, store });
|
|
56657
57152
|
StaticRoutes_default.create(app, { staticPath });
|
|
56658
57153
|
if (sessionManager) {
|
|
56659
57154
|
TestRoutes_default.create(app, { sessionManager });
|
|
@@ -56692,7 +57187,7 @@ var create7 = (options = {}) => {
|
|
|
56692
57187
|
sessionManager
|
|
56693
57188
|
});
|
|
56694
57189
|
};
|
|
56695
|
-
var Server_default = { create:
|
|
57190
|
+
var Server_default = { create: create8 };
|
|
56696
57191
|
|
|
56697
57192
|
// src/wss/SessionManager.js
|
|
56698
57193
|
var { SessionState: SessionState2 } = MessageValidator_default;
|
|
@@ -56700,12 +57195,13 @@ var DEFAULT_HEARTBEAT_INTERVAL_MS = 30000;
|
|
|
56700
57195
|
var DEFAULT_PONG_TIMEOUT_MS = 5000;
|
|
56701
57196
|
var DEFAULT_MAX_MISSED_PONGS = 3;
|
|
56702
57197
|
var DEFAULT_COMMAND_TIMEOUT_MS = 30000;
|
|
56703
|
-
var
|
|
57198
|
+
var create9 = (options = {}) => {
|
|
56704
57199
|
const {
|
|
56705
57200
|
heartbeatIntervalMs = DEFAULT_HEARTBEAT_INTERVAL_MS,
|
|
56706
57201
|
pongTimeoutMs = DEFAULT_PONG_TIMEOUT_MS,
|
|
56707
57202
|
maxMissedPongs = DEFAULT_MAX_MISSED_PONGS,
|
|
56708
|
-
commandTimeoutMs = DEFAULT_COMMAND_TIMEOUT_MS
|
|
57203
|
+
commandTimeoutMs = DEFAULT_COMMAND_TIMEOUT_MS,
|
|
57204
|
+
onSessionEnded = null
|
|
56709
57205
|
} = options;
|
|
56710
57206
|
const sessions = new Map;
|
|
56711
57207
|
const connectionToPageId = new Map;
|
|
@@ -56737,6 +57233,9 @@ var create8 = (options = {}) => {
|
|
|
56737
57233
|
if (session.wsConnection) {
|
|
56738
57234
|
connectionToPageId.delete(session.wsConnection);
|
|
56739
57235
|
}
|
|
57236
|
+
if (onSessionEnded && session.claimedBy) {
|
|
57237
|
+
onSessionEnded(pageId, session.claimedBy);
|
|
57238
|
+
}
|
|
56740
57239
|
session.state = SessionState2.REMOVED;
|
|
56741
57240
|
sessions.delete(pageId);
|
|
56742
57241
|
};
|
|
@@ -56813,7 +57312,8 @@ var create8 = (options = {}) => {
|
|
|
56813
57312
|
pongTimeout: null,
|
|
56814
57313
|
missedPongs: 0,
|
|
56815
57314
|
pendingCommands: new Map,
|
|
56816
|
-
seq: 0
|
|
57315
|
+
seq: 0,
|
|
57316
|
+
claimedBy: null
|
|
56817
57317
|
};
|
|
56818
57318
|
sessions.set(pageId, session);
|
|
56819
57319
|
connectionToPageId.set(ws, pageId);
|
|
@@ -56930,9 +57430,55 @@ var create8 = (options = {}) => {
|
|
|
56930
57430
|
pageId: session.pageId,
|
|
56931
57431
|
state: session.state,
|
|
56932
57432
|
capabilities: session.capabilities,
|
|
56933
|
-
version: session.version
|
|
57433
|
+
version: session.version,
|
|
57434
|
+
claimedBy: session.claimedBy
|
|
56934
57435
|
}));
|
|
56935
57436
|
};
|
|
57437
|
+
const claimSession = (pageId, mcpSessionId) => {
|
|
57438
|
+
const session = sessions.get(pageId);
|
|
57439
|
+
if (!session) {
|
|
57440
|
+
return { success: false, error: "NOT_FOUND" };
|
|
57441
|
+
}
|
|
57442
|
+
if (session.claimedBy === mcpSessionId) {
|
|
57443
|
+
return { success: true };
|
|
57444
|
+
}
|
|
57445
|
+
if (session.claimedBy !== null) {
|
|
57446
|
+
return { success: false, error: "ALREADY_CLAIMED" };
|
|
57447
|
+
}
|
|
57448
|
+
session.claimedBy = mcpSessionId;
|
|
57449
|
+
console.info(`[SessionManager] Session ${pageId} claimed by MCP session ${mcpSessionId}`);
|
|
57450
|
+
return { success: true };
|
|
57451
|
+
};
|
|
57452
|
+
const releaseSession = (pageId, mcpSessionId) => {
|
|
57453
|
+
const session = sessions.get(pageId);
|
|
57454
|
+
if (!session) {
|
|
57455
|
+
return { success: false, error: "NOT_FOUND" };
|
|
57456
|
+
}
|
|
57457
|
+
if (session.claimedBy === null) {
|
|
57458
|
+
return { success: true };
|
|
57459
|
+
}
|
|
57460
|
+
if (session.claimedBy !== mcpSessionId) {
|
|
57461
|
+
return { success: false, error: "NOT_CLAIMED_BY_YOU" };
|
|
57462
|
+
}
|
|
57463
|
+
session.claimedBy = null;
|
|
57464
|
+
console.info(`[SessionManager] Session ${pageId} released by MCP session ${mcpSessionId}`);
|
|
57465
|
+
return { success: true };
|
|
57466
|
+
};
|
|
57467
|
+
const isClaimedBy = (pageId, mcpSessionId) => {
|
|
57468
|
+
const session = sessions.get(pageId);
|
|
57469
|
+
if (!session) {
|
|
57470
|
+
return false;
|
|
57471
|
+
}
|
|
57472
|
+
return session.claimedBy === mcpSessionId;
|
|
57473
|
+
};
|
|
57474
|
+
const releaseAllForMcpSession = (mcpSessionId) => {
|
|
57475
|
+
for (const session of sessions.values()) {
|
|
57476
|
+
if (session.claimedBy === mcpSessionId) {
|
|
57477
|
+
session.claimedBy = null;
|
|
57478
|
+
console.info(`[SessionManager] Session ${session.pageId} released (MCP session ${mcpSessionId} ended)`);
|
|
57479
|
+
}
|
|
57480
|
+
}
|
|
57481
|
+
};
|
|
56936
57482
|
return Object.freeze({
|
|
56937
57483
|
getSession,
|
|
56938
57484
|
getPageIdForConnection,
|
|
@@ -56943,20 +57489,102 @@ var create8 = (options = {}) => {
|
|
|
56943
57489
|
handleDisconnect,
|
|
56944
57490
|
sendCommand,
|
|
56945
57491
|
endSession,
|
|
56946
|
-
shutdown
|
|
57492
|
+
shutdown,
|
|
57493
|
+
claimSession,
|
|
57494
|
+
releaseSession,
|
|
57495
|
+
isClaimedBy,
|
|
57496
|
+
releaseAllForMcpSession
|
|
56947
57497
|
});
|
|
56948
57498
|
};
|
|
56949
|
-
var SessionManager_default = { create:
|
|
57499
|
+
var SessionManager_default = { create: create9 };
|
|
56950
57500
|
|
|
56951
|
-
// src/
|
|
57501
|
+
// src/core/LocalStore.js
|
|
57502
|
+
import { createHash, randomUUID as randomUUID3 } from "crypto";
|
|
57503
|
+
import fs from "fs/promises";
|
|
57504
|
+
import os from "os";
|
|
56952
57505
|
import path from "path";
|
|
57506
|
+
var DATA_DIR = path.join(os.homedir(), ".caw");
|
|
57507
|
+
var PROJECTS_FILE = path.join(DATA_DIR, "projects.json");
|
|
57508
|
+
var generateToken = (id, name) => {
|
|
57509
|
+
const input = `${id}${name}`;
|
|
57510
|
+
return createHash("sha256").update(input).digest("hex");
|
|
57511
|
+
};
|
|
57512
|
+
var create10 = () => {
|
|
57513
|
+
const readProjects = async () => {
|
|
57514
|
+
try {
|
|
57515
|
+
const data = await fs.readFile(PROJECTS_FILE, "utf8");
|
|
57516
|
+
return JSON.parse(data);
|
|
57517
|
+
} catch (error) {
|
|
57518
|
+
if (error.code === "ENOENT") {
|
|
57519
|
+
return [];
|
|
57520
|
+
}
|
|
57521
|
+
throw error;
|
|
57522
|
+
}
|
|
57523
|
+
};
|
|
57524
|
+
const writeProjects = async (projects) => {
|
|
57525
|
+
await fs.mkdir(DATA_DIR, { recursive: true });
|
|
57526
|
+
const tempFile = `${PROJECTS_FILE}.${randomUUID3()}.tmp`;
|
|
57527
|
+
await fs.writeFile(tempFile, JSON.stringify(projects, null, 2), "utf8");
|
|
57528
|
+
await fs.rename(tempFile, PROJECTS_FILE);
|
|
57529
|
+
};
|
|
57530
|
+
const getNextId = (projects) => {
|
|
57531
|
+
if (projects.length === 0) {
|
|
57532
|
+
return 1;
|
|
57533
|
+
}
|
|
57534
|
+
return Math.max(...projects.map((p) => p.id)) + 1;
|
|
57535
|
+
};
|
|
57536
|
+
const getProjects = async () => {
|
|
57537
|
+
return readProjects();
|
|
57538
|
+
};
|
|
57539
|
+
const getProject = async (id) => {
|
|
57540
|
+
const projects = await readProjects();
|
|
57541
|
+
return projects.find((p) => p.id === id);
|
|
57542
|
+
};
|
|
57543
|
+
const getProjectByToken2 = async (token) => {
|
|
57544
|
+
const projects = await readProjects();
|
|
57545
|
+
return projects.find((p) => p.token === token);
|
|
57546
|
+
};
|
|
57547
|
+
const createProject = async ({ name, path: projectPath }) => {
|
|
57548
|
+
const projects = await readProjects();
|
|
57549
|
+
const id = getNextId(projects);
|
|
57550
|
+
const token = generateToken(id, name);
|
|
57551
|
+
const now = new Date().toISOString();
|
|
57552
|
+
const project = {
|
|
57553
|
+
id,
|
|
57554
|
+
name,
|
|
57555
|
+
path: projectPath,
|
|
57556
|
+
token,
|
|
57557
|
+
createdAt: now,
|
|
57558
|
+
updatedAt: now
|
|
57559
|
+
};
|
|
57560
|
+
projects.push(project);
|
|
57561
|
+
await writeProjects(projects);
|
|
57562
|
+
return project;
|
|
57563
|
+
};
|
|
57564
|
+
return Object.freeze({
|
|
57565
|
+
getProjects,
|
|
57566
|
+
getProject,
|
|
57567
|
+
getProjectByToken: getProjectByToken2,
|
|
57568
|
+
createProject
|
|
57569
|
+
});
|
|
57570
|
+
};
|
|
57571
|
+
var LocalStore_default = { create: create10, generateToken };
|
|
57572
|
+
|
|
57573
|
+
// src/main.js
|
|
57574
|
+
import path2 from "path";
|
|
56953
57575
|
import { fileURLToPath } from "url";
|
|
56954
57576
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
56955
|
-
var __dirname2 =
|
|
56956
|
-
var port =
|
|
56957
|
-
|
|
57577
|
+
var __dirname2 = path2.dirname(__filename2);
|
|
57578
|
+
var port = process.env.CAW_PORT;
|
|
57579
|
+
if (!port) {
|
|
57580
|
+
console.error("ERROR: CAW_PORT environment variable is required");
|
|
57581
|
+
console.error("Run via pm2 or set CAW_PORT manually: CAW_PORT=3131 node dist/main.js");
|
|
57582
|
+
process.exit(1);
|
|
57583
|
+
}
|
|
57584
|
+
var staticPath = process.env.STATIC_PATH || path2.join(__dirname2, "public_html");
|
|
57585
|
+
var store = LocalStore_default.create();
|
|
56958
57586
|
var sessionManager = SessionManager_default.create();
|
|
56959
|
-
var server = Server_default.create({ port, staticPath, sessionManager });
|
|
57587
|
+
var server = Server_default.create({ port, staticPath, sessionManager, store });
|
|
56960
57588
|
server.start();
|
|
56961
57589
|
var shutdown = async (signal) => {
|
|
56962
57590
|
console.log(`
|
|
@@ -56974,4 +57602,4 @@ ${signal} received, shutting down gracefully...`);
|
|
|
56974
57602
|
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
56975
57603
|
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
56976
57604
|
|
|
56977
|
-
//# debugId=
|
|
57605
|
+
//# debugId=36327466799C28A164756E2164756E21
|