@atlisp/mcp 1.4.4 → 1.5.1
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/atlisp-mcp.js +1721 -1536
- package/package.json +2 -2
package/dist/atlisp-mcp.js
CHANGED
|
@@ -1,140 +1,43 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
import rawBody from "raw-body";
|
|
11
|
-
import iconv from "iconv-lite";
|
|
12
|
-
import rateLimit from "express-rate-limit";
|
|
13
|
-
import { randomUUID as randomUUID4 } from "crypto";
|
|
14
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
15
|
-
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
16
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
17
|
-
|
|
18
|
-
// src/session-transport.js
|
|
19
|
-
import { randomUUID } from "crypto";
|
|
20
|
-
var SESSION_TIMEOUT = 30 * 60 * 1e3;
|
|
21
|
-
var SessionTransport = class {
|
|
22
|
-
constructor(sessionId) {
|
|
23
|
-
this.sessionId = sessionId || randomUUID();
|
|
24
|
-
this.onclose = null;
|
|
25
|
-
this.onerror = null;
|
|
26
|
-
this.onmessage = null;
|
|
27
|
-
this._pendingRequest = null;
|
|
28
|
-
this._started = false;
|
|
29
|
-
this._closed = false;
|
|
30
|
-
this._cleanupTimer = null;
|
|
31
|
-
}
|
|
32
|
-
async start() {
|
|
33
|
-
if (this._started) {
|
|
34
|
-
throw new Error("Transport already started");
|
|
35
|
-
}
|
|
36
|
-
this._started = true;
|
|
37
|
-
}
|
|
38
|
-
async send(message) {
|
|
39
|
-
if (this._closed)
|
|
40
|
-
return;
|
|
41
|
-
if (this._pendingRequest) {
|
|
42
|
-
const { resolve, reject, timeout } = this._pendingRequest;
|
|
43
|
-
clearTimeout(timeout);
|
|
44
|
-
this._pendingRequest = null;
|
|
45
|
-
resolve(message);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
async close() {
|
|
49
|
-
if (this._closed)
|
|
50
|
-
return;
|
|
51
|
-
this._closed = true;
|
|
52
|
-
if (this._pendingRequest) {
|
|
53
|
-
const { reject, timeout } = this._pendingRequest;
|
|
54
|
-
clearTimeout(timeout);
|
|
55
|
-
this._pendingRequest = null;
|
|
56
|
-
reject(new Error("Transport closed"));
|
|
57
|
-
}
|
|
58
|
-
this._clearCleanupTimer();
|
|
59
|
-
this.onclose?.();
|
|
60
|
-
}
|
|
61
|
-
async handleRequest(message, timeoutMs = 6e4) {
|
|
62
|
-
if (this._closed) {
|
|
63
|
-
throw new Error("Transport closed");
|
|
64
|
-
}
|
|
65
|
-
if (!message || !message.method) {
|
|
66
|
-
throw new Error("Invalid message");
|
|
67
|
-
}
|
|
68
|
-
if (!message.id) {
|
|
69
|
-
this.onmessage?.(message);
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
return new Promise((resolve, reject) => {
|
|
73
|
-
const timeout = setTimeout(() => {
|
|
74
|
-
if (this._pendingRequest?.resolve === resolve) {
|
|
75
|
-
this._pendingRequest = null;
|
|
76
|
-
reject(new Error("Request timeout"));
|
|
77
|
-
}
|
|
78
|
-
}, timeoutMs);
|
|
79
|
-
this._pendingRequest = { resolve, reject, timeout };
|
|
80
|
-
this.onmessage?.(message);
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
resetCleanupTimer() {
|
|
84
|
-
this._clearCleanupTimer();
|
|
85
|
-
this._cleanupTimer = setTimeout(() => {
|
|
86
|
-
this.close();
|
|
87
|
-
}, SESSION_TIMEOUT);
|
|
88
|
-
}
|
|
89
|
-
_clearCleanupTimer() {
|
|
90
|
-
if (this._cleanupTimer) {
|
|
91
|
-
clearTimeout(this._cleanupTimer);
|
|
92
|
-
this._cleanupTimer = null;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __esm = (fn, res) => function __init() {
|
|
5
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
+
};
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
95
10
|
};
|
|
96
|
-
|
|
97
|
-
// src/atlisp-mcp.js
|
|
98
|
-
import {
|
|
99
|
-
ListToolsRequestSchema,
|
|
100
|
-
CallToolRequestSchema,
|
|
101
|
-
ListResourcesRequestSchema,
|
|
102
|
-
ReadResourceRequestSchema,
|
|
103
|
-
SubscribeRequestSchema,
|
|
104
|
-
UnsubscribeRequestSchema,
|
|
105
|
-
ListPromptsRequestSchema,
|
|
106
|
-
GetPromptRequestSchema
|
|
107
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
108
|
-
|
|
109
|
-
// src/cad.js
|
|
110
|
-
import { spawn } from "child_process";
|
|
111
|
-
import path from "path";
|
|
112
|
-
import { fileURLToPath } from "url";
|
|
113
|
-
import { randomUUID as randomUUID2 } from "crypto";
|
|
114
11
|
|
|
115
12
|
// src/constants.js
|
|
116
|
-
var CAD_PLATFORMS
|
|
117
|
-
var
|
|
118
|
-
"
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
13
|
+
var CAD_PLATFORMS, FILE_EXTENSIONS, SERVER_NAME, SERVER_VERSION, MOCK_PACKAGES;
|
|
14
|
+
var init_constants = __esm({
|
|
15
|
+
"src/constants.js"() {
|
|
16
|
+
CAD_PLATFORMS = ["AutoCAD", "ZWCAD", "GStarCAD", "BricsCAD"];
|
|
17
|
+
FILE_EXTENSIONS = {
|
|
18
|
+
"AutoCAD": [".fas", ".vlx"],
|
|
19
|
+
"ZWCAD": [".zelx", ".vls"],
|
|
20
|
+
"GStarCAD": [".fas", ".vlx"],
|
|
21
|
+
"BricsCAD": [".des"]
|
|
22
|
+
};
|
|
23
|
+
SERVER_NAME = "atlisp-mcp-server";
|
|
24
|
+
SERVER_VERSION = "1.5.0";
|
|
25
|
+
MOCK_PACKAGES = [
|
|
26
|
+
{ name: "base", description: "\u57FA\u7840\u51FD\u6570\u5E93", version: "1.0.0" },
|
|
27
|
+
{ name: "at-pm", description: "\u5DE5\u7A0B\u9879\u76EE\u7BA1\u7406", version: "2.1.0" },
|
|
28
|
+
{ name: "network", description: "\u7F51\u7EDC\u529F\u80FD\u6A21\u5757", version: "1.5.0" },
|
|
29
|
+
{ name: "userman", description: "\u7528\u6237\u7BA1\u7406", version: "1.2.0" },
|
|
30
|
+
{ name: "pkgman", description: "\u5305\u7BA1\u7406\u5668", version: "2.0.0" },
|
|
31
|
+
{ name: "sidebar", description: "\u4FA7\u8FB9\u680F\u5DE5\u5177", version: "1.0.0" },
|
|
32
|
+
{ name: "aibot", description: "AI \u52A9\u624B", version: "0.9.0" },
|
|
33
|
+
{ name: "tips", description: "\u6BCF\u65E5\u63D0\u793A", version: "1.0.0" },
|
|
34
|
+
{ name: "function", description: "\u51FD\u6570\u5E93", version: "3.0.0" }
|
|
35
|
+
];
|
|
36
|
+
}
|
|
37
|
+
});
|
|
136
38
|
|
|
137
39
|
// src/config.js
|
|
40
|
+
import { z } from "zod";
|
|
138
41
|
function parseArgs() {
|
|
139
42
|
const args = process.argv.slice(2);
|
|
140
43
|
const result = { transport: "http", port: "8110", host: "0.0.0.0" };
|
|
@@ -154,42 +57,65 @@ function parseArgs() {
|
|
|
154
57
|
}
|
|
155
58
|
return result;
|
|
156
59
|
}
|
|
157
|
-
var cliArgs
|
|
158
|
-
var
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
60
|
+
var envSchema, cliArgs, envResult, env, config, config_default;
|
|
61
|
+
var init_config = __esm({
|
|
62
|
+
"src/config.js"() {
|
|
63
|
+
envSchema = z.object({
|
|
64
|
+
PORT: z.string().optional().default("8110"),
|
|
65
|
+
HOST: z.string().optional().default("0.0.0.0"),
|
|
66
|
+
TRANSPORT: z.enum(["http", "stdio"]).optional().default("http"),
|
|
67
|
+
MCP_API_KEY: z.string().optional().default(""),
|
|
68
|
+
ENABLE_SSE: z.string().optional().default("true"),
|
|
69
|
+
MESSAGE_TIMEOUT: z.string().optional().default("60000"),
|
|
70
|
+
HEARTBEAT_INTERVAL: z.string().optional().default("30000"),
|
|
71
|
+
BUSY_RETRIES: z.string().optional().default("10"),
|
|
72
|
+
BUSY_DELAY: z.string().optional().default("500"),
|
|
73
|
+
ENABLE_CORS: z.string().optional().default("true"),
|
|
74
|
+
CORS_ORIGIN: z.string().optional().default("*"),
|
|
75
|
+
RATE_LIMIT_WINDOW: z.string().optional().default("60000"),
|
|
76
|
+
RATE_LIMIT_MAX: z.string().optional().default("100"),
|
|
77
|
+
RESOURCE_CACHE_TTL: z.string().optional().default("5000"),
|
|
78
|
+
FUNCTION_LIB_CACHE_TTL: z.string().optional().default("300000"),
|
|
79
|
+
DEBUG: z.string().optional().default("false"),
|
|
80
|
+
DEBUG_FILE: z.string().optional().default(""),
|
|
81
|
+
FUNCTION_LIB_URL: z.string().optional().default("http://s3.atlisp.cn/json/functions.json")
|
|
82
|
+
});
|
|
83
|
+
cliArgs = parseArgs();
|
|
84
|
+
envResult = envSchema.safeParse(process.env);
|
|
85
|
+
if (!envResult.success) {
|
|
86
|
+
console.error("\u73AF\u5883\u53D8\u91CF\u914D\u7F6E\u9519\u8BEF:", envResult.error.flatten().fieldErrors);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
env = envResult.data;
|
|
90
|
+
config = {
|
|
91
|
+
port: parseInt(env.PORT || cliArgs.port, 10),
|
|
92
|
+
host: env.HOST || cliArgs.host,
|
|
93
|
+
transport: process.env.TRANSPORT ? env.TRANSPORT : cliArgs.transport,
|
|
94
|
+
apiKey: env.MCP_API_KEY,
|
|
95
|
+
enableSse: env.ENABLE_SSE === "true",
|
|
96
|
+
messageTimeout: parseInt(env.MESSAGE_TIMEOUT, 10),
|
|
97
|
+
heartbeatInterval: parseInt(env.HEARTBEAT_INTERVAL, 10),
|
|
98
|
+
busyRetries: parseInt(env.BUSY_RETRIES, 10),
|
|
99
|
+
busyDelay: parseInt(env.BUSY_DELAY, 10),
|
|
100
|
+
enableCors: env.ENABLE_CORS === "true",
|
|
101
|
+
corsOrigin: env.CORS_ORIGIN,
|
|
102
|
+
rateLimitWindow: parseInt(env.RATE_LIMIT_WINDOW, 10),
|
|
103
|
+
rateLimitMax: parseInt(env.RATE_LIMIT_MAX, 10),
|
|
104
|
+
resourceCacheTtl: parseInt(env.RESOURCE_CACHE_TTL, 10),
|
|
105
|
+
functionLibCacheTtl: parseInt(env.FUNCTION_LIB_CACHE_TTL, 10),
|
|
106
|
+
debug: env.DEBUG === "true",
|
|
107
|
+
debugFile: env.DEBUG_FILE,
|
|
108
|
+
functionLibUrl: env.FUNCTION_LIB_URL
|
|
109
|
+
};
|
|
110
|
+
config_default = config;
|
|
111
|
+
}
|
|
112
|
+
});
|
|
177
113
|
|
|
178
114
|
// src/cad.js
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
184
|
-
var workerPath = path.join(__dirname, "cad-worker.js");
|
|
185
|
-
var worker = null;
|
|
186
|
-
var _platform = null;
|
|
187
|
-
var heartbeatTimer = null;
|
|
188
|
-
var pendingRequests = /* @__PURE__ */ new Map();
|
|
189
|
-
var stdoutHandlerSetup = false;
|
|
190
|
-
var MAX_BUFFER_SIZE = 1024 * 1024;
|
|
191
|
-
var restartCount = 0;
|
|
192
|
-
var restartTimer = null;
|
|
115
|
+
import { spawn } from "child_process";
|
|
116
|
+
import path from "path";
|
|
117
|
+
import { fileURLToPath } from "url";
|
|
118
|
+
import { randomUUID } from "crypto";
|
|
193
119
|
function resetWorker() {
|
|
194
120
|
for (const [id, { reject, timeout }] of pendingRequests) {
|
|
195
121
|
clearTimeout(timeout);
|
|
@@ -239,8 +165,7 @@ function setupStdoutHandler(w) {
|
|
|
239
165
|
const lines = buffer.split("\n");
|
|
240
166
|
buffer = lines.pop() || "";
|
|
241
167
|
for (const line of lines) {
|
|
242
|
-
if (!line.trim())
|
|
243
|
-
continue;
|
|
168
|
+
if (!line.trim()) continue;
|
|
244
169
|
try {
|
|
245
170
|
const result = JSON.parse(line);
|
|
246
171
|
const rid = result.requestId;
|
|
@@ -266,8 +191,7 @@ function setupStdoutHandler(w) {
|
|
|
266
191
|
}
|
|
267
192
|
function getWorker(force = false) {
|
|
268
193
|
if (!worker || !worker.connected || force) {
|
|
269
|
-
if (worker)
|
|
270
|
-
worker.kill();
|
|
194
|
+
if (worker) worker.kill();
|
|
271
195
|
worker = spawn("node", [workerPath], {
|
|
272
196
|
stdio: ["pipe", "pipe", "pipe"]
|
|
273
197
|
});
|
|
@@ -275,8 +199,7 @@ function getWorker(force = false) {
|
|
|
275
199
|
const w = worker;
|
|
276
200
|
w.on("exit", (code) => {
|
|
277
201
|
console.error("worker exited with code:", code);
|
|
278
|
-
if (w !== worker)
|
|
279
|
-
return;
|
|
202
|
+
if (w !== worker) return;
|
|
280
203
|
w.connected = false;
|
|
281
204
|
for (const [id, { reject, timeout }] of pendingRequests) {
|
|
282
205
|
clearTimeout(timeout);
|
|
@@ -297,8 +220,7 @@ function getWorker(force = false) {
|
|
|
297
220
|
return worker;
|
|
298
221
|
}
|
|
299
222
|
function startHeartbeat() {
|
|
300
|
-
if (heartbeatTimer)
|
|
301
|
-
clearInterval(heartbeatTimer);
|
|
223
|
+
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
302
224
|
heartbeatTimer = setInterval(async () => {
|
|
303
225
|
try {
|
|
304
226
|
const result = await sendMessage({ type: "ping" });
|
|
@@ -314,7 +236,7 @@ function startHeartbeat() {
|
|
|
314
236
|
}
|
|
315
237
|
function sendMessage(msg) {
|
|
316
238
|
const w = getWorker();
|
|
317
|
-
const requestId =
|
|
239
|
+
const requestId = randomUUID();
|
|
318
240
|
return new Promise((resolve, reject) => {
|
|
319
241
|
const timeout = setTimeout(() => {
|
|
320
242
|
const pending = pendingRequests.get(requestId);
|
|
@@ -333,140 +255,146 @@ function sendMessage(msg) {
|
|
|
333
255
|
}
|
|
334
256
|
});
|
|
335
257
|
}
|
|
336
|
-
var
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
try {
|
|
349
|
-
const msg = platform ? { type: "connect", platform } : { type: "connect" };
|
|
350
|
-
const result = await sendMessage(msg);
|
|
351
|
-
if (result && result.success) {
|
|
352
|
-
this.version = result.version;
|
|
353
|
-
this.product = result.platform;
|
|
354
|
-
_platform = result.platform;
|
|
355
|
-
this.connected = true;
|
|
356
|
-
return true;
|
|
357
|
-
}
|
|
358
|
-
} catch (e) {
|
|
359
|
-
console.error("CAD connect error:", e.message);
|
|
360
|
-
}
|
|
361
|
-
return false;
|
|
362
|
-
}
|
|
363
|
-
async sendCommand(code) {
|
|
364
|
-
if (!this.connected)
|
|
365
|
-
throw new Error("\u672A\u8FDE\u63A5 CAD");
|
|
366
|
-
const result = await sendMessage({ type: "send", code, platform: _platform });
|
|
367
|
-
if (result.success)
|
|
368
|
-
return true;
|
|
369
|
-
throw new Error(result.error || "\u53D1\u9001\u5931\u8D25");
|
|
370
|
-
}
|
|
371
|
-
async sendCommandWithResult(code, encoding = null) {
|
|
372
|
-
if (!this.connected)
|
|
373
|
-
throw new Error("\u672A\u8FDE\u63A5 CAD");
|
|
374
|
-
const result = await sendMessage({ type: "sendResult", code, platform: _platform, encoding: encoding || "" });
|
|
375
|
-
if (result.success) {
|
|
376
|
-
return result.result || "";
|
|
377
|
-
}
|
|
378
|
-
throw new Error(result.error || "\u53D1\u9001\u5931\u8D25");
|
|
379
|
-
}
|
|
380
|
-
getVersion() {
|
|
381
|
-
return this.version;
|
|
382
|
-
}
|
|
383
|
-
getPlatform() {
|
|
384
|
-
return this.product;
|
|
385
|
-
}
|
|
386
|
-
async isBusy() {
|
|
387
|
-
if (!this.connected)
|
|
388
|
-
return false;
|
|
389
|
-
try {
|
|
390
|
-
const result = await sendMessage({ type: "isBusy", platform: _platform });
|
|
391
|
-
return result && result.isBusy;
|
|
392
|
-
} catch (e) {
|
|
393
|
-
return false;
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
async hasDoc() {
|
|
397
|
-
if (!this.connected)
|
|
398
|
-
return { hasDoc: false, docCount: 0 };
|
|
399
|
-
try {
|
|
400
|
-
const result = await sendMessage({ type: "hasdoc", platform: _platform });
|
|
401
|
-
return result || { hasDoc: false, docCount: 0 };
|
|
402
|
-
} catch (e) {
|
|
403
|
-
return { hasDoc: false, docCount: 0 };
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
async newDoc() {
|
|
407
|
-
if (!this.connected)
|
|
408
|
-
throw new Error("\u672A\u8FDE\u63A5 CAD");
|
|
409
|
-
try {
|
|
410
|
-
const result = await sendMessage({ type: "newdoc", platform: _platform });
|
|
411
|
-
return result && result.success;
|
|
412
|
-
} catch (e) {
|
|
413
|
-
return false;
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
async bringToFront() {
|
|
417
|
-
if (!this.connected)
|
|
418
|
-
throw new Error("\u672A\u8FDE\u63A5 CAD");
|
|
419
|
-
try {
|
|
420
|
-
const result = await sendMessage({ type: "bringToFront", platform: _platform });
|
|
421
|
-
return result && result.success;
|
|
422
|
-
} catch (e) {
|
|
423
|
-
return false;
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
async getInfo() {
|
|
427
|
-
if (!this.connected)
|
|
428
|
-
return "CAD \u672A\u8FDE\u63A5";
|
|
429
|
-
return `Platform: ${this.product}, Version: ${this.version}, Status: Connected`;
|
|
430
|
-
}
|
|
431
|
-
async disconnect() {
|
|
432
|
-
this.connected = false;
|
|
433
|
-
this.version = null;
|
|
434
|
-
this.product = null;
|
|
435
|
-
_platform = null;
|
|
436
|
-
resetWorker();
|
|
437
|
-
}
|
|
438
|
-
async ping() {
|
|
439
|
-
try {
|
|
440
|
-
const result = await sendMessage({ type: "ping" });
|
|
441
|
-
return result && result.pong;
|
|
442
|
-
} catch (e) {
|
|
443
|
-
return false;
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
_reset() {
|
|
447
|
-
this.connected = false;
|
|
448
|
-
this.version = null;
|
|
449
|
-
this.product = null;
|
|
258
|
+
var MESSAGE_TIMEOUT, HEARTBEAT_INTERVAL, MAX_RESTART_ATTEMPTS, RESTART_BACKOFF_BASE, __dirname, workerPath, worker, _platform, heartbeatTimer, pendingRequests, stdoutHandlerSetup, MAX_BUFFER_SIZE, restartCount, restartTimer, CadConnection, cad;
|
|
259
|
+
var init_cad = __esm({
|
|
260
|
+
"src/cad.js"() {
|
|
261
|
+
init_constants();
|
|
262
|
+
init_config();
|
|
263
|
+
MESSAGE_TIMEOUT = config_default.messageTimeout;
|
|
264
|
+
HEARTBEAT_INTERVAL = config_default.heartbeatInterval;
|
|
265
|
+
MAX_RESTART_ATTEMPTS = 5;
|
|
266
|
+
RESTART_BACKOFF_BASE = 1e3;
|
|
267
|
+
__dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
268
|
+
workerPath = path.join(__dirname, "cad-worker.js");
|
|
269
|
+
worker = null;
|
|
450
270
|
_platform = null;
|
|
451
|
-
|
|
271
|
+
heartbeatTimer = null;
|
|
272
|
+
pendingRequests = /* @__PURE__ */ new Map();
|
|
273
|
+
stdoutHandlerSetup = false;
|
|
274
|
+
MAX_BUFFER_SIZE = 1024 * 1024;
|
|
275
|
+
restartCount = 0;
|
|
276
|
+
restartTimer = null;
|
|
277
|
+
CadConnection = class {
|
|
278
|
+
constructor() {
|
|
279
|
+
this.connected = false;
|
|
280
|
+
this.version = null;
|
|
281
|
+
this.product = null;
|
|
282
|
+
}
|
|
283
|
+
isAvailable() {
|
|
284
|
+
return process.platform === "win32";
|
|
285
|
+
}
|
|
286
|
+
async connect(platform = null) {
|
|
287
|
+
if (!this.isAvailable()) return false;
|
|
288
|
+
try {
|
|
289
|
+
const msg = platform ? { type: "connect", platform } : { type: "connect" };
|
|
290
|
+
const result = await sendMessage(msg);
|
|
291
|
+
if (result && result.success) {
|
|
292
|
+
this.version = result.version;
|
|
293
|
+
this.product = result.platform;
|
|
294
|
+
_platform = result.platform;
|
|
295
|
+
this.connected = true;
|
|
296
|
+
return true;
|
|
297
|
+
}
|
|
298
|
+
} catch (e) {
|
|
299
|
+
console.error("CAD connect error:", e.message);
|
|
300
|
+
}
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
async sendCommand(code) {
|
|
304
|
+
if (!this.connected) throw new Error("\u672A\u8FDE\u63A5 CAD");
|
|
305
|
+
const result = await sendMessage({ type: "send", code, platform: _platform });
|
|
306
|
+
if (result.success) return true;
|
|
307
|
+
throw new Error(result.error || "\u53D1\u9001\u5931\u8D25");
|
|
308
|
+
}
|
|
309
|
+
async sendCommandWithResult(code, encoding = null) {
|
|
310
|
+
if (!this.connected) throw new Error("\u672A\u8FDE\u63A5 CAD");
|
|
311
|
+
const result = await sendMessage({ type: "sendResult", code, platform: _platform, encoding: encoding || "" });
|
|
312
|
+
if (result.success) {
|
|
313
|
+
return result.result || "";
|
|
314
|
+
}
|
|
315
|
+
throw new Error(result.error || "\u53D1\u9001\u5931\u8D25");
|
|
316
|
+
}
|
|
317
|
+
getVersion() {
|
|
318
|
+
return this.version;
|
|
319
|
+
}
|
|
320
|
+
getPlatform() {
|
|
321
|
+
return this.product;
|
|
322
|
+
}
|
|
323
|
+
async isBusy() {
|
|
324
|
+
if (!this.connected) return false;
|
|
325
|
+
try {
|
|
326
|
+
const result = await sendMessage({ type: "isBusy", platform: _platform });
|
|
327
|
+
return result && result.isBusy;
|
|
328
|
+
} catch (e) {
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
async hasDoc() {
|
|
333
|
+
if (!this.connected) return { hasDoc: false, docCount: 0 };
|
|
334
|
+
try {
|
|
335
|
+
const result = await sendMessage({ type: "hasdoc", platform: _platform });
|
|
336
|
+
return result || { hasDoc: false, docCount: 0 };
|
|
337
|
+
} catch (e) {
|
|
338
|
+
return { hasDoc: false, docCount: 0 };
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
async newDoc() {
|
|
342
|
+
if (!this.connected) throw new Error("\u672A\u8FDE\u63A5 CAD");
|
|
343
|
+
try {
|
|
344
|
+
const result = await sendMessage({ type: "newdoc", platform: _platform });
|
|
345
|
+
return result && result.success;
|
|
346
|
+
} catch (e) {
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
async bringToFront() {
|
|
351
|
+
if (!this.connected) throw new Error("\u672A\u8FDE\u63A5 CAD");
|
|
352
|
+
try {
|
|
353
|
+
const result = await sendMessage({ type: "bringToFront", platform: _platform });
|
|
354
|
+
return result && result.success;
|
|
355
|
+
} catch (e) {
|
|
356
|
+
return false;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
async getInfo() {
|
|
360
|
+
if (!this.connected) return "CAD \u672A\u8FDE\u63A5";
|
|
361
|
+
return `Platform: ${this.product}, Version: ${this.version}, Status: Connected`;
|
|
362
|
+
}
|
|
363
|
+
async disconnect() {
|
|
364
|
+
this.connected = false;
|
|
365
|
+
this.version = null;
|
|
366
|
+
this.product = null;
|
|
367
|
+
_platform = null;
|
|
368
|
+
resetWorker();
|
|
369
|
+
}
|
|
370
|
+
async ping() {
|
|
371
|
+
try {
|
|
372
|
+
const result = await sendMessage({ type: "ping" });
|
|
373
|
+
return result && result.pong;
|
|
374
|
+
} catch (e) {
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
_reset() {
|
|
379
|
+
this.connected = false;
|
|
380
|
+
this.version = null;
|
|
381
|
+
this.product = null;
|
|
382
|
+
_platform = null;
|
|
383
|
+
resetWorker();
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
cad = new CadConnection();
|
|
452
387
|
}
|
|
453
|
-
};
|
|
454
|
-
var cad = new CadConnection();
|
|
388
|
+
});
|
|
455
389
|
|
|
456
390
|
// src/logger.js
|
|
457
391
|
import fs from "fs";
|
|
458
392
|
import path2 from "path";
|
|
459
393
|
import os from "os";
|
|
460
|
-
var DEBUG_FILE = process.env.DEBUG_FILE || path2.join(os.tmpdir(), "mcp-server-debug.log");
|
|
461
|
-
var MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
462
|
-
var MAX_LOG_FILES = 5;
|
|
463
|
-
var stream = null;
|
|
464
394
|
function rotateLog() {
|
|
465
|
-
if (!fs.existsSync(DEBUG_FILE))
|
|
466
|
-
return;
|
|
395
|
+
if (!fs.existsSync(DEBUG_FILE)) return;
|
|
467
396
|
const stat = fs.statSync(DEBUG_FILE);
|
|
468
|
-
if (stat.size < MAX_LOG_SIZE)
|
|
469
|
-
return;
|
|
397
|
+
if (stat.size < MAX_LOG_SIZE) return;
|
|
470
398
|
if (stream) {
|
|
471
399
|
stream.end();
|
|
472
400
|
stream = null;
|
|
@@ -493,10 +421,27 @@ function getStream() {
|
|
|
493
421
|
}
|
|
494
422
|
return stream;
|
|
495
423
|
}
|
|
496
|
-
function
|
|
497
|
-
const entry =
|
|
498
|
-
|
|
499
|
-
|
|
424
|
+
function formatMessage(level, message, meta = {}) {
|
|
425
|
+
const entry = {
|
|
426
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
427
|
+
level,
|
|
428
|
+
message,
|
|
429
|
+
...meta,
|
|
430
|
+
pid: process.pid,
|
|
431
|
+
hostname: os.hostname()
|
|
432
|
+
};
|
|
433
|
+
return JSON.stringify(entry);
|
|
434
|
+
}
|
|
435
|
+
function log(message, meta = {}) {
|
|
436
|
+
if (config_default.debug) {
|
|
437
|
+
getStream().write(formatMessage("INFO", message, meta) + "\n");
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
function error(message, meta = {}) {
|
|
441
|
+
getStream().write(formatMessage("ERROR", message, meta) + "\n");
|
|
442
|
+
if (config_default.debug) {
|
|
443
|
+
console.error(`[ERROR] ${message}`, meta);
|
|
444
|
+
}
|
|
500
445
|
}
|
|
501
446
|
function closeLog() {
|
|
502
447
|
if (stream) {
|
|
@@ -504,6 +449,16 @@ function closeLog() {
|
|
|
504
449
|
stream = null;
|
|
505
450
|
}
|
|
506
451
|
}
|
|
452
|
+
var DEBUG_FILE, MAX_LOG_SIZE, MAX_LOG_FILES, stream;
|
|
453
|
+
var init_logger = __esm({
|
|
454
|
+
"src/logger.js"() {
|
|
455
|
+
init_config();
|
|
456
|
+
DEBUG_FILE = config_default.debugFile || path2.join(os.tmpdir(), "mcp-server-debug.log");
|
|
457
|
+
MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
458
|
+
MAX_LOG_FILES = 5;
|
|
459
|
+
stream = null;
|
|
460
|
+
}
|
|
461
|
+
});
|
|
507
462
|
|
|
508
463
|
// src/handlers/cad-handlers.js
|
|
509
464
|
async function connectCad(platform = null) {
|
|
@@ -528,8 +483,7 @@ async function evalLisp(code, withResult = false, encoding = null) {
|
|
|
528
483
|
return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
|
|
529
484
|
}
|
|
530
485
|
const trimmed = (code || "").trim();
|
|
531
|
-
if (!trimmed)
|
|
532
|
-
return { content: [{ type: "text", text: "\u547D\u4EE4\u4E3A\u7A7A\uFF0C\u672A\u53D1\u9001" }] };
|
|
486
|
+
if (!trimmed) return { content: [{ type: "text", text: "\u547D\u4EE4\u4E3A\u7A7A\uFF0C\u672A\u53D1\u9001" }] };
|
|
533
487
|
try {
|
|
534
488
|
if (withResult) {
|
|
535
489
|
const result = await cad.sendCommandWithResult(trimmed, encoding);
|
|
@@ -549,17 +503,14 @@ async function getCadInfo() {
|
|
|
549
503
|
if (!cad.connected) {
|
|
550
504
|
await cad.connect();
|
|
551
505
|
}
|
|
552
|
-
if (!cad.connected)
|
|
553
|
-
return { content: [{ type: "text", text: "CAD \u672A\u8FDE\u63A5" }] };
|
|
506
|
+
if (!cad.connected) return { content: [{ type: "text", text: "CAD \u672A\u8FDE\u63A5" }] };
|
|
554
507
|
const info = await cad.getInfo();
|
|
555
508
|
return { content: [{ type: "text", text: info }] };
|
|
556
509
|
}
|
|
557
510
|
async function atCommand(command) {
|
|
558
|
-
if (!cad.connected)
|
|
559
|
-
return { content: [{ type: "text", text: "\u8BF7\u5148\u8FDE\u63A5 CAD" }], isError: true };
|
|
511
|
+
if (!cad.connected) return { content: [{ type: "text", text: "\u8BF7\u5148\u8FDE\u63A5 CAD" }], isError: true };
|
|
560
512
|
const trimmed = (command || "").trim();
|
|
561
|
-
if (!trimmed)
|
|
562
|
-
return { content: [{ type: "text", text: "\u547D\u4EE4\u4E3A\u7A7A\uFF0C\u672A\u53D1\u9001" }] };
|
|
513
|
+
if (!trimmed) return { content: [{ type: "text", text: "\u547D\u4EE4\u4E3A\u7A7A\uFF0C\u672A\u53D1\u9001" }] };
|
|
563
514
|
await cad.sendCommand(trimmed + "\n");
|
|
564
515
|
return { content: [{ type: "text", text: "\u5DF2\u53D1\u9001\u547D\u4EE4" }] };
|
|
565
516
|
}
|
|
@@ -567,8 +518,7 @@ async function newDocument() {
|
|
|
567
518
|
if (!cad.connected) {
|
|
568
519
|
await cad.connect();
|
|
569
520
|
}
|
|
570
|
-
if (!cad.connected)
|
|
571
|
-
return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
|
|
521
|
+
if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
|
|
572
522
|
const result = await cad.newDoc();
|
|
573
523
|
if (result) {
|
|
574
524
|
return { content: [{ type: "text", text: "\u5DF2\u5728 CAD \u4E2D\u65B0\u5EFA\u7A7A\u767D\u6587\u6863" }] };
|
|
@@ -579,8 +529,7 @@ async function bringToFront() {
|
|
|
579
529
|
if (!cad.connected) {
|
|
580
530
|
await cad.connect();
|
|
581
531
|
}
|
|
582
|
-
if (!cad.connected)
|
|
583
|
-
return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
|
|
532
|
+
if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
|
|
584
533
|
const result = await cad.bringToFront();
|
|
585
534
|
if (result) {
|
|
586
535
|
return { content: [{ type: "text", text: "\u5DF2\u5C06 CAD \u7A97\u53E3\u5207\u6362\u5230\u524D\u53F0" }] };
|
|
@@ -591,8 +540,7 @@ async function installAtlisp() {
|
|
|
591
540
|
if (!cad.connected) {
|
|
592
541
|
await cad.connect();
|
|
593
542
|
}
|
|
594
|
-
if (!cad.connected)
|
|
595
|
-
return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
|
|
543
|
+
if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
|
|
596
544
|
const installCode = `(progn(vl-load-com)(setq s strcat h "http" o(vlax-create-object (s"win"h".win"h"request.5.1"))v vlax-invoke e eval r read)(v o'open "get" (s h"://atlisp.""cn/@"):vlax-true)(v o'send)(v o'WaitforResponse 1000)(e(r(vlax-get-property o'ResponseText))))`;
|
|
597
545
|
await cad.sendCommand(installCode + "\n");
|
|
598
546
|
return { content: [{ type: "text", text: "\u5DF2\u53D1\u9001 @lisp \u5B89\u88C5\u4EE3\u7801\u5230 CAD" }] };
|
|
@@ -601,12 +549,10 @@ async function listFunctionsInCad() {
|
|
|
601
549
|
if (!cad.connected) {
|
|
602
550
|
await cad.connect();
|
|
603
551
|
}
|
|
604
|
-
if (!cad.connected)
|
|
605
|
-
return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
|
|
552
|
+
if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
|
|
606
553
|
try {
|
|
607
554
|
const result = await cad.sendCommandWithResult("(atoms-family 0)", null);
|
|
608
|
-
if (result)
|
|
609
|
-
return { content: [{ type: "text", text: result }] };
|
|
555
|
+
if (result) return { content: [{ type: "text", text: result }] };
|
|
610
556
|
return { content: [{ type: "text", text: "CAD \u51FD\u6570\u5217\u8868\u4E3A\u7A7A" }] };
|
|
611
557
|
} catch (e) {
|
|
612
558
|
return { content: [{ type: "text", text: `\u9519\u8BEF: ${e.message}` }], isError: true };
|
|
@@ -661,8 +607,7 @@ async function initAtlisp() {
|
|
|
661
607
|
if (!cad.connected) {
|
|
662
608
|
await cad.connect();
|
|
663
609
|
}
|
|
664
|
-
if (!cad.connected)
|
|
665
|
-
return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
|
|
610
|
+
if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
|
|
666
611
|
const code = `(if (null @::load-module)
|
|
667
612
|
(progn
|
|
668
613
|
(vl-load-com)
|
|
@@ -678,12 +623,14 @@ async function initAtlisp() {
|
|
|
678
623
|
return { content: [{ type: "text", text: `\u9519\u8BEF: ${e.message}` }], isError: true };
|
|
679
624
|
}
|
|
680
625
|
}
|
|
626
|
+
var init_cad_handlers = __esm({
|
|
627
|
+
"src/handlers/cad-handlers.js"() {
|
|
628
|
+
init_cad();
|
|
629
|
+
init_logger();
|
|
630
|
+
}
|
|
631
|
+
});
|
|
681
632
|
|
|
682
633
|
// src/handlers/package-handlers.js
|
|
683
|
-
var PACKAGES_LIST_URL = process.env.PACKAGES_LIST_URL || "http://s3.atlisp.cn/packages-list?fmt=json";
|
|
684
|
-
var cachedPackages = null;
|
|
685
|
-
var cachedAt = null;
|
|
686
|
-
var CACHE_TTL = 10 * 60 * 1e3;
|
|
687
634
|
async function fetchPackages() {
|
|
688
635
|
const now = Date.now();
|
|
689
636
|
if (cachedPackages && cachedAt && now - cachedAt < CACHE_TTL) {
|
|
@@ -702,15 +649,12 @@ async function fetchPackages() {
|
|
|
702
649
|
return cachedPackages || MOCK_PACKAGES;
|
|
703
650
|
}
|
|
704
651
|
function flattenPackages(data) {
|
|
705
|
-
if (Array.isArray(data))
|
|
706
|
-
|
|
707
|
-
if (data && Array.isArray(data.packages))
|
|
708
|
-
return data.packages;
|
|
652
|
+
if (Array.isArray(data)) return data;
|
|
653
|
+
if (data && Array.isArray(data.packages)) return data.packages;
|
|
709
654
|
return [];
|
|
710
655
|
}
|
|
711
656
|
async function listPackages() {
|
|
712
|
-
if (!cad.connected)
|
|
713
|
-
await cad.connect();
|
|
657
|
+
if (!cad.connected) await cad.connect();
|
|
714
658
|
if (!cad.connected) {
|
|
715
659
|
const data2 = await fetchPackages();
|
|
716
660
|
const items2 = flattenPackages(data2);
|
|
@@ -718,8 +662,7 @@ async function listPackages() {
|
|
|
718
662
|
return { content: [{ type: "text", text: text2 }] };
|
|
719
663
|
}
|
|
720
664
|
const result = await cad.sendCommandWithResult("(@::package-list-in-local)");
|
|
721
|
-
if (result && result !== "nil")
|
|
722
|
-
return { content: [{ type: "text", text: result }] };
|
|
665
|
+
if (result && result !== "nil") return { content: [{ type: "text", text: result }] };
|
|
723
666
|
const data = await fetchPackages();
|
|
724
667
|
const items = flattenPackages(data);
|
|
725
668
|
const text = items.length ? `\u4ECE\u6CE8\u518C\u8868\u83B7\u53D6\u7684 @lisp \u5305:
|
|
@@ -739,12 +682,10 @@ async function searchPackages(query) {
|
|
|
739
682
|
for (const p of items) {
|
|
740
683
|
const name = (p.name || "").toLowerCase();
|
|
741
684
|
const desc = (p.description || "").toLowerCase();
|
|
742
|
-
if (name.includes(q) || desc.includes(q))
|
|
743
|
-
results.push(p);
|
|
685
|
+
if (name.includes(q) || desc.includes(q)) results.push(p);
|
|
744
686
|
}
|
|
745
687
|
} else {
|
|
746
|
-
for (const p of items)
|
|
747
|
-
results.push(p);
|
|
688
|
+
for (const p of items) results.push(p);
|
|
748
689
|
}
|
|
749
690
|
if (results.length === 0) {
|
|
750
691
|
return { content: [{ type: "text", text: `\u672A\u627E\u5230\u5305\u542B "${query}" \u7684\u5305` }] };
|
|
@@ -765,16 +706,22 @@ async function installPackage(packageName) {
|
|
|
765
706
|
}
|
|
766
707
|
return { content: [{ type: "text", text: `\u5305 "${packageName}" \u5B89\u88C5\u547D\u4EE4\u5DF2\u53D1\u9001\uFF01` }] };
|
|
767
708
|
}
|
|
709
|
+
var PACKAGES_LIST_URL, cachedPackages, cachedAt, CACHE_TTL;
|
|
710
|
+
var init_package_handlers = __esm({
|
|
711
|
+
"src/handlers/package-handlers.js"() {
|
|
712
|
+
init_cad();
|
|
713
|
+
init_constants();
|
|
714
|
+
PACKAGES_LIST_URL = process.env.PACKAGES_LIST_URL || "http://s3.atlisp.cn/packages-list?fmt=json";
|
|
715
|
+
cachedPackages = null;
|
|
716
|
+
cachedAt = null;
|
|
717
|
+
CACHE_TTL = 10 * 60 * 1e3;
|
|
718
|
+
}
|
|
719
|
+
});
|
|
768
720
|
|
|
769
721
|
// src/handlers/function-handlers.js
|
|
770
722
|
import fs2 from "fs";
|
|
771
723
|
import path3 from "path";
|
|
772
724
|
import os2 from "os";
|
|
773
|
-
var FUNCTIONS_DIR = process.env.FUNCTIONS_DIR || "";
|
|
774
|
-
var FUNCTIONS_URL = process.env.FUNCTIONS_URL || "http://s3.atlisp.cn/json/functions.json";
|
|
775
|
-
var CACHE_DIR = path3.join(os2.homedir(), ".atlisp", "cache");
|
|
776
|
-
var FUNCLIB_CACHE_FILE = path3.join(CACHE_DIR, "functions.json");
|
|
777
|
-
var FUNCLIB_CACHE_TTL = 24 * 60 * 60 * 1e3;
|
|
778
725
|
function readCacheFromDisk() {
|
|
779
726
|
try {
|
|
780
727
|
const stat = fs2.statSync(FUNCLIB_CACHE_FILE);
|
|
@@ -796,8 +743,7 @@ function writeCacheToDisk(data) {
|
|
|
796
743
|
}
|
|
797
744
|
async function fetchFunctions() {
|
|
798
745
|
const diskCache = readCacheFromDisk();
|
|
799
|
-
if (diskCache)
|
|
800
|
-
return diskCache;
|
|
746
|
+
if (diskCache) return diskCache;
|
|
801
747
|
try {
|
|
802
748
|
const response = await fetch(FUNCTIONS_URL, { headers: { "User-Agent": "atlisp-mcp" } });
|
|
803
749
|
if (response.ok) {
|
|
@@ -817,8 +763,7 @@ async function getFunctionUsage(funcName, packageName) {
|
|
|
817
763
|
if (data) {
|
|
818
764
|
const funcs = Object.values(data.all_functions || {});
|
|
819
765
|
const func = funcs.find((f2) => f2.name === funcName || f2.name === `${packageName}:${funcName}`);
|
|
820
|
-
if (func)
|
|
821
|
-
results.push(func);
|
|
766
|
+
if (func) results.push(func);
|
|
822
767
|
}
|
|
823
768
|
if (results.length === 0 && FUNCTIONS_DIR) {
|
|
824
769
|
try {
|
|
@@ -827,8 +772,7 @@ async function getFunctionUsage(funcName, packageName) {
|
|
|
827
772
|
const raw = fs2.readFileSync(path3.join(FUNCTIONS_DIR, file), "utf-8");
|
|
828
773
|
const data2 = JSON.parse(raw);
|
|
829
774
|
const func = data2.functions?.find((f2) => f2.name === funcName);
|
|
830
|
-
if (func)
|
|
831
|
-
results.push(func);
|
|
775
|
+
if (func) results.push(func);
|
|
832
776
|
}
|
|
833
777
|
} catch {
|
|
834
778
|
}
|
|
@@ -890,15 +834,23 @@ ${allFuncs.map((f) => f.name).join("\n")}` }] };
|
|
|
890
834
|
return { content: [{ type: "text", text: `\u9519\u8BEF: ${e.message}` }], isError: true };
|
|
891
835
|
}
|
|
892
836
|
}
|
|
837
|
+
var FUNCTIONS_DIR, FUNCTIONS_URL, CACHE_DIR, FUNCLIB_CACHE_FILE, FUNCLIB_CACHE_TTL;
|
|
838
|
+
var init_function_handlers = __esm({
|
|
839
|
+
"src/handlers/function-handlers.js"() {
|
|
840
|
+
init_logger();
|
|
841
|
+
init_cad_handlers();
|
|
842
|
+
FUNCTIONS_DIR = process.env.FUNCTIONS_DIR || "";
|
|
843
|
+
FUNCTIONS_URL = process.env.FUNCTIONS_URL || "http://s3.atlisp.cn/json/functions.json";
|
|
844
|
+
CACHE_DIR = path3.join(os2.homedir(), ".atlisp", "cache");
|
|
845
|
+
FUNCLIB_CACHE_FILE = path3.join(CACHE_DIR, "functions.json");
|
|
846
|
+
FUNCLIB_CACHE_TTL = 24 * 60 * 60 * 1e3;
|
|
847
|
+
}
|
|
848
|
+
});
|
|
893
849
|
|
|
894
850
|
// src/handlers/resource-handlers.js
|
|
895
851
|
import fs3 from "fs";
|
|
896
852
|
import path4 from "path";
|
|
897
853
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
898
|
-
var __dirname2 = path4.dirname(fileURLToPath2(import.meta.url));
|
|
899
|
-
var STANDARDS_DIR = path4.join(__dirname2, "..", "standards");
|
|
900
|
-
var CACHE_TTL2 = config_default.resourceCacheTtl;
|
|
901
|
-
var resourceCache = /* @__PURE__ */ new Map();
|
|
902
854
|
function getCached(key) {
|
|
903
855
|
const entry = resourceCache.get(key);
|
|
904
856
|
if (entry && Date.now() - entry.timestamp < CACHE_TTL2) {
|
|
@@ -922,14 +874,11 @@ function clearCache(uri) {
|
|
|
922
874
|
}
|
|
923
875
|
}
|
|
924
876
|
function parseLispList(str) {
|
|
925
|
-
if (!str || typeof str !== "string")
|
|
926
|
-
return null;
|
|
877
|
+
if (!str || typeof str !== "string") return null;
|
|
927
878
|
const trimmed = str.trim();
|
|
928
|
-
if (!trimmed.startsWith("(") || !trimmed.endsWith(")"))
|
|
929
|
-
return null;
|
|
879
|
+
if (!trimmed.startsWith("(") || !trimmed.endsWith(")")) return null;
|
|
930
880
|
const content = trimmed.slice(1, -1).trim();
|
|
931
|
-
if (!content)
|
|
932
|
-
return [];
|
|
881
|
+
if (!content) return [];
|
|
933
882
|
const result = [];
|
|
934
883
|
let depth = 0;
|
|
935
884
|
let current = "";
|
|
@@ -971,44 +920,32 @@ function parseLispList(str) {
|
|
|
971
920
|
}
|
|
972
921
|
current += ch;
|
|
973
922
|
}
|
|
974
|
-
if (current.trim())
|
|
975
|
-
result.push(current.trim());
|
|
923
|
+
if (current.trim()) result.push(current.trim());
|
|
976
924
|
return result.map((item) => {
|
|
977
925
|
const t = item.trim();
|
|
978
|
-
if (t === "nil")
|
|
979
|
-
|
|
980
|
-
if (t === "
|
|
981
|
-
return true;
|
|
982
|
-
if (t === "T")
|
|
983
|
-
return true;
|
|
926
|
+
if (t === "nil") return null;
|
|
927
|
+
if (t === "t") return true;
|
|
928
|
+
if (t === "T") return true;
|
|
984
929
|
const num = Number(t);
|
|
985
|
-
if (!isNaN(num) && t !== "")
|
|
986
|
-
|
|
987
|
-
if (t.startsWith('"') && t.endsWith('"'))
|
|
988
|
-
return t.slice(1, -1);
|
|
930
|
+
if (!isNaN(num) && t !== "") return num;
|
|
931
|
+
if (t.startsWith('"') && t.endsWith('"')) return t.slice(1, -1);
|
|
989
932
|
return t;
|
|
990
933
|
});
|
|
991
934
|
}
|
|
992
935
|
function parseLispRecursive(str) {
|
|
993
|
-
if (!str || typeof str !== "string")
|
|
994
|
-
return null;
|
|
936
|
+
if (!str || typeof str !== "string") return null;
|
|
995
937
|
str = str.trim();
|
|
996
|
-
if (!str)
|
|
997
|
-
return null;
|
|
938
|
+
if (!str) return null;
|
|
998
939
|
let pos = 0;
|
|
999
940
|
function skipWs() {
|
|
1000
|
-
while (pos < str.length && /\s/.test(str[pos]))
|
|
1001
|
-
pos++;
|
|
941
|
+
while (pos < str.length && /\s/.test(str[pos])) pos++;
|
|
1002
942
|
}
|
|
1003
943
|
function parseValue() {
|
|
1004
944
|
skipWs();
|
|
1005
|
-
if (pos >= str.length)
|
|
1006
|
-
return null;
|
|
945
|
+
if (pos >= str.length) return null;
|
|
1007
946
|
const ch = str[pos];
|
|
1008
|
-
if (ch === "(")
|
|
1009
|
-
|
|
1010
|
-
if (ch === '"')
|
|
1011
|
-
return parseString();
|
|
947
|
+
if (ch === "(") return parseList();
|
|
948
|
+
if (ch === '"') return parseString();
|
|
1012
949
|
if (ch === "'") {
|
|
1013
950
|
pos++;
|
|
1014
951
|
return parseValue();
|
|
@@ -1021,16 +958,11 @@ function parseLispRecursive(str) {
|
|
|
1021
958
|
while (pos < str.length && str[pos] !== '"') {
|
|
1022
959
|
if (str[pos] === "\\" && pos + 1 < str.length) {
|
|
1023
960
|
pos++;
|
|
1024
|
-
if (str[pos] === '"')
|
|
1025
|
-
|
|
1026
|
-
else if (str[pos] === "
|
|
1027
|
-
|
|
1028
|
-
else
|
|
1029
|
-
r += " ";
|
|
1030
|
-
else if (str[pos] === "\\")
|
|
1031
|
-
r += "\\";
|
|
1032
|
-
else
|
|
1033
|
-
r += str[pos];
|
|
961
|
+
if (str[pos] === '"') r += '"';
|
|
962
|
+
else if (str[pos] === "n") r += "\n";
|
|
963
|
+
else if (str[pos] === "t") r += " ";
|
|
964
|
+
else if (str[pos] === "\\") r += "\\";
|
|
965
|
+
else r += str[pos];
|
|
1034
966
|
} else {
|
|
1035
967
|
r += str[pos];
|
|
1036
968
|
}
|
|
@@ -1041,16 +973,12 @@ function parseLispRecursive(str) {
|
|
|
1041
973
|
}
|
|
1042
974
|
function parseAtom() {
|
|
1043
975
|
const start = pos;
|
|
1044
|
-
while (pos < str.length && !/\s/.test(str[pos]) && str[pos] !== ")" && str[pos] !== "(")
|
|
1045
|
-
pos++;
|
|
976
|
+
while (pos < str.length && !/\s/.test(str[pos]) && str[pos] !== ")" && str[pos] !== "(") pos++;
|
|
1046
977
|
const atom = str.slice(start, pos);
|
|
1047
|
-
if (atom === "nil" || atom === "NIL")
|
|
1048
|
-
|
|
1049
|
-
if (atom === "t" || atom === "T")
|
|
1050
|
-
return true;
|
|
978
|
+
if (atom === "nil" || atom === "NIL") return null;
|
|
979
|
+
if (atom === "t" || atom === "T") return true;
|
|
1051
980
|
const num = Number(atom);
|
|
1052
|
-
if (!isNaN(num) && atom !== "")
|
|
1053
|
-
return num;
|
|
981
|
+
if (!isNaN(num) && atom !== "") return num;
|
|
1054
982
|
return atom;
|
|
1055
983
|
}
|
|
1056
984
|
function parseList() {
|
|
@@ -1058,15 +986,13 @@ function parseLispRecursive(str) {
|
|
|
1058
986
|
const items = [];
|
|
1059
987
|
while (pos < str.length) {
|
|
1060
988
|
skipWs();
|
|
1061
|
-
if (pos >= str.length)
|
|
1062
|
-
break;
|
|
989
|
+
if (pos >= str.length) break;
|
|
1063
990
|
if (str[pos] === ")") {
|
|
1064
991
|
pos++;
|
|
1065
992
|
return items;
|
|
1066
993
|
}
|
|
1067
994
|
const left = parseValue();
|
|
1068
|
-
if (left === void 0)
|
|
1069
|
-
break;
|
|
995
|
+
if (left === void 0) break;
|
|
1070
996
|
skipWs();
|
|
1071
997
|
if (str[pos] === "." && (pos + 1 >= str.length || /\s/.test(str[pos + 1]) || str[pos + 1] === ")")) {
|
|
1072
998
|
pos++;
|
|
@@ -1087,8 +1013,7 @@ function parseLispRecursive(str) {
|
|
|
1087
1013
|
return parseValue();
|
|
1088
1014
|
}
|
|
1089
1015
|
function lispPairsToObject(pairs) {
|
|
1090
|
-
if (!Array.isArray(pairs))
|
|
1091
|
-
return pairs;
|
|
1016
|
+
if (!Array.isArray(pairs)) return pairs;
|
|
1092
1017
|
const obj = {};
|
|
1093
1018
|
for (let i = 0; i < pairs.length - 1; i += 2) {
|
|
1094
1019
|
const key = pairs[i];
|
|
@@ -1100,17 +1025,35 @@ function lispPairsToObject(pairs) {
|
|
|
1100
1025
|
return obj;
|
|
1101
1026
|
}
|
|
1102
1027
|
function parseEntity(arr) {
|
|
1103
|
-
if (!Array.isArray(arr) || arr.length < 5)
|
|
1104
|
-
|
|
1105
|
-
const
|
|
1106
|
-
|
|
1028
|
+
if (!Array.isArray(arr) || arr.length < 5) return null;
|
|
1029
|
+
const [type, handle, layer, color, linetype, ...rest] = arr;
|
|
1030
|
+
const entity = {
|
|
1031
|
+
type: String(type).toUpperCase().replace(/^"|"$/g, ""),
|
|
1032
|
+
handle: String(handle),
|
|
1033
|
+
layer: String(layer),
|
|
1034
|
+
color,
|
|
1035
|
+
linetype: String(linetype)
|
|
1036
|
+
};
|
|
1037
|
+
const entType = typeof entity.type === "string" ? entity.type.toUpperCase() : entity.type;
|
|
1038
|
+
if (/^(TEXT|MTEXT|ATTRIB|TCH_TEXT)$/.test(entType) && rest[0] != null) {
|
|
1039
|
+
entity.text = String(rest[0]);
|
|
1040
|
+
}
|
|
1041
|
+
if (/^(LINE|LWPOLYLINE|POLYLINE|ARC|CIRCLE|SPLINE|ELLIPSE|XLINE|RAY)$/.test(entType) && Array.isArray(rest[0])) {
|
|
1042
|
+
entity.points = rest[0].map((pt) => {
|
|
1043
|
+
if (Array.isArray(pt) && pt.length >= 2)
|
|
1044
|
+
return { x: Number(pt[0]), y: Number(pt[1]), z: Number(pt[2] || 0) };
|
|
1045
|
+
return null;
|
|
1046
|
+
}).filter(Boolean);
|
|
1047
|
+
}
|
|
1048
|
+
if (entType === "INSERT" && rest[0] != null) {
|
|
1049
|
+
entity.blockName = String(rest[0]);
|
|
1050
|
+
}
|
|
1051
|
+
return entity;
|
|
1107
1052
|
}
|
|
1108
1053
|
function buildEntityCode({ type, layer, bbox }) {
|
|
1109
1054
|
const items = [];
|
|
1110
|
-
if (type)
|
|
1111
|
-
|
|
1112
|
-
if (layer)
|
|
1113
|
-
items.push(`(cons 8 "${layer.replace(/"/g, '\\"')}")`);
|
|
1055
|
+
if (type) items.push(`(cons 0 "${type.replace(/"/g, '\\"')}")`);
|
|
1056
|
+
if (layer) items.push(`(cons 8 "${layer.replace(/"/g, '\\"')}")`);
|
|
1114
1057
|
if (bbox) {
|
|
1115
1058
|
const parts = bbox.split(",").map((s) => parseFloat(s.trim()));
|
|
1116
1059
|
if (parts.length === 4) {
|
|
@@ -1127,21 +1070,32 @@ function buildEntityCode({ type, layer, bbox }) {
|
|
|
1127
1070
|
const ssgetExpr = items.length ? `(ssget "_X" ${filterExpr})` : '(ssget "_X")';
|
|
1128
1071
|
return `(progn
|
|
1129
1072
|
(vl-load-com)
|
|
1130
|
-
(defun @e:
|
|
1131
|
-
(setq
|
|
1132
|
-
(
|
|
1133
|
-
|
|
1134
|
-
|
|
1073
|
+
(defun @e:safe-fn (f ent / r)
|
|
1074
|
+
(setq r (vl-catch-all-apply f (list ent)))
|
|
1075
|
+
(if (vl-catch-all-error-p r) nil r))
|
|
1076
|
+
(defun @e:dp (ent / ed typ gcl is)
|
|
1077
|
+
(setq ed (entget ent) typ (cdr (assoc 0 ed)) gcl (assoc 62 ed) is (assoc 6 ed))
|
|
1078
|
+
(append
|
|
1079
|
+
(list typ (cdr (assoc 5 ed)) (cdr (assoc 8 ed))
|
|
1080
|
+
(if gcl (cdr gcl) 256)
|
|
1081
|
+
(if is (cdr is) "ByLayer"))
|
|
1082
|
+
(cond
|
|
1083
|
+
((wcmatch typ "TEXT,MTEXT,ATTRIB,TCH_TEXT")
|
|
1084
|
+
(list (@e:safe-fn '(lambda (e) (text:remove-fmt (text:get-mtext e))) ent)))
|
|
1085
|
+
((wcmatch typ "LINE,LWPOLYLINE,POLYLINE,ARC,CIRCLE,SPLINE,ELLIPSE,XLINE,RAY")
|
|
1086
|
+
(list (@e:safe-fn 'curve:get-points ent)))
|
|
1087
|
+
((= typ "INSERT")
|
|
1088
|
+
(list (@e:safe-fn 'block:get-effectivename ent)))
|
|
1089
|
+
(t nil))))
|
|
1135
1090
|
(setq @e:total (if (setq @e:ss ${ssgetExpr}) (sslength @e:ss) 0)
|
|
1136
1091
|
@e:ents nil @e:i 0)
|
|
1137
|
-
(while (and @e:ss (< @e:i @e:total)(< (length @e:ents)
|
|
1092
|
+
(while (and @e:ss (< @e:i @e:total)(< (length @e:ents) 3000))
|
|
1138
1093
|
(setq @e:ents (cons (@e:dp (ssname @e:ss @e:i)) @e:ents) @e:i (1+ @e:i)))
|
|
1139
1094
|
(vl-prin1-to-string (list @e:total (reverse @e:ents))))`;
|
|
1140
1095
|
}
|
|
1141
1096
|
function buildTextCode({ layer, bbox, offset, limit }) {
|
|
1142
1097
|
const items = [`'(0 . "TEXT,MTEXT,ATTRIB,TCH_TEXT")`];
|
|
1143
|
-
if (layer)
|
|
1144
|
-
items.push(`(cons 8 "${layer.replace(/"/g, '\\"')}")`);
|
|
1098
|
+
if (layer) items.push(`(cons 8 "${layer.replace(/"/g, '\\"')}")`);
|
|
1145
1099
|
if (bbox) {
|
|
1146
1100
|
const parts = bbox.split(",").map((s) => parseFloat(s.trim()));
|
|
1147
1101
|
if (parts.length === 4) {
|
|
@@ -1177,31 +1131,16 @@ function buildTextCode({ layer, bbox, offset, limit }) {
|
|
|
1177
1131
|
(@t:run))
|
|
1178
1132
|
`;
|
|
1179
1133
|
}
|
|
1180
|
-
var RESOURCES = [
|
|
1181
|
-
{ uri: "atlisp://cad/info", name: "CAD Info", description: "CAD \u8FDE\u63A5\u4FE1\u606F", mimeType: "application/json", subscribable: true },
|
|
1182
|
-
{ uri: "atlisp://dwg/layers", name: "Layers", description: "\u56FE\u5C42\u5217\u8868", mimeType: "application/json", subscribable: true },
|
|
1183
|
-
{ uri: "atlisp://dwg/entities", name: "Entities", description: "\u56FE\u5143\u5217\u8868\uFF0C\u8FD4\u56DE\u603B\u6570\u548C\u524D100\u4E2A\u56FE\u5143\uFF08\u7C7B\u578B\u3001\u53E5\u67C4\u3001\u56FE\u5C42\u3001\u989C\u8272\u3001\u7EBF\u578B\uFF09\uFF0C\u652F\u6301 ?type=LINE&layer=0&bbox=0,0,100,100 \u8FC7\u6EE4", mimeType: "application/json", subscribable: true },
|
|
1184
|
-
{ uri: "atlisp://dwg/texts", name: "Texts", description: "\u6587\u672C\u5B9E\u4F53\u5185\u5BB9\u5217\u8868 (TEXT/MTEXT/ATTRIB/TCH_TEXT)\uFF0C\u652F\u6301 ?layer=0&bbox=0,0,100,100&limit=5000&offset=0", mimeType: "application/json", subscribable: true },
|
|
1185
|
-
{ uri: "atlisp://dwg/name", name: "DWG Name", description: "\u5F53\u524D DWG \u6587\u4EF6\u540D", mimeType: "application/json", subscribable: true },
|
|
1186
|
-
{ uri: "atlisp://dwg/path", name: "DWG Path", description: "\u6587\u4EF6\u5B8C\u6574\u8DEF\u5F84", mimeType: "application/json", subscribable: true },
|
|
1187
|
-
{ uri: "atlisp://cad/dwgs", name: "DWG List", description: "\u6240\u6709\u6253\u5F00\u7684 DWG \u6587\u4EF6\u5217\u8868", mimeType: "application/json", subscribable: true },
|
|
1188
|
-
{ uri: "atlisp://packages", name: "Packages", description: "\u5DF2\u5B89\u88C5\u5305\u5217\u8868", mimeType: "application/json", subscribable: true },
|
|
1189
|
-
{ uri: "atlisp://platforms", name: "Platforms", description: "\u652F\u6301\u7684\u5E73\u53F0\u4FE1\u606F", mimeType: "application/json", subscribable: false },
|
|
1190
|
-
{ uri: "atlisp://standards/drafting", name: "Drafting Standards", description: "CAD \u5236\u56FE\u89C4\u8303", mimeType: "application/json", subscribable: false },
|
|
1191
|
-
{ uri: "atlisp://standards/coding", name: "Coding Standards", description: "@lisp \u7F16\u7801\u89C4\u8303", mimeType: "application/json", subscribable: false }
|
|
1192
|
-
];
|
|
1193
1134
|
function parseQuery(uri) {
|
|
1194
1135
|
const qIdx = uri.indexOf("?");
|
|
1195
|
-
if (qIdx === -1)
|
|
1196
|
-
return { base: uri, params: {} };
|
|
1136
|
+
if (qIdx === -1) return { base: uri, params: {} };
|
|
1197
1137
|
const base = uri.substring(0, qIdx);
|
|
1198
1138
|
const params = {};
|
|
1199
1139
|
uri.substring(qIdx + 1).split("&").forEach((p) => {
|
|
1200
1140
|
const eqIdx = p.indexOf("=");
|
|
1201
1141
|
const k = eqIdx === -1 ? p : p.substring(0, eqIdx);
|
|
1202
1142
|
const v = eqIdx === -1 ? "" : p.substring(eqIdx + 1);
|
|
1203
|
-
if (k)
|
|
1204
|
-
params[k] = decodeURIComponent(v);
|
|
1143
|
+
if (k) params[k] = decodeURIComponent(v);
|
|
1205
1144
|
});
|
|
1206
1145
|
return { base, params };
|
|
1207
1146
|
}
|
|
@@ -1245,8 +1184,7 @@ async function readResource(uri) {
|
|
|
1245
1184
|
}
|
|
1246
1185
|
async function getCadInfo2() {
|
|
1247
1186
|
const cached = getCached("cad:info");
|
|
1248
|
-
if (cached)
|
|
1249
|
-
return cached;
|
|
1187
|
+
if (cached) return cached;
|
|
1250
1188
|
if (!cad.connected) {
|
|
1251
1189
|
await cad.connect();
|
|
1252
1190
|
}
|
|
@@ -1273,12 +1211,9 @@ async function getCadInfo2() {
|
|
|
1273
1211
|
async function getLayers(filterName = null) {
|
|
1274
1212
|
const cacheKey = filterName ? `dwg:layers:${filterName}` : "dwg:layers:all";
|
|
1275
1213
|
const cached = getCached(cacheKey);
|
|
1276
|
-
if (cached)
|
|
1277
|
-
|
|
1278
|
-
if (!cad.connected)
|
|
1279
|
-
await cad.connect();
|
|
1280
|
-
if (!cad.connected)
|
|
1281
|
-
return [];
|
|
1214
|
+
if (cached) return cached;
|
|
1215
|
+
if (!cad.connected) await cad.connect();
|
|
1216
|
+
if (!cad.connected) return [];
|
|
1282
1217
|
try {
|
|
1283
1218
|
const code = filterName ? `(vl-prin1-to-string (cdr (assoc 2 (tblnext "LAYER" T))))` : `(progn (setq res nil)(while (setq e (tblnext "LAYER" (null res)))(setq res (cons (list (vl-prin1-to-string (cdr (assoc 2 e))) (cdr (assoc 62 e)) (cdr (assoc 70 e))) res))) (reverse res))`;
|
|
1284
1219
|
const result = await cad.sendCommandWithResult(code);
|
|
@@ -1325,10 +1260,8 @@ async function getLayers(filterName = null) {
|
|
|
1325
1260
|
async function getEntities(params = {}) {
|
|
1326
1261
|
const cacheKey = `dwg:entities:${JSON.stringify(params)}`;
|
|
1327
1262
|
const cached = getCached(cacheKey);
|
|
1328
|
-
if (cached)
|
|
1329
|
-
|
|
1330
|
-
if (!cad.connected)
|
|
1331
|
-
await cad.connect();
|
|
1263
|
+
if (cached) return cached;
|
|
1264
|
+
if (!cad.connected) await cad.connect();
|
|
1332
1265
|
if (!cad.connected) {
|
|
1333
1266
|
const result = { total: 0, entities: [] };
|
|
1334
1267
|
setCache(cacheKey, result);
|
|
@@ -1368,10 +1301,8 @@ async function getEntities(params = {}) {
|
|
|
1368
1301
|
async function getTexts(params = {}) {
|
|
1369
1302
|
const cacheKey = `dwg:texts:${JSON.stringify(params)}`;
|
|
1370
1303
|
const cached = getCached(cacheKey);
|
|
1371
|
-
if (cached)
|
|
1372
|
-
|
|
1373
|
-
if (!cad.connected)
|
|
1374
|
-
await cad.connect();
|
|
1304
|
+
if (cached) return cached;
|
|
1305
|
+
if (!cad.connected) await cad.connect();
|
|
1375
1306
|
if (!cad.connected) {
|
|
1376
1307
|
const result = { total: 0, texts: [] };
|
|
1377
1308
|
setCache(cacheKey, result);
|
|
@@ -1409,10 +1340,8 @@ async function getTexts(params = {}) {
|
|
|
1409
1340
|
}
|
|
1410
1341
|
}
|
|
1411
1342
|
async function getDwgName() {
|
|
1412
|
-
if (!cad.connected)
|
|
1413
|
-
|
|
1414
|
-
if (!cad.connected)
|
|
1415
|
-
return { name: null };
|
|
1343
|
+
if (!cad.connected) await cad.connect();
|
|
1344
|
+
if (!cad.connected) return { name: null };
|
|
1416
1345
|
try {
|
|
1417
1346
|
const code = `(vl-princ-to-string (getvar "dwgname"))`;
|
|
1418
1347
|
const name = await cad.sendCommandWithResult(code);
|
|
@@ -1422,10 +1351,8 @@ async function getDwgName() {
|
|
|
1422
1351
|
}
|
|
1423
1352
|
}
|
|
1424
1353
|
async function getDwgPath() {
|
|
1425
|
-
if (!cad.connected)
|
|
1426
|
-
|
|
1427
|
-
if (!cad.connected)
|
|
1428
|
-
return { path: null, name: null };
|
|
1354
|
+
if (!cad.connected) await cad.connect();
|
|
1355
|
+
if (!cad.connected) return { path: null, name: null };
|
|
1429
1356
|
try {
|
|
1430
1357
|
const code = `(progn
|
|
1431
1358
|
(setq prefix (vl-princ-to-string (getvar "dwgprefix")))
|
|
@@ -1443,8 +1370,7 @@ async function getDwgPath() {
|
|
|
1443
1370
|
}
|
|
1444
1371
|
if (!parsed) {
|
|
1445
1372
|
const listResult = parseLispList(result);
|
|
1446
|
-
if (listResult && listResult.length >= 2)
|
|
1447
|
-
parsed = listResult;
|
|
1373
|
+
if (listResult && listResult.length >= 2) parsed = listResult;
|
|
1448
1374
|
}
|
|
1449
1375
|
if (parsed && Array.isArray(parsed) && parsed.length >= 2) {
|
|
1450
1376
|
return { path: String(parsed[0]), name: String(parsed[1]) };
|
|
@@ -1457,12 +1383,9 @@ async function getDwgPath() {
|
|
|
1457
1383
|
async function getPackages(filterName = null) {
|
|
1458
1384
|
const cacheKey = filterName ? `packages:${filterName}` : "packages:all";
|
|
1459
1385
|
const cached = getCached(cacheKey);
|
|
1460
|
-
if (cached)
|
|
1461
|
-
|
|
1462
|
-
if (!cad.connected)
|
|
1463
|
-
await cad.connect();
|
|
1464
|
-
if (!cad.connected)
|
|
1465
|
-
return [];
|
|
1386
|
+
if (cached) return cached;
|
|
1387
|
+
if (!cad.connected) await cad.connect();
|
|
1388
|
+
if (!cad.connected) return [];
|
|
1466
1389
|
try {
|
|
1467
1390
|
const code = `(mapcar 'cdr @::*local-pkgs*)`;
|
|
1468
1391
|
const result = await cad.sendCommandWithResult(code);
|
|
@@ -1476,8 +1399,7 @@ async function getPackages(filterName = null) {
|
|
|
1476
1399
|
} catch {
|
|
1477
1400
|
raw = parseLispList(result) || [];
|
|
1478
1401
|
}
|
|
1479
|
-
if (!Array.isArray(raw))
|
|
1480
|
-
raw = [];
|
|
1402
|
+
if (!Array.isArray(raw)) raw = [];
|
|
1481
1403
|
const packages = raw.map((item) => {
|
|
1482
1404
|
if (Array.isArray(item)) {
|
|
1483
1405
|
const pkg = { name: "", version: "0.0.0", description: "", loaded: true };
|
|
@@ -1486,20 +1408,13 @@ async function getPackages(filterName = null) {
|
|
|
1486
1408
|
const [[key, val]] = Object.entries(entry);
|
|
1487
1409
|
const k = String(key);
|
|
1488
1410
|
const v = String(val);
|
|
1489
|
-
if (k === ":NAME")
|
|
1490
|
-
|
|
1491
|
-
else if (k === ":
|
|
1492
|
-
|
|
1493
|
-
else if (k === ":
|
|
1494
|
-
|
|
1495
|
-
else if (k === ":
|
|
1496
|
-
pkg.fullName = v;
|
|
1497
|
-
else if (k === ":AUTHOR")
|
|
1498
|
-
pkg.author = v;
|
|
1499
|
-
else if (k === ":CATEGORY")
|
|
1500
|
-
pkg.category = v;
|
|
1501
|
-
else if (k === ":URL")
|
|
1502
|
-
pkg.url = v;
|
|
1411
|
+
if (k === ":NAME") pkg.name = v;
|
|
1412
|
+
else if (k === ":VERSION") pkg.version = v;
|
|
1413
|
+
else if (k === ":DESCRIPTION") pkg.description = v;
|
|
1414
|
+
else if (k === ":FULL-NAME") pkg.fullName = v;
|
|
1415
|
+
else if (k === ":AUTHOR") pkg.author = v;
|
|
1416
|
+
else if (k === ":CATEGORY") pkg.category = v;
|
|
1417
|
+
else if (k === ":URL") pkg.url = v;
|
|
1503
1418
|
}
|
|
1504
1419
|
}
|
|
1505
1420
|
return pkg;
|
|
@@ -1510,22 +1425,15 @@ async function getPackages(filterName = null) {
|
|
|
1510
1425
|
if (item && typeof item === "object") {
|
|
1511
1426
|
const pkg = { name: "", version: "0.0.0", description: "", loaded: true };
|
|
1512
1427
|
const nameVal = item.name || item.Name || item.NAME || item[":NAME"];
|
|
1513
|
-
if (nameVal)
|
|
1514
|
-
pkg.name = String(nameVal);
|
|
1428
|
+
if (nameVal) pkg.name = String(nameVal);
|
|
1515
1429
|
const verVal = item.version || item.Version || item.VERSION || item[":VERSION"];
|
|
1516
|
-
if (verVal)
|
|
1517
|
-
pkg.version = String(verVal);
|
|
1430
|
+
if (verVal) pkg.version = String(verVal);
|
|
1518
1431
|
const descVal = item.description || item.Description || item.DESCRIPTION || item[":DESCRIPTION"];
|
|
1519
|
-
if (descVal)
|
|
1520
|
-
|
|
1521
|
-
if (item.
|
|
1522
|
-
|
|
1523
|
-
if (item.
|
|
1524
|
-
pkg.author = String(item.author || item.Author);
|
|
1525
|
-
if (item.category || item.Category)
|
|
1526
|
-
pkg.category = String(item.category || item.Category);
|
|
1527
|
-
if (item.url || item.Url || item.URL)
|
|
1528
|
-
pkg.url = String(item.url || item.Url || item.URL);
|
|
1432
|
+
if (descVal) pkg.description = String(descVal);
|
|
1433
|
+
if (item.fullName || item.full_name) pkg.fullName = String(item.fullName || item.full_name);
|
|
1434
|
+
if (item.author || item.Author) pkg.author = String(item.author || item.Author);
|
|
1435
|
+
if (item.category || item.Category) pkg.category = String(item.category || item.Category);
|
|
1436
|
+
if (item.url || item.Url || item.URL) pkg.url = String(item.url || item.Url || item.URL);
|
|
1529
1437
|
return pkg;
|
|
1530
1438
|
}
|
|
1531
1439
|
return null;
|
|
@@ -1551,12 +1459,9 @@ function getPlatforms() {
|
|
|
1551
1459
|
async function getDwgList() {
|
|
1552
1460
|
const cacheKey = "cad:dwgs";
|
|
1553
1461
|
const cached = getCached(cacheKey);
|
|
1554
|
-
if (cached)
|
|
1555
|
-
|
|
1556
|
-
if (!cad.connected)
|
|
1557
|
-
await cad.connect();
|
|
1558
|
-
if (!cad.connected)
|
|
1559
|
-
return [];
|
|
1462
|
+
if (cached) return cached;
|
|
1463
|
+
if (!cad.connected) await cad.connect();
|
|
1464
|
+
if (!cad.connected) return [];
|
|
1560
1465
|
try {
|
|
1561
1466
|
const code = `(progn
|
|
1562
1467
|
(vl-load-com)
|
|
@@ -1580,8 +1485,7 @@ async function getDwgList() {
|
|
|
1580
1485
|
parsed = listResult;
|
|
1581
1486
|
}
|
|
1582
1487
|
}
|
|
1583
|
-
if (!Array.isArray(parsed))
|
|
1584
|
-
parsed = [];
|
|
1488
|
+
if (!Array.isArray(parsed)) parsed = [];
|
|
1585
1489
|
const dwgList = parsed.map((item) => {
|
|
1586
1490
|
if (Array.isArray(item) && item.length >= 2) {
|
|
1587
1491
|
return { name: String(item[0]), path: String(item[1]) };
|
|
@@ -1605,11 +1509,9 @@ function loadStandardsFile(filename) {
|
|
|
1605
1509
|
return null;
|
|
1606
1510
|
}
|
|
1607
1511
|
}
|
|
1608
|
-
var standardsCache = /* @__PURE__ */ new Map();
|
|
1609
1512
|
function getCachedStandards(key) {
|
|
1610
1513
|
const entry = standardsCache.get(key);
|
|
1611
|
-
if (entry && Date.now() - entry.timestamp < 3e4)
|
|
1612
|
-
return entry.data;
|
|
1514
|
+
if (entry && Date.now() - entry.timestamp < 3e4) return entry.data;
|
|
1613
1515
|
standardsCache.delete(key);
|
|
1614
1516
|
return null;
|
|
1615
1517
|
}
|
|
@@ -1618,32 +1520,50 @@ function setCachedStandards(key, data) {
|
|
|
1618
1520
|
}
|
|
1619
1521
|
function getDraftingStandards() {
|
|
1620
1522
|
const cached = getCachedStandards("drafting");
|
|
1621
|
-
if (cached)
|
|
1622
|
-
return cached;
|
|
1523
|
+
if (cached) return cached;
|
|
1623
1524
|
const data = loadStandardsFile("drafting.json");
|
|
1624
|
-
if (data)
|
|
1625
|
-
setCachedStandards("drafting", data);
|
|
1525
|
+
if (data) setCachedStandards("drafting", data);
|
|
1626
1526
|
return data || { error: "\u5236\u56FE\u89C4\u8303\u6587\u4EF6\u52A0\u8F7D\u5931\u8D25" };
|
|
1627
1527
|
}
|
|
1628
1528
|
function getCodingStandards() {
|
|
1629
1529
|
const cached = getCachedStandards("coding");
|
|
1630
|
-
if (cached)
|
|
1631
|
-
return cached;
|
|
1530
|
+
if (cached) return cached;
|
|
1632
1531
|
const data = loadStandardsFile("coding.json");
|
|
1633
|
-
if (data)
|
|
1634
|
-
setCachedStandards("coding", data);
|
|
1532
|
+
if (data) setCachedStandards("coding", data);
|
|
1635
1533
|
return data || { error: "\u7F16\u7801\u89C4\u8303\u6587\u4EF6\u52A0\u8F7D\u5931\u8D25" };
|
|
1636
1534
|
}
|
|
1535
|
+
var __dirname2, STANDARDS_DIR, CACHE_TTL2, resourceCache, RESOURCES, standardsCache;
|
|
1536
|
+
var init_resource_handlers = __esm({
|
|
1537
|
+
"src/handlers/resource-handlers.js"() {
|
|
1538
|
+
init_cad();
|
|
1539
|
+
init_constants();
|
|
1540
|
+
init_logger();
|
|
1541
|
+
init_config();
|
|
1542
|
+
__dirname2 = path4.dirname(fileURLToPath2(import.meta.url));
|
|
1543
|
+
STANDARDS_DIR = path4.join(__dirname2, "..", "standards");
|
|
1544
|
+
CACHE_TTL2 = config_default.resourceCacheTtl;
|
|
1545
|
+
resourceCache = /* @__PURE__ */ new Map();
|
|
1546
|
+
RESOURCES = [
|
|
1547
|
+
{ uri: "atlisp://cad/info", name: "CAD Info", description: "CAD \u8FDE\u63A5\u4FE1\u606F", mimeType: "application/json", subscribable: true },
|
|
1548
|
+
{ uri: "atlisp://dwg/layers", name: "Layers", description: "\u56FE\u5C42\u5217\u8868", mimeType: "application/json", subscribable: true },
|
|
1549
|
+
{ uri: "atlisp://dwg/entities", name: "Entities", description: "\u56FE\u5143\u5217\u8868\uFF0C\u8FD4\u56DE\u603B\u6570\u548C\u524D3000\u4E2A\u56FE\u5143\uFF0C\u6587\u672C\u9644\u52A0 text \u5B57\u6BB5\uFF0C\u66F2\u7EBF\u9644\u52A0 points \u6570\u7EC4\uFF0CINSERT \u9644\u52A0 blockName\uFF0C\u652F\u6301 ?type=LINE&layer=0&bbox=0,0,100,100 \u8FC7\u6EE4", mimeType: "application/json", subscribable: true },
|
|
1550
|
+
{ uri: "atlisp://dwg/texts", name: "Texts", description: "\u6587\u672C\u5B9E\u4F53\u5185\u5BB9\u5217\u8868 (TEXT/MTEXT/ATTRIB/TCH_TEXT)\uFF0C\u652F\u6301 ?layer=0&bbox=0,0,100,100&limit=5000&offset=0", mimeType: "application/json", subscribable: true },
|
|
1551
|
+
{ uri: "atlisp://dwg/name", name: "DWG Name", description: "\u5F53\u524D DWG \u6587\u4EF6\u540D", mimeType: "application/json", subscribable: true },
|
|
1552
|
+
{ uri: "atlisp://dwg/path", name: "DWG Path", description: "\u6587\u4EF6\u5B8C\u6574\u8DEF\u5F84", mimeType: "application/json", subscribable: true },
|
|
1553
|
+
{ uri: "atlisp://cad/dwgs", name: "DWG List", description: "\u6240\u6709\u6253\u5F00\u7684 DWG \u6587\u4EF6\u5217\u8868", mimeType: "application/json", subscribable: true },
|
|
1554
|
+
{ uri: "atlisp://packages", name: "Packages", description: "\u5DF2\u5B89\u88C5\u5305\u5217\u8868", mimeType: "application/json", subscribable: true },
|
|
1555
|
+
{ uri: "atlisp://platforms", name: "Platforms", description: "\u652F\u6301\u7684\u5E73\u53F0\u4FE1\u606F", mimeType: "application/json", subscribable: false },
|
|
1556
|
+
{ uri: "atlisp://standards/drafting", name: "Drafting Standards", description: "CAD \u5236\u56FE\u89C4\u8303", mimeType: "application/json", subscribable: false },
|
|
1557
|
+
{ uri: "atlisp://standards/coding", name: "Coding Standards", description: "@lisp \u7F16\u7801\u89C4\u8303", mimeType: "application/json", subscribable: false }
|
|
1558
|
+
];
|
|
1559
|
+
standardsCache = /* @__PURE__ */ new Map();
|
|
1560
|
+
}
|
|
1561
|
+
});
|
|
1637
1562
|
|
|
1638
1563
|
// src/handlers/prompt-handlers.js
|
|
1639
1564
|
import fs4 from "fs";
|
|
1640
1565
|
import path5 from "path";
|
|
1641
1566
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1642
|
-
var __dirname3 = path5.dirname(fileURLToPath3(import.meta.url));
|
|
1643
|
-
var PROMPTS_DIR = path5.join(__dirname3, "..", "prompts");
|
|
1644
|
-
var externalPrompts = null;
|
|
1645
|
-
var externalPromptsAt = 0;
|
|
1646
|
-
var EXTERNAL_PROMPTS_TTL = 3e4;
|
|
1647
1567
|
function loadExternalPrompts() {
|
|
1648
1568
|
const now = Date.now();
|
|
1649
1569
|
if (externalPrompts && now - externalPromptsAt < EXTERNAL_PROMPTS_TTL) {
|
|
@@ -1673,121 +1593,6 @@ function loadExternalPrompts() {
|
|
|
1673
1593
|
externalPromptsAt = now;
|
|
1674
1594
|
return externalPrompts;
|
|
1675
1595
|
}
|
|
1676
|
-
var PROMPTS = [
|
|
1677
|
-
{
|
|
1678
|
-
name: "analyze-drawing",
|
|
1679
|
-
description: "\u5206\u6790\u5F53\u524D\u56FE\u7EB8\u7EDF\u8BA1\u4FE1\u606F",
|
|
1680
|
-
arguments: []
|
|
1681
|
-
},
|
|
1682
|
-
{
|
|
1683
|
-
name: "batch-draw-lines",
|
|
1684
|
-
description: "\u6279\u91CF\u7ED8\u5236\u7EBF\u6BB5",
|
|
1685
|
-
arguments: [
|
|
1686
|
-
{ name: "count", description: "\u7EBF\u6BB5\u6570\u91CF", required: true },
|
|
1687
|
-
{ name: "length", description: "\u7EBF\u6BB5\u957F\u5EA6", required: false }
|
|
1688
|
-
]
|
|
1689
|
-
},
|
|
1690
|
-
{
|
|
1691
|
-
name: "generate-dimension",
|
|
1692
|
-
description: "\u751F\u6210\u5C3A\u5BF8\u6807\u6CE8",
|
|
1693
|
-
arguments: [
|
|
1694
|
-
{ name: "style", description: "\u6807\u6CE8\u6837\u5F0F (horizontal/vertical/aligned)", required: false }
|
|
1695
|
-
]
|
|
1696
|
-
},
|
|
1697
|
-
{
|
|
1698
|
-
name: "export-entities",
|
|
1699
|
-
description: "\u5BFC\u51FA\u56FE\u7EB8\u5B9E\u4F53\u7EDF\u8BA1",
|
|
1700
|
-
arguments: [
|
|
1701
|
-
{ name: "format", description: "\u5BFC\u51FA\u683C\u5F0F (json/list)", required: false }
|
|
1702
|
-
]
|
|
1703
|
-
},
|
|
1704
|
-
{
|
|
1705
|
-
name: "manage-layers",
|
|
1706
|
-
description: "\u56FE\u5C42\u7BA1\u7406\uFF1A\u521B\u5EFA\u3001\u51BB\u7ED3\u3001\u9501\u5B9A\u3001\u5220\u9664\u56FE\u5C42",
|
|
1707
|
-
arguments: [
|
|
1708
|
-
{ name: "action", description: "\u64CD\u4F5C\u7C7B\u578B (list/freeze/lock/off/make/delete)", required: true },
|
|
1709
|
-
{ name: "layers", description: "\u56FE\u5C42\u540D\uFF0C\u591A\u4E2A\u7528\u9017\u53F7\u5206\u9694", required: false },
|
|
1710
|
-
{ name: "color", description: '\u56FE\u5C42\u989C\u8272 (ACI\u53F7\u6216RGB\u5982 "1,2,3")', required: false }
|
|
1711
|
-
]
|
|
1712
|
-
},
|
|
1713
|
-
{
|
|
1714
|
-
name: "manage-blocks",
|
|
1715
|
-
description: "\u5757\u64CD\u4F5C\uFF1A\u63D2\u5165\u5757\u3001\u5217\u51FA\u5757\u3001\u83B7\u53D6/\u8BBE\u7F6E\u5C5E\u6027",
|
|
1716
|
-
arguments: [
|
|
1717
|
-
{ name: "action", description: "\u64CD\u4F5C\u7C7B\u578B (list/insert/attributes/dynprops)", required: true },
|
|
1718
|
-
{ name: "blockName", description: "\u5757\u540D", required: false },
|
|
1719
|
-
{ name: "insertPoint", description: '\u63D2\u5165\u70B9 "x,y"', required: false },
|
|
1720
|
-
{ name: "scale", description: "\u7F29\u653E\u6BD4\u4F8B", required: false }
|
|
1721
|
-
]
|
|
1722
|
-
},
|
|
1723
|
-
{
|
|
1724
|
-
name: "curve-analysis",
|
|
1725
|
-
description: "\u66F2\u7EBF\u5206\u6790\uFF1A\u4EA4\u70B9\u3001\u6700\u8FD1\u70B9\u3001\u5B50\u6BB5\u3001\u7F13\u51B2\u533A",
|
|
1726
|
-
arguments: [
|
|
1727
|
-
{ name: "action", description: "\u64CD\u4F5C (inters/subsegments/closestpoint/join)", required: true },
|
|
1728
|
-
{ name: "distance", description: "\u8FDE\u63A5\u5BB9\u5DEE/\u504F\u79FB\u8DDD\u79BB", required: false }
|
|
1729
|
-
]
|
|
1730
|
-
},
|
|
1731
|
-
{
|
|
1732
|
-
name: "geometry-calc",
|
|
1733
|
-
description: "\u51E0\u4F55\u8BA1\u7B97\uFF1A\u51F8\u5305\u3001\u70B9\u5230\u7EBF\u8DDD\u79BB\u3001\u9762\u5185\u6D4B\u8BD5\u3001\u53D8\u6362",
|
|
1734
|
-
arguments: [
|
|
1735
|
-
{ name: "action", description: "\u64CD\u4F5C (convexhull/dist-point-line/in-curve-p/transform/centroid)", required: true },
|
|
1736
|
-
{ name: "points", description: '\u70B9\u96C6 "x1,y1;x2,y2;..."', required: false }
|
|
1737
|
-
]
|
|
1738
|
-
},
|
|
1739
|
-
{
|
|
1740
|
-
name: "text-process",
|
|
1741
|
-
description: "\u6587\u672C\u5904\u7406\uFF1AMTEXT\u89E3\u6790\u3001Markdown\u8F6CMTEXT\u3001\u53BB\u683C\u5F0F",
|
|
1742
|
-
arguments: [
|
|
1743
|
-
{ name: "action", description: "\u64CD\u4F5C (parse-mtext/remove-fmt/from-markdown/mtext2text)", required: true },
|
|
1744
|
-
{ name: "markdown", description: "Markdown\u6587\u672C\uFF08from-markdown\u64CD\u4F5C\u65F6\u7528\uFF09", required: false }
|
|
1745
|
-
]
|
|
1746
|
-
},
|
|
1747
|
-
{
|
|
1748
|
-
name: "pickset-filter",
|
|
1749
|
-
description: "\u9009\u62E9\u96C6\u64CD\u4F5C\uFF1A\u8FC7\u6EE4\u3001\u8F6C\u6362\u3001\u6279\u91CF\u64CD\u4F5C",
|
|
1750
|
-
arguments: [
|
|
1751
|
-
{ name: "action", description: "\u64CD\u4F5C (by-layer/by-type/by-box/erase/zoom)", required: true },
|
|
1752
|
-
{ name: "dxfType", description: 'DXF\u7C7B\u578B "LINE, CIRCLE, LWPOLYLINE, INSERT, TEXT"', required: false },
|
|
1753
|
-
{ name: "layerName", description: "\u56FE\u5C42\u540D", required: false }
|
|
1754
|
-
]
|
|
1755
|
-
},
|
|
1756
|
-
{
|
|
1757
|
-
name: "color-convert",
|
|
1758
|
-
description: "\u989C\u8272\u8F6C\u6362\uFF1AACI/RGB/TrueColor/CSS\u4E92\u8F6C",
|
|
1759
|
-
arguments: [
|
|
1760
|
-
{ name: "action", description: "\u64CD\u4F5C (aci2rgb/rgb2css/truecolor2rgb/rgb2truecolor)", required: true },
|
|
1761
|
-
{ name: "value", description: "\u989C\u8272\u503C", required: true }
|
|
1762
|
-
]
|
|
1763
|
-
},
|
|
1764
|
-
{
|
|
1765
|
-
name: "json-exchange",
|
|
1766
|
-
description: "JSON\u6570\u636E\u4EA4\u6362\uFF1A\u4ECE\u5B57\u7B26\u4E32\u89E3\u6790JSON alist\uFF0C\u4ECEalist\u751F\u6210JSON",
|
|
1767
|
-
arguments: [
|
|
1768
|
-
{ name: "action", description: "\u64CD\u4F5C (encode/decode)", required: true },
|
|
1769
|
-
{ name: "data", description: "JSON\u5B57\u7B26\u4E32\u6216Lisp alist\u6587\u672C", required: true }
|
|
1770
|
-
]
|
|
1771
|
-
},
|
|
1772
|
-
{
|
|
1773
|
-
name: "excel-report",
|
|
1774
|
-
description: "Excel\u62A5\u8868\uFF1A\u5BFC\u51FACAD\u5B9E\u4F53\u6570\u636E\u5230Excel",
|
|
1775
|
-
arguments: [
|
|
1776
|
-
{ name: "visible", description: "\u662F\u5426\u663E\u793AExcel\u7A97\u53E3 (true/false)", required: false },
|
|
1777
|
-
{ name: "savePath", description: "\u4FDD\u5B58\u8DEF\u5F84\uFF0C\u4E0D\u586B\u5219\u4E0D\u4FDD\u5B58", required: false }
|
|
1778
|
-
]
|
|
1779
|
-
},
|
|
1780
|
-
{
|
|
1781
|
-
name: "string-process",
|
|
1782
|
-
description: "\u5B57\u7B26\u4E32\u5904\u7406\uFF1A\u683C\u5F0F\u5316\u3001\u62C6\u5206\u3001\u66FF\u6362\u3001\u6B63\u5219",
|
|
1783
|
-
arguments: [
|
|
1784
|
-
{ name: "action", description: "\u64CD\u4F5C (format/split/subst-all/regexp/trim/number2chinese)", required: true },
|
|
1785
|
-
{ name: "input", description: "\u8F93\u5165\u5B57\u7B26\u4E32", required: true },
|
|
1786
|
-
{ name: "arg1", description: "\u53C2\u65701\uFF08\u683C\u5F0F\u53C2\u6570/\u5206\u9694\u7B26/\u65E7\u4E32\uFF09", required: false },
|
|
1787
|
-
{ name: "arg2", description: "\u53C2\u65702\uFF08\u65B0\u4E32/\u6B63\u5219\u6A21\u5F0F\uFF09", required: false }
|
|
1788
|
-
]
|
|
1789
|
-
}
|
|
1790
|
-
];
|
|
1791
1596
|
function findExternalPrompt(name) {
|
|
1792
1597
|
const ext = loadExternalPrompts();
|
|
1793
1598
|
return ext.find((p) => p.name === name);
|
|
@@ -1885,14 +1690,10 @@ ${generateSuggestions(entities, layers)}`
|
|
|
1885
1690
|
}
|
|
1886
1691
|
function generateSuggestions(entities, layers) {
|
|
1887
1692
|
const suggestions = [];
|
|
1888
|
-
if (entities.total > 1e3)
|
|
1889
|
-
|
|
1890
|
-
if (
|
|
1891
|
-
|
|
1892
|
-
if (entities.byType.LINE && entities.byType.LINE > 500)
|
|
1893
|
-
suggestions.push("- \u7EBF\u6761\u8F83\u591A\uFF0C\u53EF\u8003\u8651\u4F7F\u7528 BLOCK \u51CF\u5C11\u5B9E\u4F53\u6570\u91CF");
|
|
1894
|
-
if (!entities.byType.DIMENSION)
|
|
1895
|
-
suggestions.push("- \u672A\u53D1\u73B0\u5C3A\u5BF8\u6807\u6CE8\uFF0C\u5EFA\u8BAE\u6DFB\u52A0\u6807\u6CE8");
|
|
1693
|
+
if (entities.total > 1e3) suggestions.push("- \u56FE\u7EB8\u5B9E\u4F53\u8F83\u591A\uFF0C\u5EFA\u8BAE\u4F7F\u7528\u56FE\u5C42\u5206\u7C7B\u7BA1\u7406");
|
|
1694
|
+
if (layers.length > 20) suggestions.push("- \u56FE\u5C42\u8F83\u591A\uFF0C\u5EFA\u8BAE\u6E05\u7406\u672A\u4F7F\u7528\u7684\u56FE\u5C42");
|
|
1695
|
+
if (entities.byType.LINE && entities.byType.LINE > 500) suggestions.push("- \u7EBF\u6761\u8F83\u591A\uFF0C\u53EF\u8003\u8651\u4F7F\u7528 BLOCK \u51CF\u5C11\u5B9E\u4F53\u6570\u91CF");
|
|
1696
|
+
if (!entities.byType.DIMENSION) suggestions.push("- \u672A\u53D1\u73B0\u5C3A\u5BF8\u6807\u6CE8\uFF0C\u5EFA\u8BAE\u6DFB\u52A0\u6807\u6CE8");
|
|
1896
1697
|
return suggestions.length ? suggestions.join("\n") : "- \u56FE\u7EB8\u7ED3\u6784\u826F\u597D";
|
|
1897
1698
|
}
|
|
1898
1699
|
async function generateBatchLinesPrompt(args) {
|
|
@@ -2408,86 +2209,215 @@ ${code}
|
|
|
2408
2209
|
}]
|
|
2409
2210
|
};
|
|
2410
2211
|
}
|
|
2212
|
+
var __dirname3, PROMPTS_DIR, externalPrompts, externalPromptsAt, EXTERNAL_PROMPTS_TTL, PROMPTS;
|
|
2213
|
+
var init_prompt_handlers = __esm({
|
|
2214
|
+
"src/handlers/prompt-handlers.js"() {
|
|
2215
|
+
init_cad();
|
|
2216
|
+
__dirname3 = path5.dirname(fileURLToPath3(import.meta.url));
|
|
2217
|
+
PROMPTS_DIR = path5.join(__dirname3, "..", "prompts");
|
|
2218
|
+
externalPrompts = null;
|
|
2219
|
+
externalPromptsAt = 0;
|
|
2220
|
+
EXTERNAL_PROMPTS_TTL = 3e4;
|
|
2221
|
+
PROMPTS = [
|
|
2222
|
+
{
|
|
2223
|
+
name: "analyze-drawing",
|
|
2224
|
+
description: "\u5206\u6790\u5F53\u524D\u56FE\u7EB8\u7EDF\u8BA1\u4FE1\u606F",
|
|
2225
|
+
arguments: []
|
|
2226
|
+
},
|
|
2227
|
+
{
|
|
2228
|
+
name: "batch-draw-lines",
|
|
2229
|
+
description: "\u6279\u91CF\u7ED8\u5236\u7EBF\u6BB5",
|
|
2230
|
+
arguments: [
|
|
2231
|
+
{ name: "count", description: "\u7EBF\u6BB5\u6570\u91CF", required: true },
|
|
2232
|
+
{ name: "length", description: "\u7EBF\u6BB5\u957F\u5EA6", required: false }
|
|
2233
|
+
]
|
|
2234
|
+
},
|
|
2235
|
+
{
|
|
2236
|
+
name: "generate-dimension",
|
|
2237
|
+
description: "\u751F\u6210\u5C3A\u5BF8\u6807\u6CE8",
|
|
2238
|
+
arguments: [
|
|
2239
|
+
{ name: "style", description: "\u6807\u6CE8\u6837\u5F0F (horizontal/vertical/aligned)", required: false }
|
|
2240
|
+
]
|
|
2241
|
+
},
|
|
2242
|
+
{
|
|
2243
|
+
name: "export-entities",
|
|
2244
|
+
description: "\u5BFC\u51FA\u56FE\u7EB8\u5B9E\u4F53\u7EDF\u8BA1",
|
|
2245
|
+
arguments: [
|
|
2246
|
+
{ name: "format", description: "\u5BFC\u51FA\u683C\u5F0F (json/list)", required: false }
|
|
2247
|
+
]
|
|
2248
|
+
},
|
|
2249
|
+
{
|
|
2250
|
+
name: "manage-layers",
|
|
2251
|
+
description: "\u56FE\u5C42\u7BA1\u7406\uFF1A\u521B\u5EFA\u3001\u51BB\u7ED3\u3001\u9501\u5B9A\u3001\u5220\u9664\u56FE\u5C42",
|
|
2252
|
+
arguments: [
|
|
2253
|
+
{ name: "action", description: "\u64CD\u4F5C\u7C7B\u578B (list/freeze/lock/off/make/delete)", required: true },
|
|
2254
|
+
{ name: "layers", description: "\u56FE\u5C42\u540D\uFF0C\u591A\u4E2A\u7528\u9017\u53F7\u5206\u9694", required: false },
|
|
2255
|
+
{ name: "color", description: '\u56FE\u5C42\u989C\u8272 (ACI\u53F7\u6216RGB\u5982 "1,2,3")', required: false }
|
|
2256
|
+
]
|
|
2257
|
+
},
|
|
2258
|
+
{
|
|
2259
|
+
name: "manage-blocks",
|
|
2260
|
+
description: "\u5757\u64CD\u4F5C\uFF1A\u63D2\u5165\u5757\u3001\u5217\u51FA\u5757\u3001\u83B7\u53D6/\u8BBE\u7F6E\u5C5E\u6027",
|
|
2261
|
+
arguments: [
|
|
2262
|
+
{ name: "action", description: "\u64CD\u4F5C\u7C7B\u578B (list/insert/attributes/dynprops)", required: true },
|
|
2263
|
+
{ name: "blockName", description: "\u5757\u540D", required: false },
|
|
2264
|
+
{ name: "insertPoint", description: '\u63D2\u5165\u70B9 "x,y"', required: false },
|
|
2265
|
+
{ name: "scale", description: "\u7F29\u653E\u6BD4\u4F8B", required: false }
|
|
2266
|
+
]
|
|
2267
|
+
},
|
|
2268
|
+
{
|
|
2269
|
+
name: "curve-analysis",
|
|
2270
|
+
description: "\u66F2\u7EBF\u5206\u6790\uFF1A\u4EA4\u70B9\u3001\u6700\u8FD1\u70B9\u3001\u5B50\u6BB5\u3001\u7F13\u51B2\u533A",
|
|
2271
|
+
arguments: [
|
|
2272
|
+
{ name: "action", description: "\u64CD\u4F5C (inters/subsegments/closestpoint/join)", required: true },
|
|
2273
|
+
{ name: "distance", description: "\u8FDE\u63A5\u5BB9\u5DEE/\u504F\u79FB\u8DDD\u79BB", required: false }
|
|
2274
|
+
]
|
|
2275
|
+
},
|
|
2276
|
+
{
|
|
2277
|
+
name: "geometry-calc",
|
|
2278
|
+
description: "\u51E0\u4F55\u8BA1\u7B97\uFF1A\u51F8\u5305\u3001\u70B9\u5230\u7EBF\u8DDD\u79BB\u3001\u9762\u5185\u6D4B\u8BD5\u3001\u53D8\u6362",
|
|
2279
|
+
arguments: [
|
|
2280
|
+
{ name: "action", description: "\u64CD\u4F5C (convexhull/dist-point-line/in-curve-p/transform/centroid)", required: true },
|
|
2281
|
+
{ name: "points", description: '\u70B9\u96C6 "x1,y1;x2,y2;..."', required: false }
|
|
2282
|
+
]
|
|
2283
|
+
},
|
|
2284
|
+
{
|
|
2285
|
+
name: "text-process",
|
|
2286
|
+
description: "\u6587\u672C\u5904\u7406\uFF1AMTEXT\u89E3\u6790\u3001Markdown\u8F6CMTEXT\u3001\u53BB\u683C\u5F0F",
|
|
2287
|
+
arguments: [
|
|
2288
|
+
{ name: "action", description: "\u64CD\u4F5C (parse-mtext/remove-fmt/from-markdown/mtext2text)", required: true },
|
|
2289
|
+
{ name: "markdown", description: "Markdown\u6587\u672C\uFF08from-markdown\u64CD\u4F5C\u65F6\u7528\uFF09", required: false }
|
|
2290
|
+
]
|
|
2291
|
+
},
|
|
2292
|
+
{
|
|
2293
|
+
name: "pickset-filter",
|
|
2294
|
+
description: "\u9009\u62E9\u96C6\u64CD\u4F5C\uFF1A\u8FC7\u6EE4\u3001\u8F6C\u6362\u3001\u6279\u91CF\u64CD\u4F5C",
|
|
2295
|
+
arguments: [
|
|
2296
|
+
{ name: "action", description: "\u64CD\u4F5C (by-layer/by-type/by-box/erase/zoom)", required: true },
|
|
2297
|
+
{ name: "dxfType", description: 'DXF\u7C7B\u578B "LINE, CIRCLE, LWPOLYLINE, INSERT, TEXT"', required: false },
|
|
2298
|
+
{ name: "layerName", description: "\u56FE\u5C42\u540D", required: false }
|
|
2299
|
+
]
|
|
2300
|
+
},
|
|
2301
|
+
{
|
|
2302
|
+
name: "color-convert",
|
|
2303
|
+
description: "\u989C\u8272\u8F6C\u6362\uFF1AACI/RGB/TrueColor/CSS\u4E92\u8F6C",
|
|
2304
|
+
arguments: [
|
|
2305
|
+
{ name: "action", description: "\u64CD\u4F5C (aci2rgb/rgb2css/truecolor2rgb/rgb2truecolor)", required: true },
|
|
2306
|
+
{ name: "value", description: "\u989C\u8272\u503C", required: true }
|
|
2307
|
+
]
|
|
2308
|
+
},
|
|
2309
|
+
{
|
|
2310
|
+
name: "json-exchange",
|
|
2311
|
+
description: "JSON\u6570\u636E\u4EA4\u6362\uFF1A\u4ECE\u5B57\u7B26\u4E32\u89E3\u6790JSON alist\uFF0C\u4ECEalist\u751F\u6210JSON",
|
|
2312
|
+
arguments: [
|
|
2313
|
+
{ name: "action", description: "\u64CD\u4F5C (encode/decode)", required: true },
|
|
2314
|
+
{ name: "data", description: "JSON\u5B57\u7B26\u4E32\u6216Lisp alist\u6587\u672C", required: true }
|
|
2315
|
+
]
|
|
2316
|
+
},
|
|
2317
|
+
{
|
|
2318
|
+
name: "excel-report",
|
|
2319
|
+
description: "Excel\u62A5\u8868\uFF1A\u5BFC\u51FACAD\u5B9E\u4F53\u6570\u636E\u5230Excel",
|
|
2320
|
+
arguments: [
|
|
2321
|
+
{ name: "visible", description: "\u662F\u5426\u663E\u793AExcel\u7A97\u53E3 (true/false)", required: false },
|
|
2322
|
+
{ name: "savePath", description: "\u4FDD\u5B58\u8DEF\u5F84\uFF0C\u4E0D\u586B\u5219\u4E0D\u4FDD\u5B58", required: false }
|
|
2323
|
+
]
|
|
2324
|
+
},
|
|
2325
|
+
{
|
|
2326
|
+
name: "string-process",
|
|
2327
|
+
description: "\u5B57\u7B26\u4E32\u5904\u7406\uFF1A\u683C\u5F0F\u5316\u3001\u62C6\u5206\u3001\u66FF\u6362\u3001\u6B63\u5219",
|
|
2328
|
+
arguments: [
|
|
2329
|
+
{ name: "action", description: "\u64CD\u4F5C (format/split/subst-all/regexp/trim/number2chinese)", required: true },
|
|
2330
|
+
{ name: "input", description: "\u8F93\u5165\u5B57\u7B26\u4E32", required: true },
|
|
2331
|
+
{ name: "arg1", description: "\u53C2\u65701\uFF08\u683C\u5F0F\u53C2\u6570/\u5206\u9694\u7B26/\u65E7\u4E32\uFF09", required: false },
|
|
2332
|
+
{ name: "arg2", description: "\u53C2\u65702\uFF08\u65B0\u4E32/\u6B63\u5219\u6A21\u5F0F\uFF09", required: false }
|
|
2333
|
+
]
|
|
2334
|
+
}
|
|
2335
|
+
];
|
|
2336
|
+
}
|
|
2337
|
+
});
|
|
2411
2338
|
|
|
2412
2339
|
// src/session-manager.js
|
|
2413
|
-
var Session
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
#sessions = /* @__PURE__ */ new Map();
|
|
2423
|
-
createSession(clientId) {
|
|
2424
|
-
let session = this.#sessions.get(clientId);
|
|
2425
|
-
if (!session) {
|
|
2426
|
-
session = new Session(clientId);
|
|
2427
|
-
this.#sessions.set(clientId, session);
|
|
2428
|
-
}
|
|
2429
|
-
return session;
|
|
2430
|
-
}
|
|
2431
|
-
getSession(clientId) {
|
|
2432
|
-
return this.#sessions.get(clientId) || null;
|
|
2433
|
-
}
|
|
2434
|
-
hasSession(clientId) {
|
|
2435
|
-
return this.#sessions.has(clientId);
|
|
2436
|
-
}
|
|
2437
|
-
removeSession(clientId) {
|
|
2438
|
-
return this.#sessions.delete(clientId);
|
|
2439
|
-
}
|
|
2440
|
-
subscribe(clientId, uri) {
|
|
2441
|
-
const session = this.#sessions.get(clientId);
|
|
2442
|
-
if (session) {
|
|
2443
|
-
session.subscriptions.add(uri);
|
|
2444
|
-
return true;
|
|
2445
|
-
}
|
|
2446
|
-
return false;
|
|
2447
|
-
}
|
|
2448
|
-
unsubscribe(clientId, uri) {
|
|
2449
|
-
const session = this.#sessions.get(clientId);
|
|
2450
|
-
if (session) {
|
|
2451
|
-
return session.subscriptions.delete(uri);
|
|
2452
|
-
}
|
|
2453
|
-
return false;
|
|
2454
|
-
}
|
|
2455
|
-
getSubscriptions(clientId) {
|
|
2456
|
-
const session = this.#sessions.get(clientId);
|
|
2457
|
-
return session ? Array.from(session.subscriptions) : [];
|
|
2458
|
-
}
|
|
2459
|
-
isSubscribed(clientId, uri) {
|
|
2460
|
-
const session = this.#sessions.get(clientId);
|
|
2461
|
-
return session ? session.subscriptions.has(uri) : false;
|
|
2462
|
-
}
|
|
2463
|
-
getAllSubscribedUris() {
|
|
2464
|
-
const all = /* @__PURE__ */ new Set();
|
|
2465
|
-
for (const session of this.#sessions.values()) {
|
|
2466
|
-
for (const uri of session.subscriptions) {
|
|
2467
|
-
all.add(uri);
|
|
2340
|
+
var Session, SessionManager;
|
|
2341
|
+
var init_session_manager = __esm({
|
|
2342
|
+
"src/session-manager.js"() {
|
|
2343
|
+
Session = class {
|
|
2344
|
+
constructor(clientId) {
|
|
2345
|
+
this.clientId = clientId;
|
|
2346
|
+
this.subscriptions = /* @__PURE__ */ new Set();
|
|
2347
|
+
this.createdAt = Date.now();
|
|
2348
|
+
this.connected = false;
|
|
2468
2349
|
}
|
|
2469
|
-
}
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2350
|
+
};
|
|
2351
|
+
SessionManager = class {
|
|
2352
|
+
#sessions = /* @__PURE__ */ new Map();
|
|
2353
|
+
createSession(clientId) {
|
|
2354
|
+
let session = this.#sessions.get(clientId);
|
|
2355
|
+
if (!session) {
|
|
2356
|
+
session = new Session(clientId);
|
|
2357
|
+
this.#sessions.set(clientId, session);
|
|
2358
|
+
}
|
|
2359
|
+
return session;
|
|
2360
|
+
}
|
|
2361
|
+
getSession(clientId) {
|
|
2362
|
+
return this.#sessions.get(clientId) || null;
|
|
2363
|
+
}
|
|
2364
|
+
hasSession(clientId) {
|
|
2365
|
+
return this.#sessions.has(clientId);
|
|
2366
|
+
}
|
|
2367
|
+
removeSession(clientId) {
|
|
2368
|
+
return this.#sessions.delete(clientId);
|
|
2369
|
+
}
|
|
2370
|
+
subscribe(clientId, uri) {
|
|
2371
|
+
const session = this.#sessions.get(clientId);
|
|
2372
|
+
if (session) {
|
|
2373
|
+
session.subscriptions.add(uri);
|
|
2374
|
+
return true;
|
|
2375
|
+
}
|
|
2376
|
+
return false;
|
|
2377
|
+
}
|
|
2378
|
+
unsubscribe(clientId, uri) {
|
|
2379
|
+
const session = this.#sessions.get(clientId);
|
|
2380
|
+
if (session) {
|
|
2381
|
+
return session.subscriptions.delete(uri);
|
|
2382
|
+
}
|
|
2383
|
+
return false;
|
|
2384
|
+
}
|
|
2385
|
+
getSubscriptions(clientId) {
|
|
2386
|
+
const session = this.#sessions.get(clientId);
|
|
2387
|
+
return session ? Array.from(session.subscriptions) : [];
|
|
2388
|
+
}
|
|
2389
|
+
isSubscribed(clientId, uri) {
|
|
2390
|
+
const session = this.#sessions.get(clientId);
|
|
2391
|
+
return session ? session.subscriptions.has(uri) : false;
|
|
2392
|
+
}
|
|
2393
|
+
getAllSubscribedUris() {
|
|
2394
|
+
const all = /* @__PURE__ */ new Set();
|
|
2395
|
+
for (const session of this.#sessions.values()) {
|
|
2396
|
+
for (const uri of session.subscriptions) {
|
|
2397
|
+
all.add(uri);
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
return all;
|
|
2401
|
+
}
|
|
2402
|
+
list() {
|
|
2403
|
+
return Array.from(this.#sessions.entries()).map(([id, s]) => ({
|
|
2404
|
+
clientId: id,
|
|
2405
|
+
subscriptions: Array.from(s.subscriptions),
|
|
2406
|
+
createdAt: s.createdAt,
|
|
2407
|
+
connected: s.connected
|
|
2408
|
+
}));
|
|
2409
|
+
}
|
|
2410
|
+
count() {
|
|
2411
|
+
return this.#sessions.size;
|
|
2412
|
+
}
|
|
2413
|
+
clear() {
|
|
2414
|
+
this.#sessions.clear();
|
|
2415
|
+
}
|
|
2416
|
+
};
|
|
2485
2417
|
}
|
|
2486
|
-
};
|
|
2418
|
+
});
|
|
2487
2419
|
|
|
2488
2420
|
// src/subscription-manager.js
|
|
2489
|
-
var sessionManager = new SessionManager();
|
|
2490
|
-
var _notifyFn = null;
|
|
2491
2421
|
function setNotify(fn) {
|
|
2492
2422
|
_notifyFn = fn;
|
|
2493
2423
|
}
|
|
@@ -2520,242 +2450,250 @@ function notify(uri, data) {
|
|
|
2520
2450
|
_notifyFn(uri, data);
|
|
2521
2451
|
}
|
|
2522
2452
|
}
|
|
2523
|
-
|
|
2453
|
+
var sessionManager, _notifyFn;
|
|
2454
|
+
var init_subscription_manager = __esm({
|
|
2455
|
+
"src/subscription-manager.js"() {
|
|
2456
|
+
init_session_manager();
|
|
2457
|
+
sessionManager = new SessionManager();
|
|
2458
|
+
_notifyFn = null;
|
|
2459
|
+
}
|
|
2460
|
+
});
|
|
2461
|
+
|
|
2524
2462
|
// src/sse-session.js
|
|
2525
|
-
import { randomUUID as
|
|
2526
|
-
var DEFAULT_HEARTBEAT_INTERVAL
|
|
2527
|
-
var
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2463
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
2464
|
+
var DEFAULT_HEARTBEAT_INTERVAL, DEFAULT_RETRY_INTERVAL, MAX_SENT_MESSAGES, SSE_EVENTS, SseSession;
|
|
2465
|
+
var init_sse_session = __esm({
|
|
2466
|
+
"src/sse-session.js"() {
|
|
2467
|
+
DEFAULT_HEARTBEAT_INTERVAL = 3e4;
|
|
2468
|
+
DEFAULT_RETRY_INTERVAL = 3e4;
|
|
2469
|
+
MAX_SENT_MESSAGES = 1e3;
|
|
2470
|
+
SSE_EVENTS = {
|
|
2471
|
+
MESSAGE: "message",
|
|
2472
|
+
ENDPOINT: "endpoint",
|
|
2473
|
+
PING: "ping",
|
|
2474
|
+
ERROR: "error",
|
|
2475
|
+
PROGRESS: "progress",
|
|
2476
|
+
CAPABILITIES: "capabilities"
|
|
2477
|
+
};
|
|
2478
|
+
SseSession = class {
|
|
2479
|
+
#res;
|
|
2480
|
+
#clientId;
|
|
2481
|
+
#active = true;
|
|
2482
|
+
#heartbeatTimer = null;
|
|
2483
|
+
#lastEventId = 0;
|
|
2484
|
+
#heartbeatInterval;
|
|
2485
|
+
#retryInterval;
|
|
2486
|
+
#sessionData = {};
|
|
2487
|
+
#sentMessages = [];
|
|
2488
|
+
#backpressureBuffer = [];
|
|
2489
|
+
#drainHandler = null;
|
|
2490
|
+
constructor(res, options = {}) {
|
|
2491
|
+
this.#res = res;
|
|
2492
|
+
this.#clientId = options.clientId || randomUUID2();
|
|
2493
|
+
this.#heartbeatInterval = options.heartbeatInterval || DEFAULT_HEARTBEAT_INTERVAL;
|
|
2494
|
+
this.#retryInterval = options.retryInterval || DEFAULT_RETRY_INTERVAL;
|
|
2495
|
+
this.#sessionData = options.sessionData || {};
|
|
2496
|
+
if (options.resumeFromId && options.resumeFromId > 0) {
|
|
2497
|
+
this.#lastEventId = options.resumeFromId;
|
|
2498
|
+
}
|
|
2499
|
+
this._setupSseHeaders();
|
|
2500
|
+
this._sendRetry();
|
|
2501
|
+
this._setupDrain();
|
|
2502
|
+
}
|
|
2503
|
+
get clientId() {
|
|
2504
|
+
return this.#clientId;
|
|
2505
|
+
}
|
|
2506
|
+
get isActive() {
|
|
2507
|
+
return this.#active;
|
|
2508
|
+
}
|
|
2509
|
+
get lastEventId() {
|
|
2510
|
+
return this.#lastEventId;
|
|
2511
|
+
}
|
|
2512
|
+
get sessionData() {
|
|
2513
|
+
return this.#sessionData;
|
|
2514
|
+
}
|
|
2515
|
+
get sentMessages() {
|
|
2516
|
+
return [...this.#sentMessages];
|
|
2517
|
+
}
|
|
2518
|
+
getMessagesSince(eventId) {
|
|
2519
|
+
return this.#sentMessages.filter((m) => m.id > eventId);
|
|
2520
|
+
}
|
|
2521
|
+
setSessionData(key, value) {
|
|
2522
|
+
this.#sessionData[key] = value;
|
|
2523
|
+
}
|
|
2524
|
+
getSessionData(key) {
|
|
2525
|
+
return this.#sessionData[key];
|
|
2526
|
+
}
|
|
2527
|
+
get retryInterval() {
|
|
2528
|
+
return this.#retryInterval;
|
|
2529
|
+
}
|
|
2530
|
+
setRetryInterval(ms) {
|
|
2531
|
+
this.#retryInterval = ms;
|
|
2532
|
+
}
|
|
2533
|
+
_sendRetry() {
|
|
2534
|
+
this._write(`retry: ${this.#retryInterval}
|
|
2594
2535
|
|
|
2595
2536
|
`);
|
|
2596
|
-
}
|
|
2597
|
-
_setupSseHeaders() {
|
|
2598
|
-
this.#res.setHeader("Content-Type", "text/event-stream");
|
|
2599
|
-
this.#res.setHeader("Cache-Control", "no-cache");
|
|
2600
|
-
this.#res.setHeader("Connection", "keep-alive");
|
|
2601
|
-
this.#res.setHeader("Transfer-Encoding", "chunked");
|
|
2602
|
-
this.#res.setHeader("X-Accel-Buffering", "no");
|
|
2603
|
-
if (typeof this.#res.flush !== "function") {
|
|
2604
|
-
this.#res.flush = () => {
|
|
2605
|
-
};
|
|
2606
|
-
}
|
|
2607
|
-
}
|
|
2608
|
-
_formatEvent(event, data, id = null) {
|
|
2609
|
-
const lines = [];
|
|
2610
|
-
if (id !== null) {
|
|
2611
|
-
lines.push(`id: ${id}`);
|
|
2612
|
-
}
|
|
2613
|
-
lines.push(`event: ${event}`);
|
|
2614
|
-
if (data !== null && data !== void 0) {
|
|
2615
|
-
const jsonStr = JSON.stringify(data);
|
|
2616
|
-
lines.push(`data: ${jsonStr}`);
|
|
2617
|
-
} else {
|
|
2618
|
-
lines.push("data:");
|
|
2619
|
-
}
|
|
2620
|
-
return lines.join("\n") + "\n\n";
|
|
2621
|
-
}
|
|
2622
|
-
_setupDrain() {
|
|
2623
|
-
if (typeof this.#res.on !== "function")
|
|
2624
|
-
return;
|
|
2625
|
-
this.#drainHandler = () => {
|
|
2626
|
-
if (!this.#active)
|
|
2627
|
-
return;
|
|
2628
|
-
if (this.#backpressureBuffer.length > 0) {
|
|
2629
|
-
this._drainBuffer();
|
|
2630
2537
|
}
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2538
|
+
_setupSseHeaders() {
|
|
2539
|
+
this.#res.setHeader("Content-Type", "text/event-stream");
|
|
2540
|
+
this.#res.setHeader("Cache-Control", "no-cache");
|
|
2541
|
+
this.#res.setHeader("Connection", "keep-alive");
|
|
2542
|
+
this.#res.setHeader("Transfer-Encoding", "chunked");
|
|
2543
|
+
this.#res.setHeader("X-Accel-Buffering", "no");
|
|
2544
|
+
if (typeof this.#res.flush !== "function") {
|
|
2545
|
+
this.#res.flush = () => {
|
|
2546
|
+
};
|
|
2547
|
+
}
|
|
2641
2548
|
}
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
return
|
|
2549
|
+
_formatEvent(event, data, id = null) {
|
|
2550
|
+
const lines = [];
|
|
2551
|
+
if (id !== null) {
|
|
2552
|
+
lines.push(`id: ${id}`);
|
|
2553
|
+
}
|
|
2554
|
+
lines.push(`event: ${event}`);
|
|
2555
|
+
if (data !== null && data !== void 0) {
|
|
2556
|
+
const jsonStr = JSON.stringify(data);
|
|
2557
|
+
lines.push(`data: ${jsonStr}`);
|
|
2558
|
+
} else {
|
|
2559
|
+
lines.push("data:");
|
|
2560
|
+
}
|
|
2561
|
+
return lines.join("\n") + "\n\n";
|
|
2562
|
+
}
|
|
2563
|
+
_setupDrain() {
|
|
2564
|
+
if (typeof this.#res.on !== "function") return;
|
|
2565
|
+
this.#drainHandler = () => {
|
|
2566
|
+
if (!this.#active) return;
|
|
2567
|
+
if (this.#backpressureBuffer.length > 0) {
|
|
2568
|
+
this._drainBuffer();
|
|
2569
|
+
}
|
|
2570
|
+
};
|
|
2571
|
+
this.#res.on("drain", this.#drainHandler);
|
|
2572
|
+
}
|
|
2573
|
+
_drainBuffer() {
|
|
2574
|
+
const batch = this.#backpressureBuffer.splice(0, 50);
|
|
2575
|
+
for (const item of batch) {
|
|
2576
|
+
const ok = this.#res.write(item);
|
|
2577
|
+
if (!ok) {
|
|
2578
|
+
this.#backpressureBuffer.unshift(...batch.slice(batch.indexOf(item)));
|
|
2579
|
+
return;
|
|
2580
|
+
}
|
|
2581
|
+
}
|
|
2582
|
+
this._flush();
|
|
2583
|
+
if (this.#backpressureBuffer.length > 0) {
|
|
2584
|
+
this._drainBuffer();
|
|
2585
|
+
}
|
|
2655
2586
|
}
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2587
|
+
_write(content) {
|
|
2588
|
+
if (!this.#active) return false;
|
|
2589
|
+
try {
|
|
2590
|
+
if (this.#backpressureBuffer.length > 0) {
|
|
2591
|
+
this.#backpressureBuffer.push(content);
|
|
2592
|
+
return true;
|
|
2593
|
+
}
|
|
2594
|
+
const result = this.#res.write(content);
|
|
2595
|
+
if (typeof this.#res.flush === "function") {
|
|
2596
|
+
this.#res.flush();
|
|
2597
|
+
}
|
|
2598
|
+
if (result === false) {
|
|
2599
|
+
this.#backpressureBuffer.push(content);
|
|
2600
|
+
return true;
|
|
2601
|
+
}
|
|
2602
|
+
return true;
|
|
2603
|
+
} catch (e) {
|
|
2604
|
+
this.#active = false;
|
|
2605
|
+
this.close();
|
|
2606
|
+
return false;
|
|
2607
|
+
}
|
|
2659
2608
|
}
|
|
2660
|
-
|
|
2661
|
-
|
|
2609
|
+
_flush() {
|
|
2610
|
+
try {
|
|
2611
|
+
if (typeof this.#res.flush === "function") {
|
|
2612
|
+
this.#res.flush();
|
|
2613
|
+
}
|
|
2614
|
+
} catch (e) {
|
|
2615
|
+
}
|
|
2616
|
+
}
|
|
2617
|
+
flushBackpressure() {
|
|
2618
|
+
if (this.#backpressureBuffer.length > 0) {
|
|
2619
|
+
this._drainBuffer();
|
|
2620
|
+
}
|
|
2621
|
+
}
|
|
2622
|
+
sendEvent(event, data, id = null) {
|
|
2623
|
+
this.#lastEventId++;
|
|
2624
|
+
const eventId = id !== null ? id : this.#lastEventId;
|
|
2625
|
+
const formatted = this._formatEvent(event, data, eventId);
|
|
2626
|
+
this.#sentMessages.push({ event, data, id: eventId, timestamp: Date.now() });
|
|
2627
|
+
if (this.#sentMessages.length > MAX_SENT_MESSAGES) {
|
|
2628
|
+
this.#sentMessages.splice(0, this.#sentMessages.length - MAX_SENT_MESSAGES);
|
|
2629
|
+
}
|
|
2630
|
+
this._write(formatted);
|
|
2631
|
+
this._flush();
|
|
2662
2632
|
return true;
|
|
2663
2633
|
}
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
this.#active = false;
|
|
2667
|
-
this.close();
|
|
2668
|
-
return false;
|
|
2669
|
-
}
|
|
2670
|
-
}
|
|
2671
|
-
_flush() {
|
|
2672
|
-
try {
|
|
2673
|
-
if (typeof this.#res.flush === "function") {
|
|
2674
|
-
this.#res.flush();
|
|
2634
|
+
sendMessage(data) {
|
|
2635
|
+
return this.sendEvent(SSE_EVENTS.MESSAGE, data);
|
|
2675
2636
|
}
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
sendMessage(data) {
|
|
2697
|
-
return this.sendEvent(SSE_EVENTS.MESSAGE, data);
|
|
2698
|
-
}
|
|
2699
|
-
sendEndpoint(path8) {
|
|
2700
|
-
return this.sendEvent(SSE_EVENTS.ENDPOINT, path8);
|
|
2701
|
-
}
|
|
2702
|
-
sendError(code, message, id = null) {
|
|
2703
|
-
return this.sendEvent(SSE_EVENTS.ERROR, { code, message }, id);
|
|
2704
|
-
}
|
|
2705
|
-
sendPing() {
|
|
2706
|
-
return this.sendEvent(SSE_EVENTS.PING, { timestamp: Date.now() });
|
|
2707
|
-
}
|
|
2708
|
-
sendProgress(current, total, message = "") {
|
|
2709
|
-
return this.sendEvent(SSE_EVENTS.PROGRESS, { current, total, message });
|
|
2710
|
-
}
|
|
2711
|
-
sendCapabilities(capabilities) {
|
|
2712
|
-
return this.sendEvent(SSE_EVENTS.CAPABILITIES, capabilities);
|
|
2713
|
-
}
|
|
2714
|
-
sendResponse(jsonrpcResponse, id = null) {
|
|
2715
|
-
const eventId = id !== null ? id : this.#lastEventId;
|
|
2716
|
-
return this.sendEvent(SSE_EVENTS.MESSAGE, jsonrpcResponse, eventId);
|
|
2717
|
-
}
|
|
2718
|
-
startHeartbeat(interval = null) {
|
|
2719
|
-
this.stopHeartbeat();
|
|
2720
|
-
const ms = interval || this.#heartbeatInterval;
|
|
2721
|
-
this.#heartbeatTimer = setInterval(() => {
|
|
2722
|
-
if (!this.#active) {
|
|
2637
|
+
sendEndpoint(path8) {
|
|
2638
|
+
return this.sendEvent(SSE_EVENTS.ENDPOINT, path8);
|
|
2639
|
+
}
|
|
2640
|
+
sendError(code, message, id = null) {
|
|
2641
|
+
return this.sendEvent(SSE_EVENTS.ERROR, { code, message }, id);
|
|
2642
|
+
}
|
|
2643
|
+
sendPing() {
|
|
2644
|
+
return this.sendEvent(SSE_EVENTS.PING, { timestamp: Date.now() });
|
|
2645
|
+
}
|
|
2646
|
+
sendProgress(current, total, message = "") {
|
|
2647
|
+
return this.sendEvent(SSE_EVENTS.PROGRESS, { current, total, message });
|
|
2648
|
+
}
|
|
2649
|
+
sendCapabilities(capabilities) {
|
|
2650
|
+
return this.sendEvent(SSE_EVENTS.CAPABILITIES, capabilities);
|
|
2651
|
+
}
|
|
2652
|
+
sendResponse(jsonrpcResponse, id = null) {
|
|
2653
|
+
const eventId = id !== null ? id : this.#lastEventId;
|
|
2654
|
+
return this.sendEvent(SSE_EVENTS.MESSAGE, jsonrpcResponse, eventId);
|
|
2655
|
+
}
|
|
2656
|
+
startHeartbeat(interval = null) {
|
|
2723
2657
|
this.stopHeartbeat();
|
|
2724
|
-
|
|
2658
|
+
const ms = interval || this.#heartbeatInterval;
|
|
2659
|
+
this.#heartbeatTimer = setInterval(() => {
|
|
2660
|
+
if (!this.#active) {
|
|
2661
|
+
this.stopHeartbeat();
|
|
2662
|
+
return;
|
|
2663
|
+
}
|
|
2664
|
+
if (!this.sendPing()) {
|
|
2665
|
+
this.stopHeartbeat();
|
|
2666
|
+
this.close();
|
|
2667
|
+
}
|
|
2668
|
+
}, ms);
|
|
2725
2669
|
}
|
|
2726
|
-
|
|
2670
|
+
stopHeartbeat() {
|
|
2671
|
+
if (this.#heartbeatTimer) {
|
|
2672
|
+
clearInterval(this.#heartbeatTimer);
|
|
2673
|
+
this.#heartbeatTimer = null;
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
close() {
|
|
2677
|
+
this.#active = false;
|
|
2727
2678
|
this.stopHeartbeat();
|
|
2728
|
-
this
|
|
2679
|
+
if (this.#drainHandler) {
|
|
2680
|
+
this.#res.removeListener("drain", this.#drainHandler);
|
|
2681
|
+
this.#drainHandler = null;
|
|
2682
|
+
}
|
|
2683
|
+
this.#backpressureBuffer = [];
|
|
2684
|
+
try {
|
|
2685
|
+
this.#res.end();
|
|
2686
|
+
} catch (e) {
|
|
2687
|
+
}
|
|
2729
2688
|
}
|
|
2730
|
-
}
|
|
2731
|
-
}
|
|
2732
|
-
stopHeartbeat() {
|
|
2733
|
-
if (this.#heartbeatTimer) {
|
|
2734
|
-
clearInterval(this.#heartbeatTimer);
|
|
2735
|
-
this.#heartbeatTimer = null;
|
|
2736
|
-
}
|
|
2737
|
-
}
|
|
2738
|
-
close() {
|
|
2739
|
-
this.#active = false;
|
|
2740
|
-
this.stopHeartbeat();
|
|
2741
|
-
if (this.#drainHandler) {
|
|
2742
|
-
this.#res.removeListener("drain", this.#drainHandler);
|
|
2743
|
-
this.#drainHandler = null;
|
|
2744
|
-
}
|
|
2745
|
-
this.#backpressureBuffer = [];
|
|
2746
|
-
try {
|
|
2747
|
-
this.#res.end();
|
|
2748
|
-
} catch (e) {
|
|
2749
|
-
}
|
|
2689
|
+
};
|
|
2750
2690
|
}
|
|
2751
|
-
};
|
|
2691
|
+
});
|
|
2752
2692
|
|
|
2753
2693
|
// src/sse-session-manager.js
|
|
2754
2694
|
import fs5 from "fs";
|
|
2755
2695
|
import path6 from "path";
|
|
2756
2696
|
import os3 from "os";
|
|
2757
|
-
var SESSION_STORE_DIR = path6.join(os3.homedir(), ".atlisp", "sessions");
|
|
2758
|
-
var SESSION_STORE_TTL = 24 * 60 * 60 * 1e3;
|
|
2759
2697
|
function getSessionStorePath(clientId) {
|
|
2760
2698
|
return path6.join(SESSION_STORE_DIR, `${clientId}.json`);
|
|
2761
2699
|
}
|
|
@@ -2775,8 +2713,7 @@ function persistSessionData(clientId, sessionData) {
|
|
|
2775
2713
|
function loadPersistedSessionData(clientId) {
|
|
2776
2714
|
try {
|
|
2777
2715
|
const filePath = getSessionStorePath(clientId);
|
|
2778
|
-
if (!fs5.existsSync(filePath))
|
|
2779
|
-
return null;
|
|
2716
|
+
if (!fs5.existsSync(filePath)) return null;
|
|
2780
2717
|
const raw = fs5.readFileSync(filePath, "utf-8");
|
|
2781
2718
|
const store = JSON.parse(raw);
|
|
2782
2719
|
if (Date.now() - store.savedAt > SESSION_STORE_TTL) {
|
|
@@ -2788,200 +2725,571 @@ function loadPersistedSessionData(clientId) {
|
|
|
2788
2725
|
return null;
|
|
2789
2726
|
}
|
|
2790
2727
|
}
|
|
2791
|
-
var
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
session
|
|
2728
|
+
var SESSION_STORE_DIR, SESSION_STORE_TTL, SseSessionManager;
|
|
2729
|
+
var init_sse_session_manager = __esm({
|
|
2730
|
+
"src/sse-session-manager.js"() {
|
|
2731
|
+
init_sse_session();
|
|
2732
|
+
SESSION_STORE_DIR = path6.join(os3.homedir(), ".atlisp", "sessions");
|
|
2733
|
+
SESSION_STORE_TTL = 24 * 60 * 60 * 1e3;
|
|
2734
|
+
SseSessionManager = class {
|
|
2735
|
+
#sessions = /* @__PURE__ */ new Map();
|
|
2736
|
+
#eventHandlers = /* @__PURE__ */ new Map();
|
|
2737
|
+
constructor() {
|
|
2738
|
+
this.#sessions = /* @__PURE__ */ new Map();
|
|
2739
|
+
this.#eventHandlers = /* @__PURE__ */ new Map();
|
|
2740
|
+
}
|
|
2741
|
+
add(clientId, session) {
|
|
2742
|
+
if (!(session instanceof SseSession)) {
|
|
2743
|
+
throw new Error("session must be an SseSession instance");
|
|
2744
|
+
}
|
|
2745
|
+
const persisted = loadPersistedSessionData(clientId);
|
|
2746
|
+
if (persisted) {
|
|
2747
|
+
for (const [key, value] of Object.entries(persisted)) {
|
|
2748
|
+
session.setSessionData(key, value);
|
|
2749
|
+
}
|
|
2750
|
+
}
|
|
2751
|
+
this.#sessions.set(clientId, session);
|
|
2752
|
+
session.startHeartbeat();
|
|
2753
|
+
}
|
|
2754
|
+
remove(clientId) {
|
|
2755
|
+
const session = this.#sessions.get(clientId);
|
|
2756
|
+
if (session) {
|
|
2757
|
+
persistSessionData(clientId, session.sessionData);
|
|
2758
|
+
session.close();
|
|
2759
|
+
this.#sessions.delete(clientId);
|
|
2760
|
+
return true;
|
|
2761
|
+
}
|
|
2762
|
+
return false;
|
|
2806
2763
|
}
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2764
|
+
get(clientId) {
|
|
2765
|
+
return this.#sessions.get(clientId) || null;
|
|
2766
|
+
}
|
|
2767
|
+
has(clientId) {
|
|
2768
|
+
return this.#sessions.has(clientId);
|
|
2769
|
+
}
|
|
2770
|
+
list() {
|
|
2771
|
+
return Array.from(this.#sessions.keys()).map((id) => ({
|
|
2772
|
+
clientId: id,
|
|
2773
|
+
isActive: this.#sessions.get(id)?.isActive ?? false
|
|
2774
|
+
}));
|
|
2775
|
+
}
|
|
2776
|
+
listActive() {
|
|
2777
|
+
return this.list().filter((s) => s.isActive);
|
|
2778
|
+
}
|
|
2779
|
+
count() {
|
|
2780
|
+
return this.#sessions.size;
|
|
2781
|
+
}
|
|
2782
|
+
sendToClient(clientId, event, data, id = null) {
|
|
2783
|
+
const session = this.#sessions.get(clientId);
|
|
2784
|
+
if (!session || !session.isActive) {
|
|
2785
|
+
return false;
|
|
2786
|
+
}
|
|
2787
|
+
return session.sendEvent(event, data, id);
|
|
2788
|
+
}
|
|
2789
|
+
sendMessageToClient(clientId, data) {
|
|
2790
|
+
return this.sendToClient(clientId, SSE_EVENTS.MESSAGE, data);
|
|
2791
|
+
}
|
|
2792
|
+
sendErrorToClient(clientId, code, message, id = null) {
|
|
2793
|
+
return this.sendToClient(clientId, SSE_EVENTS.ERROR, { code, message }, id);
|
|
2794
|
+
}
|
|
2795
|
+
sendProgressToClient(clientId, current, total, message = "") {
|
|
2796
|
+
return this.sendToClient(clientId, SSE_EVENTS.PROGRESS, { current, total, message });
|
|
2797
|
+
}
|
|
2798
|
+
broadcast(event, data) {
|
|
2799
|
+
let successCount = 0;
|
|
2800
|
+
for (const [clientId, session] of this.#sessions) {
|
|
2801
|
+
if (session.isActive && session.sendEvent(event, data)) {
|
|
2802
|
+
successCount++;
|
|
2803
|
+
}
|
|
2804
|
+
}
|
|
2805
|
+
return successCount;
|
|
2806
|
+
}
|
|
2807
|
+
broadcastMessage(data) {
|
|
2808
|
+
return this.broadcast(SSE_EVENTS.MESSAGE, data);
|
|
2809
|
+
}
|
|
2810
|
+
broadcastError(code, message) {
|
|
2811
|
+
return this.broadcast(SSE_EVENTS.ERROR, { code, message });
|
|
2812
|
+
}
|
|
2813
|
+
on(event, handler) {
|
|
2814
|
+
if (!this.#eventHandlers.has(event)) {
|
|
2815
|
+
this.#eventHandlers.set(event, /* @__PURE__ */ new Set());
|
|
2816
|
+
}
|
|
2817
|
+
this.#eventHandlers.get(event).add(handler);
|
|
2818
|
+
}
|
|
2819
|
+
off(event, handler) {
|
|
2820
|
+
const handlers = this.#eventHandlers.get(event);
|
|
2821
|
+
if (handlers) {
|
|
2822
|
+
handlers.delete(handler);
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
2825
|
+
emit(event, data) {
|
|
2826
|
+
const handlers = this.#eventHandlers.get(event);
|
|
2827
|
+
if (handlers) {
|
|
2828
|
+
for (const handler of handlers) {
|
|
2829
|
+
try {
|
|
2830
|
+
handler(data);
|
|
2831
|
+
} catch (e) {
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2835
|
+
}
|
|
2836
|
+
cleanupInactive() {
|
|
2837
|
+
for (const [clientId, session] of this.#sessions) {
|
|
2838
|
+
if (!session.isActive) {
|
|
2839
|
+
session.close();
|
|
2840
|
+
this.#sessions.delete(clientId);
|
|
2841
|
+
}
|
|
2842
|
+
}
|
|
2843
|
+
}
|
|
2844
|
+
closeAll() {
|
|
2845
|
+
for (const [clientId, session] of this.#sessions) {
|
|
2846
|
+
persistSessionData(clientId, session.sessionData);
|
|
2847
|
+
session.close();
|
|
2848
|
+
}
|
|
2849
|
+
this.#sessions.clear();
|
|
2850
|
+
}
|
|
2851
|
+
};
|
|
2835
2852
|
}
|
|
2836
|
-
|
|
2837
|
-
|
|
2853
|
+
});
|
|
2854
|
+
|
|
2855
|
+
// src/session-transport.js
|
|
2856
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
2857
|
+
var SESSION_TIMEOUT, SessionTransport;
|
|
2858
|
+
var init_session_transport = __esm({
|
|
2859
|
+
"src/session-transport.js"() {
|
|
2860
|
+
SESSION_TIMEOUT = 30 * 60 * 1e3;
|
|
2861
|
+
SessionTransport = class {
|
|
2862
|
+
constructor(sessionId) {
|
|
2863
|
+
this.sessionId = sessionId || randomUUID3();
|
|
2864
|
+
this.onclose = null;
|
|
2865
|
+
this.onerror = null;
|
|
2866
|
+
this.onmessage = null;
|
|
2867
|
+
this._pendingRequest = null;
|
|
2868
|
+
this._started = false;
|
|
2869
|
+
this._closed = false;
|
|
2870
|
+
this._cleanupTimer = null;
|
|
2871
|
+
}
|
|
2872
|
+
async start() {
|
|
2873
|
+
if (this._started) {
|
|
2874
|
+
throw new Error("Transport already started");
|
|
2875
|
+
}
|
|
2876
|
+
this._started = true;
|
|
2877
|
+
}
|
|
2878
|
+
async send(message) {
|
|
2879
|
+
if (this._closed) return;
|
|
2880
|
+
if (this._pendingRequest) {
|
|
2881
|
+
const { resolve, reject, timeout } = this._pendingRequest;
|
|
2882
|
+
clearTimeout(timeout);
|
|
2883
|
+
this._pendingRequest = null;
|
|
2884
|
+
resolve(message);
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
async close() {
|
|
2888
|
+
if (this._closed) return;
|
|
2889
|
+
this._closed = true;
|
|
2890
|
+
if (this._pendingRequest) {
|
|
2891
|
+
const { reject, timeout } = this._pendingRequest;
|
|
2892
|
+
clearTimeout(timeout);
|
|
2893
|
+
this._pendingRequest = null;
|
|
2894
|
+
reject(new Error("Transport closed"));
|
|
2895
|
+
}
|
|
2896
|
+
this._clearCleanupTimer();
|
|
2897
|
+
this.onclose?.();
|
|
2898
|
+
}
|
|
2899
|
+
async handleRequest(message, timeoutMs = 6e4) {
|
|
2900
|
+
if (this._closed) {
|
|
2901
|
+
throw new Error("Transport closed");
|
|
2902
|
+
}
|
|
2903
|
+
if (!message || !message.method) {
|
|
2904
|
+
throw new Error("Invalid message");
|
|
2905
|
+
}
|
|
2906
|
+
if (!message.id) {
|
|
2907
|
+
this.onmessage?.(message);
|
|
2908
|
+
return null;
|
|
2909
|
+
}
|
|
2910
|
+
return new Promise((resolve, reject) => {
|
|
2911
|
+
const timeout = setTimeout(() => {
|
|
2912
|
+
if (this._pendingRequest?.resolve === resolve) {
|
|
2913
|
+
this._pendingRequest = null;
|
|
2914
|
+
reject(new Error("Request timeout"));
|
|
2915
|
+
}
|
|
2916
|
+
}, timeoutMs);
|
|
2917
|
+
this._pendingRequest = { resolve, reject, timeout };
|
|
2918
|
+
this.onmessage?.(message);
|
|
2919
|
+
});
|
|
2920
|
+
}
|
|
2921
|
+
resetCleanupTimer() {
|
|
2922
|
+
this._clearCleanupTimer();
|
|
2923
|
+
this._cleanupTimer = setTimeout(() => {
|
|
2924
|
+
this.close();
|
|
2925
|
+
}, SESSION_TIMEOUT);
|
|
2926
|
+
}
|
|
2927
|
+
_clearCleanupTimer() {
|
|
2928
|
+
if (this._cleanupTimer) {
|
|
2929
|
+
clearTimeout(this._cleanupTimer);
|
|
2930
|
+
this._cleanupTimer = null;
|
|
2931
|
+
}
|
|
2932
|
+
}
|
|
2933
|
+
};
|
|
2838
2934
|
}
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2935
|
+
});
|
|
2936
|
+
|
|
2937
|
+
// src/mcp-handlers.js
|
|
2938
|
+
import {
|
|
2939
|
+
ListToolsRequestSchema,
|
|
2940
|
+
CallToolRequestSchema,
|
|
2941
|
+
ListResourcesRequestSchema,
|
|
2942
|
+
ReadResourceRequestSchema,
|
|
2943
|
+
SubscribeRequestSchema,
|
|
2944
|
+
UnsubscribeRequestSchema,
|
|
2945
|
+
ListPromptsRequestSchema,
|
|
2946
|
+
GetPromptRequestSchema
|
|
2947
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
2948
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2949
|
+
function createMcpServer(handlers = {}) {
|
|
2950
|
+
const { tools: tools2 = [], handleToolCall: handleToolCall2 = () => ({ content: [] }) } = handlers;
|
|
2951
|
+
const server = new Server(
|
|
2952
|
+
{ name: SERVER_NAME, version: SERVER_VERSION },
|
|
2953
|
+
{ capabilities: SERVER_CAPABILITIES }
|
|
2954
|
+
);
|
|
2955
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: tools2 }));
|
|
2956
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
2957
|
+
const { name, arguments: args } = request.params;
|
|
2958
|
+
return await handleToolCall2(name, args || {});
|
|
2959
|
+
});
|
|
2960
|
+
server.setRequestHandler(ListResourcesRequestSchema, async (request) => {
|
|
2961
|
+
const ctx = mcpContext.getStore();
|
|
2962
|
+
const sessionId = ctx?.sessionId;
|
|
2963
|
+
const subscribedUris = sessionId ? list(sessionId) : list();
|
|
2964
|
+
return {
|
|
2965
|
+
resources: await listResources(subscribedUris)
|
|
2966
|
+
};
|
|
2967
|
+
});
|
|
2968
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
2969
|
+
const { uri } = request.params;
|
|
2970
|
+
try {
|
|
2971
|
+
const data = await readResource(uri);
|
|
2972
|
+
return {
|
|
2973
|
+
contents: [{
|
|
2974
|
+
uri,
|
|
2975
|
+
mimeType: "application/json",
|
|
2976
|
+
text: JSON.stringify(data)
|
|
2977
|
+
}]
|
|
2978
|
+
};
|
|
2979
|
+
} catch (e) {
|
|
2980
|
+
return {
|
|
2981
|
+
contents: [],
|
|
2982
|
+
isError: true,
|
|
2983
|
+
error: { code: -32603, message: e.message }
|
|
2984
|
+
};
|
|
2843
2985
|
}
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2986
|
+
});
|
|
2987
|
+
server.setRequestHandler(SubscribeRequestSchema, async (request) => {
|
|
2988
|
+
const { uri } = request.params;
|
|
2989
|
+
const ctx = mcpContext.getStore();
|
|
2990
|
+
subscribe(uri, ctx?.sessionId);
|
|
2991
|
+
return { success: true };
|
|
2992
|
+
});
|
|
2993
|
+
server.setRequestHandler(UnsubscribeRequestSchema, async (request) => {
|
|
2994
|
+
const { uri } = request.params;
|
|
2995
|
+
const ctx = mcpContext.getStore();
|
|
2996
|
+
unsubscribe(uri, ctx?.sessionId);
|
|
2997
|
+
return { success: true };
|
|
2998
|
+
});
|
|
2999
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => ({
|
|
3000
|
+
prompts: await listPrompts()
|
|
3001
|
+
}));
|
|
3002
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
3003
|
+
const { name, arguments: args } = request.params;
|
|
3004
|
+
return await getPrompt(name, args || {});
|
|
3005
|
+
});
|
|
3006
|
+
return server;
|
|
3007
|
+
}
|
|
3008
|
+
var SERVER_CAPABILITIES;
|
|
3009
|
+
var init_mcp_handlers = __esm({
|
|
3010
|
+
"src/mcp-handlers.js"() {
|
|
3011
|
+
init_resource_handlers();
|
|
3012
|
+
init_prompt_handlers();
|
|
3013
|
+
init_constants();
|
|
3014
|
+
init_subscription_manager();
|
|
3015
|
+
init_mcp_server_state();
|
|
3016
|
+
SERVER_CAPABILITIES = {
|
|
3017
|
+
tools: {},
|
|
3018
|
+
resources: { subscribe: true, listChanged: true },
|
|
3019
|
+
prompts: {}
|
|
3020
|
+
};
|
|
2848
3021
|
}
|
|
2849
|
-
|
|
2850
|
-
|
|
3022
|
+
});
|
|
3023
|
+
|
|
3024
|
+
// src/mcp-server-state.js
|
|
3025
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
3026
|
+
async function getOrCreateSessionServer(sessionId) {
|
|
3027
|
+
if (!mcpHandlers) {
|
|
3028
|
+
const { tools: tools2, handleToolCall: handleToolCall2 } = await init_atlisp_mcp().then(() => atlisp_mcp_exports);
|
|
3029
|
+
mcpHandlers = { tools: tools2, handleToolCall: handleToolCall2 };
|
|
2851
3030
|
}
|
|
2852
|
-
|
|
2853
|
-
|
|
3031
|
+
let entry = sessionServers.get(sessionId);
|
|
3032
|
+
if (!entry) {
|
|
3033
|
+
const server = createMcpServer(mcpHandlers);
|
|
3034
|
+
const transport = new SessionTransport(sessionId);
|
|
3035
|
+
server.connect(transport);
|
|
3036
|
+
entry = { server, transport };
|
|
3037
|
+
sessionServers.set(sessionId, entry);
|
|
3038
|
+
sessionTransports.set(sessionId, transport);
|
|
2854
3039
|
}
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
3040
|
+
return entry;
|
|
3041
|
+
}
|
|
3042
|
+
function createStdioServer(handlers) {
|
|
3043
|
+
mcpHandlers = handlers;
|
|
3044
|
+
return createMcpServer(handlers);
|
|
3045
|
+
}
|
|
3046
|
+
function removeSessionServer(sessionId) {
|
|
3047
|
+
const entry = sessionServers.get(sessionId);
|
|
3048
|
+
if (entry) {
|
|
3049
|
+
entry.transport.close().catch(() => {
|
|
3050
|
+
});
|
|
3051
|
+
entry.server.close().catch(() => {
|
|
3052
|
+
});
|
|
3053
|
+
sessionServers.delete(sessionId);
|
|
3054
|
+
sessionTransports.delete(sessionId);
|
|
2863
3055
|
}
|
|
2864
|
-
|
|
2865
|
-
|
|
3056
|
+
}
|
|
3057
|
+
function broadcastToAllSessions(uri) {
|
|
3058
|
+
for (const [sid, entry] of sessionServers) {
|
|
3059
|
+
entry.server.sendResourceUpdated({ uri }).catch((err) => {
|
|
3060
|
+
error(`Failed to send resource update to session ${sid}: ${err.message}`, { sessionId: sid, uri });
|
|
3061
|
+
});
|
|
2866
3062
|
}
|
|
2867
|
-
|
|
2868
|
-
|
|
3063
|
+
}
|
|
3064
|
+
var mcpContext, sessionServers, sessionTransports, mcpHandlers;
|
|
3065
|
+
var init_mcp_server_state = __esm({
|
|
3066
|
+
"src/mcp-server-state.js"() {
|
|
3067
|
+
init_session_transport();
|
|
3068
|
+
init_mcp_handlers();
|
|
3069
|
+
init_logger();
|
|
3070
|
+
mcpContext = new AsyncLocalStorage();
|
|
3071
|
+
sessionServers = /* @__PURE__ */ new Map();
|
|
3072
|
+
sessionTransports = /* @__PURE__ */ new Map();
|
|
3073
|
+
mcpHandlers = null;
|
|
2869
3074
|
}
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
3075
|
+
});
|
|
3076
|
+
|
|
3077
|
+
// src/routes.js
|
|
3078
|
+
import express from "express";
|
|
3079
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
3080
|
+
import iconv from "iconv-lite";
|
|
3081
|
+
import rateLimit from "express-rate-limit";
|
|
3082
|
+
function createJsonBodyParser() {
|
|
3083
|
+
return async (req, res, next) => {
|
|
3084
|
+
const ct = (req.headers["content-type"] || "").toLowerCase();
|
|
3085
|
+
if (!JSON_CONTENT_TYPES.some((t) => ct.startsWith(t))) return next();
|
|
3086
|
+
try {
|
|
3087
|
+
const chunks = [];
|
|
3088
|
+
for await (const chunk of req) {
|
|
3089
|
+
chunks.push(chunk);
|
|
3090
|
+
}
|
|
3091
|
+
const totalLength = chunks.reduce((acc, c) => acc + c.length, 0);
|
|
3092
|
+
if (totalLength > 10 * 1024 * 1024) {
|
|
3093
|
+
throw new Error("Request body too large");
|
|
3094
|
+
}
|
|
3095
|
+
const buf = Buffer.concat(chunks);
|
|
3096
|
+
const m = ct.match(/charset\s*=\s*([^\s;]+)/i);
|
|
3097
|
+
let charset = m ? m[1].toLowerCase() : "utf-8";
|
|
3098
|
+
const charsetMap = { "gb2312": "gbk", "gb18030": "gbk", "gbk": "gbk", "936": "gbk" };
|
|
3099
|
+
charset = charsetMap[charset] || charset;
|
|
3100
|
+
const str = iconv.decode(buf, charset);
|
|
3101
|
+
req.body = JSON.parse(str);
|
|
3102
|
+
next();
|
|
3103
|
+
} catch (e) {
|
|
3104
|
+
res.status(400).json({ error: "Invalid JSON body", message: e.message });
|
|
2873
3105
|
}
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
3106
|
+
};
|
|
3107
|
+
}
|
|
3108
|
+
function createLoggingMiddleware() {
|
|
3109
|
+
return (req, res, next) => {
|
|
3110
|
+
log(`${req.method} ${req.path}`, { method: req.method, path: req.path, query: req.query });
|
|
3111
|
+
next();
|
|
3112
|
+
};
|
|
3113
|
+
}
|
|
3114
|
+
function createAuthMiddleware() {
|
|
3115
|
+
const PUBLIC_PATHS = ["/health"];
|
|
3116
|
+
return (req, res, next) => {
|
|
3117
|
+
if (apiKey) {
|
|
3118
|
+
if (PUBLIC_PATHS.includes(req.path)) return next();
|
|
3119
|
+
const auth = req.get("Authorization");
|
|
3120
|
+
if (auth === `Bearer ${apiKey}`) return next();
|
|
3121
|
+
return res.status(401).json({ error: "Unauthorized" });
|
|
2880
3122
|
}
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
3123
|
+
next();
|
|
3124
|
+
};
|
|
3125
|
+
}
|
|
3126
|
+
function createCorsMiddleware() {
|
|
3127
|
+
return (req, res, next) => {
|
|
3128
|
+
if (!enableCors) return next();
|
|
3129
|
+
res.setHeader("Access-Control-Allow-Origin", corsOrigin);
|
|
3130
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS, CONNECT");
|
|
3131
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Accept, mcp-session-id, last-event-id");
|
|
3132
|
+
res.setHeader("Access-Control-Expose-Headers", "mcp-session-id, content-type, x-accel-buffering");
|
|
3133
|
+
res.setHeader("Access-Control-Max-Age", "86400");
|
|
3134
|
+
res.setHeader("Vary", "Accept");
|
|
3135
|
+
if (req.method === "OPTIONS") return res.status(204).end();
|
|
3136
|
+
next();
|
|
3137
|
+
};
|
|
3138
|
+
}
|
|
3139
|
+
function createErrorHandler() {
|
|
3140
|
+
return (err, req, res, next) => {
|
|
3141
|
+
error(`Unhandled error: ${err.message}`, { stack: err.stack, path: req.path });
|
|
3142
|
+
res.status(500).json({ error: "Internal server error", message: err.message });
|
|
3143
|
+
};
|
|
3144
|
+
}
|
|
3145
|
+
function setupRoutes(app) {
|
|
3146
|
+
app.use(createJsonBodyParser());
|
|
3147
|
+
app.use(createLoggingMiddleware());
|
|
3148
|
+
app.use(createAuthMiddleware());
|
|
3149
|
+
app.use(createCorsMiddleware());
|
|
3150
|
+
app.get("/health", (req, res) => {
|
|
3151
|
+
res.json({ status: "ok", cad: cad.connected ? "connected" : "disconnected" });
|
|
3152
|
+
});
|
|
3153
|
+
app.get("/api/docs", (req, res) => {
|
|
3154
|
+
res.json({
|
|
3155
|
+
openapi: "3.0.0",
|
|
3156
|
+
info: { title: "@lisp MCP Server", version: "1.5.0" },
|
|
3157
|
+
paths: {
|
|
3158
|
+
"/mcp": { post: { summary: "MCP JSON-RPC endpoint" } },
|
|
3159
|
+
"/sse": { get: { summary: "Server-Sent Events endpoint" } },
|
|
3160
|
+
"/message": { post: { summary: "SSE message endpoint" } },
|
|
3161
|
+
"/health": { get: { summary: "Health check" } }
|
|
3162
|
+
}
|
|
3163
|
+
});
|
|
3164
|
+
});
|
|
3165
|
+
app.all("/mcp", mcpLimiter, handleMcpPost);
|
|
3166
|
+
app.all("/mcp/:path", mcpLimiter, handleMcpPost);
|
|
3167
|
+
app.get("/mcp/stream", mcpLimiter, handleMcpPost);
|
|
3168
|
+
app.get("/sse", handleSse);
|
|
3169
|
+
app.post("/message", mcpLimiter, handleMessage);
|
|
3170
|
+
app.use(createErrorHandler());
|
|
3171
|
+
return app;
|
|
3172
|
+
}
|
|
3173
|
+
async function handleMcpPost(req, res) {
|
|
3174
|
+
const sessionId = req.headers["mcp-session-id"] || req.query.sessionId || randomUUID4();
|
|
3175
|
+
sessionManager.createSession(sessionId);
|
|
3176
|
+
const body = req.body;
|
|
3177
|
+
if (!body || !body.method) {
|
|
3178
|
+
return res.status(400).json({ error: "Invalid request" });
|
|
3179
|
+
}
|
|
3180
|
+
const { server, transport } = await getOrCreateSessionServer(sessionId);
|
|
3181
|
+
transport.resetCleanupTimer();
|
|
3182
|
+
await mcpContext.run({ sessionId }, async () => {
|
|
3183
|
+
try {
|
|
3184
|
+
const response = await transport.handleRequest(body);
|
|
3185
|
+
if (response) {
|
|
3186
|
+
const headers = { "Content-Type": "application/json" };
|
|
3187
|
+
if (transport.sessionId) {
|
|
3188
|
+
headers["mcp-session-id"] = transport.sessionId;
|
|
3189
|
+
}
|
|
3190
|
+
res.writeHead(200, headers);
|
|
3191
|
+
res.end(JSON.stringify(response));
|
|
3192
|
+
} else {
|
|
3193
|
+
if (!res.headersSent) {
|
|
3194
|
+
res.status(202).end();
|
|
2889
3195
|
}
|
|
2890
3196
|
}
|
|
2891
|
-
}
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
if (!session.isActive) {
|
|
2896
|
-
session.close();
|
|
2897
|
-
this.#sessions.delete(clientId);
|
|
3197
|
+
} catch (err) {
|
|
3198
|
+
error(`Transport error [${sessionId}]: ${err.message}`);
|
|
3199
|
+
if (!res.headersSent) {
|
|
3200
|
+
res.status(500).json({ error: "Internal server error", message: err.message });
|
|
2898
3201
|
}
|
|
2899
3202
|
}
|
|
3203
|
+
});
|
|
3204
|
+
}
|
|
3205
|
+
async function handleSse(req, res) {
|
|
3206
|
+
const sessionId = randomUUID4();
|
|
3207
|
+
sessionManager.createSession(sessionId);
|
|
3208
|
+
getOrCreateSessionServer(sessionId);
|
|
3209
|
+
const sseSession = new SseSession(res, { clientId: sessionId });
|
|
3210
|
+
sseSessionManager.add(sessionId, sseSession);
|
|
3211
|
+
sseSession.sendEndpoint(`/message?sessionId=${sessionId}`);
|
|
3212
|
+
req.on("close", () => {
|
|
3213
|
+
sseSessionManager.remove(sessionId);
|
|
3214
|
+
removeSessionServer(sessionId);
|
|
3215
|
+
});
|
|
3216
|
+
}
|
|
3217
|
+
async function handleMessage(req, res) {
|
|
3218
|
+
const sessionId = req.query.sessionId;
|
|
3219
|
+
if (!sessionId) {
|
|
3220
|
+
return res.status(400).json({ error: "Missing sessionId" });
|
|
2900
3221
|
}
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
3222
|
+
const sseSession = sseSessionManager.get(sessionId);
|
|
3223
|
+
if (!sseSession) {
|
|
3224
|
+
return res.status(404).json({ error: "Session not found" });
|
|
3225
|
+
}
|
|
3226
|
+
sessionManager.createSession(sessionId);
|
|
3227
|
+
const { server, transport } = await getOrCreateSessionServer(sessionId);
|
|
3228
|
+
transport.resetCleanupTimer();
|
|
3229
|
+
const body = req.body;
|
|
3230
|
+
if (body && body.method) {
|
|
3231
|
+
try {
|
|
3232
|
+
const response = await transport.handleRequest(body);
|
|
3233
|
+
if (response) {
|
|
3234
|
+
sseSession.sendResponse(response);
|
|
3235
|
+
}
|
|
3236
|
+
if (!res.headersSent) {
|
|
3237
|
+
res.status(202).end();
|
|
3238
|
+
}
|
|
3239
|
+
} catch (err) {
|
|
3240
|
+
error(`SSE message error [${sessionId}]: ${err.message}`);
|
|
3241
|
+
if (!res.headersSent) {
|
|
3242
|
+
res.status(500).json({ error: "Internal server error" });
|
|
3243
|
+
}
|
|
2905
3244
|
}
|
|
2906
|
-
|
|
3245
|
+
} else {
|
|
3246
|
+
res.status(400).json({ error: "Invalid request" });
|
|
3247
|
+
}
|
|
3248
|
+
}
|
|
3249
|
+
var sseSessionManager, apiKey, enableCors, corsOrigin, rateLimitWindow, rateLimitMax, mcpLimiter, JSON_CONTENT_TYPES;
|
|
3250
|
+
var init_routes = __esm({
|
|
3251
|
+
"src/routes.js"() {
|
|
3252
|
+
init_config();
|
|
3253
|
+
init_logger();
|
|
3254
|
+
init_cad();
|
|
3255
|
+
init_subscription_manager();
|
|
3256
|
+
init_sse_session_manager();
|
|
3257
|
+
init_mcp_server_state();
|
|
3258
|
+
init_sse_session();
|
|
3259
|
+
sseSessionManager = new SseSessionManager();
|
|
3260
|
+
({ apiKey, enableCors, corsOrigin, rateLimitWindow, rateLimitMax } = config_default);
|
|
3261
|
+
mcpLimiter = rateLimit({
|
|
3262
|
+
windowMs: rateLimitWindow,
|
|
3263
|
+
max: rateLimitMax,
|
|
3264
|
+
standardHeaders: true,
|
|
3265
|
+
legacyHeaders: false,
|
|
3266
|
+
message: { error: "\u8BF7\u6C42\u8FC7\u4E8E\u9891\u7E41\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5" }
|
|
3267
|
+
});
|
|
3268
|
+
JSON_CONTENT_TYPES = ["application/json", "application/json-rpc", "application/vnd.api+json"];
|
|
2907
3269
|
}
|
|
2908
|
-
};
|
|
3270
|
+
});
|
|
2909
3271
|
|
|
2910
3272
|
// src/atlisp-mcp.js
|
|
2911
|
-
var
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
const args = process.argv.slice(2);
|
|
2920
|
-
for (let i = 0; i < args.length; i++) {
|
|
2921
|
-
if (args[i] === "--version" || args[i] === "-v") {
|
|
2922
|
-
console.log(`atlisp-mcp v${version}`);
|
|
2923
|
-
process.exit(0);
|
|
2924
|
-
} else if (args[i] === "--help" || args[i] === "-h") {
|
|
2925
|
-
console.log(`
|
|
2926
|
-
@lisp MCP Server v${version}
|
|
2927
|
-
|
|
2928
|
-
Usage: atlisp-mcp [options]
|
|
2929
|
-
|
|
2930
|
-
Options:
|
|
2931
|
-
-v, --version Show version number
|
|
2932
|
-
--transport <type> Transport type: http or stdio (default: http)
|
|
2933
|
-
--port <port> HTTP port (default: 8110)
|
|
2934
|
-
--host <host> HTTP host (default: 0.0.0.0)
|
|
2935
|
-
--stdio Shorthand for --transport stdio
|
|
2936
|
-
-h, --help Show this help message
|
|
2937
|
-
|
|
2938
|
-
Examples:
|
|
2939
|
-
atlisp-mcp # Start HTTP server on port 8110
|
|
2940
|
-
atlisp-mcp --port 3000 # Start HTTP server on port 3000
|
|
2941
|
-
atlisp-mcp --stdio # Start in stdio mode for MCP clients
|
|
2942
|
-
atlisp-mcp --transport stdio # Same as --stdio
|
|
2943
|
-
`);
|
|
2944
|
-
process.exit(0);
|
|
2945
|
-
} else if (args[i] === "--transport" && args[i + 1]) {
|
|
2946
|
-
process.env.TRANSPORT = args[i + 1];
|
|
2947
|
-
i++;
|
|
2948
|
-
} else if (args[i] === "--stdio") {
|
|
2949
|
-
process.env.TRANSPORT = "stdio";
|
|
2950
|
-
} else if (args[i] === "--port" && args[i + 1]) {
|
|
2951
|
-
process.env.PORT = args[i + 1];
|
|
2952
|
-
i++;
|
|
2953
|
-
} else if (args[i] === "--host" && args[i + 1]) {
|
|
2954
|
-
process.env.HOST = args[i + 1];
|
|
2955
|
-
i++;
|
|
2956
|
-
}
|
|
2957
|
-
}
|
|
2958
|
-
}
|
|
2959
|
-
var { decode: charsetDecode, encodingExists } = iconv;
|
|
2960
|
-
var mcpContext = new AsyncLocalStorage();
|
|
2961
|
-
var sseSessionManager = new SseSessionManager();
|
|
2962
|
-
var SERVER_CAPABILITIES = {
|
|
2963
|
-
tools: {},
|
|
2964
|
-
resources: { subscribe: true, listChanged: true },
|
|
2965
|
-
prompts: {}
|
|
2966
|
-
};
|
|
2967
|
-
var { port, host, transport, apiKey, rateLimitWindow, rateLimitMax } = config_default;
|
|
2968
|
-
if (apiKey)
|
|
2969
|
-
log("API Key authentication enabled");
|
|
2970
|
-
var mcpLimiter = rateLimit({
|
|
2971
|
-
windowMs: rateLimitWindow,
|
|
2972
|
-
max: rateLimitMax,
|
|
2973
|
-
standardHeaders: true,
|
|
2974
|
-
legacyHeaders: false,
|
|
2975
|
-
message: { error: "\u8BF7\u6C42\u8FC7\u4E8E\u9891\u7E41\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5" }
|
|
3273
|
+
var atlisp_mcp_exports = {};
|
|
3274
|
+
__export(atlisp_mcp_exports, {
|
|
3275
|
+
CAD_PLATFORMS: () => CAD_PLATFORMS,
|
|
3276
|
+
FILE_EXTENSIONS: () => FILE_EXTENSIONS,
|
|
3277
|
+
handleToolCall: () => handleToolCall,
|
|
3278
|
+
loadAtlibFunctionLib: () => loadAtlibFunctionLib,
|
|
3279
|
+
startServer: () => startServer,
|
|
3280
|
+
tools: () => tools
|
|
2976
3281
|
});
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
3282
|
+
import path7 from "path";
|
|
3283
|
+
import fs6 from "fs";
|
|
3284
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
3285
|
+
import { createRequire } from "module";
|
|
3286
|
+
import os4 from "os";
|
|
3287
|
+
import express2 from "express";
|
|
3288
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
2981
3289
|
async function readCacheFromDisk2() {
|
|
2982
3290
|
try {
|
|
2983
3291
|
const stat = fs6.statSync(FUNCLIB_CACHE_FILE2);
|
|
2984
|
-
if (Date.now() - stat.mtimeMs <
|
|
3292
|
+
if (Date.now() - stat.mtimeMs < CACHE_TTL3) {
|
|
2985
3293
|
const raw = fs6.readFileSync(FUNCLIB_CACHE_FILE2, "utf-8");
|
|
2986
3294
|
return JSON.parse(raw);
|
|
2987
3295
|
}
|
|
@@ -2994,7 +3302,7 @@ function writeCacheToDisk2(data) {
|
|
|
2994
3302
|
fs6.mkdirSync(CACHE_DIR2, { recursive: true });
|
|
2995
3303
|
fs6.writeFileSync(FUNCLIB_CACHE_FILE2, JSON.stringify(data), "utf-8");
|
|
2996
3304
|
} catch (e) {
|
|
2997
|
-
|
|
3305
|
+
error(`writeCacheToDisk error: ${e.message}`);
|
|
2998
3306
|
}
|
|
2999
3307
|
}
|
|
3000
3308
|
async function loadAtlibFunctionLib() {
|
|
@@ -3018,237 +3326,31 @@ async function loadAtlibFunctionLib() {
|
|
|
3018
3326
|
return data;
|
|
3019
3327
|
}
|
|
3020
3328
|
} catch (e) {
|
|
3021
|
-
|
|
3329
|
+
error(`loadAtlibFunctionLib error: ${e.message}`);
|
|
3022
3330
|
}
|
|
3023
3331
|
return cachedFunctionLib;
|
|
3024
3332
|
}
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
install_package: (a) => installPackage(a.packageName),
|
|
3033
|
-
get_platform_info: () => ({ content: [{ type: "text", text: `\u652F\u6301\u7684 CAD \u5E73\u53F0:
|
|
3034
|
-
${CAD_PLATFORMS.join("\n")}
|
|
3035
|
-
\u5F53\u524D\u8FDE\u63A5: ${cad.connected ? "\u5DF2\u8FDE\u63A5" : "\u672A\u8FDE\u63A5"}` }] }),
|
|
3036
|
-
at_command: (a) => atCommand(a.command),
|
|
3037
|
-
new_document: () => newDocument(),
|
|
3038
|
-
bring_to_front: () => bringToFront(),
|
|
3039
|
-
install_atlisp: () => installAtlisp(),
|
|
3040
|
-
init_atlisp: () => initAtlisp(),
|
|
3041
|
-
get_function_usage: (a) => getFunctionUsage(a.name, a.package),
|
|
3042
|
-
list_symbols: (a) => listSymbols(a.package),
|
|
3043
|
-
get_system_status: () => getSystemStatus(),
|
|
3044
|
-
list_prompts: async () => {
|
|
3045
|
-
const prompts = await listPrompts();
|
|
3046
|
-
return { content: [{ type: "text", text: JSON.stringify(prompts) }] };
|
|
3047
|
-
},
|
|
3048
|
-
get_prompt: async (a) => {
|
|
3049
|
-
const prompt = await getPrompt(a.name, a.args || {});
|
|
3050
|
-
return { content: prompt.messages.map((m) => ({ type: "text", text: m.content.text || m.content })) };
|
|
3051
|
-
},
|
|
3052
|
-
import_funlib: async (a) => {
|
|
3053
|
-
const data = await loadAtlibFunctionLib();
|
|
3054
|
-
if (!data)
|
|
3055
|
-
return { content: [{ type: "text", text: "\u52A0\u8F7D\u51FD\u6570\u5E93\u5931\u8D25" }], isError: true };
|
|
3056
|
-
if (a.format === "list") {
|
|
3057
|
-
const items = Object.values(data.all_functions || {}).map((f) => `${f.name}: ${f.description || ""}`);
|
|
3058
|
-
return { content: [{ type: "text", text: items.join("\n") }] };
|
|
3059
|
-
}
|
|
3060
|
-
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
3061
|
-
}
|
|
3062
|
-
};
|
|
3063
|
-
function getOrCreateSessionServer(sessionId) {
|
|
3064
|
-
let entry = sessionServers.get(sessionId);
|
|
3065
|
-
if (!entry) {
|
|
3066
|
-
const server = createMcpServer();
|
|
3067
|
-
const transport2 = new SessionTransport(sessionId);
|
|
3068
|
-
server.connect(transport2);
|
|
3069
|
-
entry = { server, transport: transport2 };
|
|
3070
|
-
sessionServers.set(sessionId, entry);
|
|
3071
|
-
sessionTransports.set(sessionId, transport2);
|
|
3072
|
-
}
|
|
3073
|
-
return entry;
|
|
3074
|
-
}
|
|
3075
|
-
function removeSessionServer(sessionId) {
|
|
3076
|
-
const entry = sessionServers.get(sessionId);
|
|
3077
|
-
if (entry) {
|
|
3078
|
-
entry.transport.close().catch(() => {
|
|
3079
|
-
});
|
|
3080
|
-
entry.server.close().catch(() => {
|
|
3081
|
-
});
|
|
3082
|
-
sessionServers.delete(sessionId);
|
|
3083
|
-
sessionTransports.delete(sessionId);
|
|
3084
|
-
}
|
|
3085
|
-
}
|
|
3086
|
-
function broadcastToAllSessions(uri) {
|
|
3087
|
-
for (const [sid, entry] of sessionServers) {
|
|
3088
|
-
entry.server.sendResourceUpdated({ uri }).catch((err) => {
|
|
3089
|
-
log(`Failed to send resource update to session ${sid}: ${err.message}`);
|
|
3090
|
-
});
|
|
3091
|
-
}
|
|
3092
|
-
}
|
|
3093
|
-
var tools = [
|
|
3094
|
-
{
|
|
3095
|
-
name: "connect_cad",
|
|
3096
|
-
description: "\u8FDE\u63A5\u5230 CAD (AutoCAD/ZWCAD/GStarCAD/BricsCAD)",
|
|
3097
|
-
inputSchema: {
|
|
3098
|
-
type: "object",
|
|
3099
|
-
properties: {
|
|
3100
|
-
platform: { type: "string", description: "\u6307\u5B9A CAD \u5E73\u53F0: AutoCAD, ZWCAD, GStarCAD, BricsCAD\uFF08\u53EF\u9009\uFF0C\u81EA\u52A8\u68C0\u6D4B\uFF09" }
|
|
3101
|
-
}
|
|
3102
|
-
}
|
|
3103
|
-
},
|
|
3104
|
-
{
|
|
3105
|
-
name: "eval_lisp",
|
|
3106
|
-
description: "\u5728 CAD \u4E2D\u6267\u884C AutoLISP \u4EE3\u7801\uFF08\u4E0D\u8FD4\u56DE\u7ED3\u679C\uFF09",
|
|
3107
|
-
inputSchema: {
|
|
3108
|
-
type: "object",
|
|
3109
|
-
properties: { code: { type: "string", description: "\u8981\u6267\u884C\u7684 LISP \u4EE3\u7801" } },
|
|
3110
|
-
required: ["code"]
|
|
3111
|
-
}
|
|
3112
|
-
},
|
|
3113
|
-
{
|
|
3114
|
-
name: "eval_lisp_with_result",
|
|
3115
|
-
description: "\u5728 CAD \u4E2D\u6267\u884C AutoLISP \u4EE3\u7801\u5E76\u8FD4\u56DE\u7ED3\u679C",
|
|
3116
|
-
inputSchema: {
|
|
3117
|
-
type: "object",
|
|
3118
|
-
properties: {
|
|
3119
|
-
code: { type: "string", description: "\u8981\u6267\u884C\u7684 LISP \u4EE3\u7801" },
|
|
3120
|
-
encoding: { type: "string", description: "\u7ED3\u679C\u7F16\u7801\uFF0C\u5982 utf-8, gbk, gb2312, gb18030\uFF08\u53EF\u9009\uFF0C\u9ED8\u8BA4\u81EA\u52A8\u68C0\u6D4B\uFF09" }
|
|
3121
|
-
},
|
|
3122
|
-
required: ["code"]
|
|
3123
|
-
}
|
|
3124
|
-
},
|
|
3125
|
-
{
|
|
3126
|
-
name: "get_cad_info",
|
|
3127
|
-
description: "\u83B7\u53D6\u5F53\u524D CAD \u4FE1\u606F",
|
|
3128
|
-
inputSchema: { type: "object", properties: {} }
|
|
3129
|
-
},
|
|
3130
|
-
{
|
|
3131
|
-
name: "list_packages",
|
|
3132
|
-
description: "\u5217\u51FA\u5DF2\u5B89\u88C5\u7684 @lisp \u5305",
|
|
3133
|
-
inputSchema: { type: "object", properties: {} }
|
|
3134
|
-
},
|
|
3135
|
-
{
|
|
3136
|
-
name: "search_packages",
|
|
3137
|
-
description: "\u641C\u7D22 @lisp \u5305",
|
|
3138
|
-
inputSchema: {
|
|
3139
|
-
type: "object",
|
|
3140
|
-
properties: { query: { type: "string", description: "\u641C\u7D22\u5173\u952E\u8BCD" } },
|
|
3141
|
-
required: ["query"]
|
|
3142
|
-
}
|
|
3143
|
-
},
|
|
3144
|
-
{
|
|
3145
|
-
name: "install_package",
|
|
3146
|
-
description: "\u5B89\u88C5 @lisp \u5305\u5230 CAD",
|
|
3147
|
-
inputSchema: {
|
|
3148
|
-
type: "object",
|
|
3149
|
-
properties: { packageName: { type: "string", description: "\u5305\u540D\u79F0" } },
|
|
3150
|
-
required: ["packageName"]
|
|
3151
|
-
}
|
|
3152
|
-
},
|
|
3153
|
-
{
|
|
3154
|
-
name: "get_platform_info",
|
|
3155
|
-
description: "\u83B7\u53D6 CAD \u5E73\u53F0\u4FE1\u606F",
|
|
3156
|
-
inputSchema: { type: "object", properties: {} }
|
|
3157
|
-
},
|
|
3158
|
-
{
|
|
3159
|
-
name: "at_command",
|
|
3160
|
-
description: "\u6267\u884C @lisp \u547D\u4EE4",
|
|
3161
|
-
inputSchema: {
|
|
3162
|
-
type: "object",
|
|
3163
|
-
properties: { command: { type: "string", description: "@lisp \u547D\u4EE4" } },
|
|
3164
|
-
required: ["command"]
|
|
3165
|
-
}
|
|
3166
|
-
},
|
|
3167
|
-
{
|
|
3168
|
-
name: "new_document",
|
|
3169
|
-
description: "\u5728 CAD \u4E2D\u65B0\u5EFA\u7A7A\u767D\u6587\u6863",
|
|
3170
|
-
inputSchema: { type: "object", properties: {} }
|
|
3171
|
-
},
|
|
3172
|
-
{
|
|
3173
|
-
name: "bring_to_front",
|
|
3174
|
-
description: "\u5C06 CAD \u7A97\u53E3\u5207\u6362\u5230\u524D\u53F0\uFF08\u4ECE\u540E\u53F0\u8FD0\u884C\u72B6\u6001\u5524\u9192\uFF09",
|
|
3175
|
-
inputSchema: { type: "object", properties: {} }
|
|
3176
|
-
},
|
|
3177
|
-
{
|
|
3178
|
-
name: "install_atlisp",
|
|
3179
|
-
description: "\u5728 CAD \u4E2D\u5B89\u88C5 @lisp",
|
|
3180
|
-
inputSchema: { type: "object", properties: {} }
|
|
3181
|
-
},
|
|
3182
|
-
{
|
|
3183
|
-
name: "init_atlisp",
|
|
3184
|
-
description: "\u521D\u59CB\u5316 CAD \u4E2D\u7684 @lisp \u73AF\u5883\uFF0C\u52A0\u8F7D\u51FD\u6570\u5E93\u548C\u521D\u59CB\u53D8\u91CF",
|
|
3185
|
-
inputSchema: { type: "object", properties: {} }
|
|
3186
|
-
},
|
|
3187
|
-
{
|
|
3188
|
-
name: "get_function_usage",
|
|
3189
|
-
description: "\u83B7\u53D6 @lisp \u51FD\u6570\u7528\u6CD5",
|
|
3190
|
-
inputSchema: {
|
|
3191
|
-
type: "object",
|
|
3192
|
-
properties: {
|
|
3193
|
-
name: { type: "string", description: "\u51FD\u6570\u540D\uFF0C\u5982 string:length" },
|
|
3194
|
-
package: { type: "string", description: "\u5305\u540D\uFF0C\u5982 string\uFF08\u53EF\u9009\uFF0C\u4E0D\u4F20\u5219\u81EA\u52A8\u641C\u7D22\uFF09" }
|
|
3195
|
-
},
|
|
3196
|
-
required: ["name"]
|
|
3197
|
-
}
|
|
3198
|
-
},
|
|
3199
|
-
{
|
|
3200
|
-
name: "list_symbols",
|
|
3201
|
-
description: "\u5217\u51FA @lisp \u6240\u6709\u7B26\u53F7\uFF0C\u7528\u4E8E\u68C0\u67E5\u51FD\u6570\u548C\u53D8\u91CF",
|
|
3202
|
-
inputSchema: {
|
|
3203
|
-
type: "object",
|
|
3204
|
-
properties: {
|
|
3205
|
-
package: { type: "string", description: "\u5305\u540D\uFF0C\u5982 string\uFF08\u53EF\u9009\uFF09" }
|
|
3206
|
-
}
|
|
3207
|
-
}
|
|
3208
|
-
},
|
|
3209
|
-
{
|
|
3210
|
-
name: "import_funlib",
|
|
3211
|
-
description: "\u4ECE\u7F51\u7EDC\u5BFC\u5165 @lisp \u51FD\u6570\u5E93\u6570\u636E\u5230 AI Agent",
|
|
3212
|
-
inputSchema: {
|
|
3213
|
-
type: "object",
|
|
3214
|
-
properties: {
|
|
3215
|
-
format: { type: "string", enum: ["json", "list"], description: "\u8FD4\u56DE\u683C\u5F0F\uFF1Ajson\uFF08\u5B8C\u6574JSON\uFF09\u6216 list\uFF08\u51FD\u6570\u540D:\u63CF\u8FF0 \u5217\u8868\uFF09\uFF0C\u9ED8\u8BA4 json" }
|
|
3216
|
-
}
|
|
3217
|
-
}
|
|
3218
|
-
},
|
|
3219
|
-
{
|
|
3220
|
-
name: "get_system_status",
|
|
3221
|
-
description: "\u83B7\u53D6\u5B8C\u6574\u7CFB\u7EDF\u8BCA\u65AD\u4FE1\u606F\uFF1AMCP \u72B6\u6001\u3001CAD \u8FDE\u63A5\u3001Lisp \u6267\u884C\u3001\u5305\u5217\u8868",
|
|
3222
|
-
inputSchema: { type: "object", properties: {} }
|
|
3223
|
-
},
|
|
3224
|
-
{
|
|
3225
|
-
name: "list_prompts",
|
|
3226
|
-
description: "\u5217\u51FA\u6240\u6709\u53EF\u7528\u7684 @lisp \u63D0\u793A\u6A21\u677F",
|
|
3227
|
-
inputSchema: { type: "object", properties: {} }
|
|
3228
|
-
},
|
|
3229
|
-
{
|
|
3230
|
-
name: "get_prompt",
|
|
3231
|
-
description: "\u83B7\u53D6\u6307\u5B9A\u63D0\u793A\u6A21\u677F\u7684\u5185\u5BB9",
|
|
3232
|
-
inputSchema: {
|
|
3233
|
-
type: "object",
|
|
3234
|
-
properties: {
|
|
3235
|
-
name: { type: "string", description: "\u63D0\u793A\u6A21\u677F\u540D\u79F0" },
|
|
3236
|
-
args: { type: "object", description: "\u6A21\u677F\u53C2\u6570\uFF08\u53EF\u9009\uFF09" }
|
|
3237
|
-
},
|
|
3238
|
-
required: ["name"]
|
|
3333
|
+
function validateToolArgs(name, args) {
|
|
3334
|
+
const schema = toolInputSchema[name];
|
|
3335
|
+
if (!schema) return true;
|
|
3336
|
+
for (const [key, type] of Object.entries(schema)) {
|
|
3337
|
+
if (type.endsWith("?")) continue;
|
|
3338
|
+
if (!args[key]) {
|
|
3339
|
+
throw new Error(`Missing required argument: ${key}`);
|
|
3239
3340
|
}
|
|
3240
3341
|
}
|
|
3241
|
-
|
|
3342
|
+
return true;
|
|
3343
|
+
}
|
|
3242
3344
|
async function handleToolCall(name, args) {
|
|
3243
3345
|
const handler = TOOL_HANDLERS[name];
|
|
3244
|
-
if (!handler)
|
|
3245
|
-
return { content: [{ type: "text", text: `\u672A\u77E5\u5DE5\u5177: ${name}` }], isError: true };
|
|
3346
|
+
if (!handler) return { content: [{ type: "text", text: `\u672A\u77E5\u5DE5\u5177: ${name}` }], isError: true };
|
|
3246
3347
|
try {
|
|
3348
|
+
validateToolArgs(name, args || {});
|
|
3247
3349
|
const result = await handler(args || {});
|
|
3248
3350
|
notifyResourceChanges(name);
|
|
3249
3351
|
return result;
|
|
3250
3352
|
} catch (e) {
|
|
3251
|
-
|
|
3353
|
+
error(`Tool call error [${name}]: ${e.message}`, { tool: name, args });
|
|
3252
3354
|
return { content: [{ type: "text", text: `\u5DE5\u5177\u6267\u884C\u9519\u8BEF: ${e.message}` }], isError: true };
|
|
3253
3355
|
}
|
|
3254
3356
|
}
|
|
@@ -3275,226 +3377,38 @@ async function initCadConnection() {
|
|
|
3275
3377
|
try {
|
|
3276
3378
|
const connected = await cad.connect();
|
|
3277
3379
|
if (connected) {
|
|
3278
|
-
log(
|
|
3279
|
-
console.error(
|
|
3380
|
+
log(`Auto-connected to CAD: ${cad.getPlatform()} ${cad.getVersion()}`);
|
|
3381
|
+
console.error(`\u5DF2\u81EA\u52A8\u8FDE\u63A5\u5230 CAD: ${cad.getPlatform()} ${cad.getVersion()}`);
|
|
3280
3382
|
} else {
|
|
3281
3383
|
log("No CAD found on startup");
|
|
3282
3384
|
}
|
|
3283
3385
|
} catch (e) {
|
|
3284
|
-
|
|
3386
|
+
error(`Auto-connect failed: ${e.message}`);
|
|
3285
3387
|
}
|
|
3286
3388
|
}
|
|
3287
|
-
var sessionServers = /* @__PURE__ */ new Map();
|
|
3288
|
-
var sessionTransports = /* @__PURE__ */ new Map();
|
|
3289
|
-
function createMcpServer() {
|
|
3290
|
-
const server = new Server(
|
|
3291
|
-
{ name: SERVER_NAME, version: SERVER_VERSION },
|
|
3292
|
-
{ capabilities: SERVER_CAPABILITIES }
|
|
3293
|
-
);
|
|
3294
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));
|
|
3295
|
-
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
3296
|
-
const { name, arguments: args } = request.params;
|
|
3297
|
-
return await handleToolCall(name, args || {});
|
|
3298
|
-
});
|
|
3299
|
-
server.setRequestHandler(ListResourcesRequestSchema, async (request) => {
|
|
3300
|
-
const ctx = mcpContext.getStore();
|
|
3301
|
-
const sessionId = ctx?.sessionId;
|
|
3302
|
-
const subscribedUris = sessionId ? list(sessionId) : list();
|
|
3303
|
-
return {
|
|
3304
|
-
resources: await listResources(subscribedUris)
|
|
3305
|
-
};
|
|
3306
|
-
});
|
|
3307
|
-
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
3308
|
-
const { uri } = request.params;
|
|
3309
|
-
try {
|
|
3310
|
-
const data = await readResource(uri);
|
|
3311
|
-
return {
|
|
3312
|
-
contents: [{
|
|
3313
|
-
uri,
|
|
3314
|
-
mimeType: "application/json",
|
|
3315
|
-
text: JSON.stringify(data)
|
|
3316
|
-
}]
|
|
3317
|
-
};
|
|
3318
|
-
} catch (e) {
|
|
3319
|
-
return {
|
|
3320
|
-
contents: [],
|
|
3321
|
-
isError: true,
|
|
3322
|
-
error: { code: -32603, message: e.message }
|
|
3323
|
-
};
|
|
3324
|
-
}
|
|
3325
|
-
});
|
|
3326
|
-
server.setRequestHandler(SubscribeRequestSchema, async (request) => {
|
|
3327
|
-
const { uri } = request.params;
|
|
3328
|
-
const ctx = mcpContext.getStore();
|
|
3329
|
-
subscribe(uri, ctx?.sessionId);
|
|
3330
|
-
return { success: true };
|
|
3331
|
-
});
|
|
3332
|
-
server.setRequestHandler(UnsubscribeRequestSchema, async (request) => {
|
|
3333
|
-
const { uri } = request.params;
|
|
3334
|
-
const ctx = mcpContext.getStore();
|
|
3335
|
-
unsubscribe(uri, ctx?.sessionId);
|
|
3336
|
-
return { success: true };
|
|
3337
|
-
});
|
|
3338
|
-
server.setRequestHandler(ListPromptsRequestSchema, async () => ({
|
|
3339
|
-
prompts: await listPrompts()
|
|
3340
|
-
}));
|
|
3341
|
-
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
3342
|
-
const { name, arguments: args } = request.params;
|
|
3343
|
-
return await getPrompt(name, args || {});
|
|
3344
|
-
});
|
|
3345
|
-
return server;
|
|
3346
|
-
}
|
|
3347
3389
|
async function startServer() {
|
|
3348
3390
|
if (config_default.transport === "stdio") {
|
|
3349
3391
|
await initCadConnection();
|
|
3350
|
-
const mcpServer =
|
|
3351
|
-
const
|
|
3392
|
+
const mcpServer = createStdioServer({ tools, handleToolCall });
|
|
3393
|
+
const transport = new StdioServerTransport();
|
|
3352
3394
|
const sessionId = "stdio-session";
|
|
3353
3395
|
sessionManager.createSession(sessionId);
|
|
3354
3396
|
await mcpContext.run({ sessionId }, async () => {
|
|
3355
|
-
await mcpServer.connect(
|
|
3397
|
+
await mcpServer.connect(transport);
|
|
3356
3398
|
});
|
|
3357
3399
|
console.error(`${SERVER_NAME} \u8FD0\u884C\u5728 stdio \u6A21\u5F0F`);
|
|
3358
3400
|
setNotify((uri, data) => {
|
|
3359
3401
|
mcpServer.sendResourceUpdated({ uri }).catch((err) => {
|
|
3360
|
-
|
|
3402
|
+
error(`Failed to send resource update: ${err.message}`);
|
|
3361
3403
|
});
|
|
3362
3404
|
});
|
|
3363
3405
|
} else {
|
|
3364
|
-
const app =
|
|
3365
|
-
|
|
3366
|
-
app.use(async (req, res, next) => {
|
|
3367
|
-
const ct = (req.headers["content-type"] || "").toLowerCase();
|
|
3368
|
-
if (!JSON_CONTENT_TYPES.some((t) => ct.startsWith(t)))
|
|
3369
|
-
return next();
|
|
3370
|
-
try {
|
|
3371
|
-
const buf = await rawBody(req, { limit: "10mb" });
|
|
3372
|
-
const m = ct.match(/charset\s*=\s*([^\s;]+)/i);
|
|
3373
|
-
let charset = m ? m[1].toLowerCase() : "utf-8";
|
|
3374
|
-
const charsetMap = { "gb2312": "gbk", "gb18030": "gbk", "gbk": "gbk", "936": "gbk" };
|
|
3375
|
-
charset = charsetMap[charset] || charset;
|
|
3376
|
-
const str = encodingExists(charset) ? charsetDecode(buf, charset) : buf.toString("utf-8");
|
|
3377
|
-
req.body = JSON.parse(str);
|
|
3378
|
-
next();
|
|
3379
|
-
} catch (e) {
|
|
3380
|
-
res.status(400).json({ error: "Invalid JSON body", message: e.message });
|
|
3381
|
-
}
|
|
3382
|
-
});
|
|
3383
|
-
app.use((req, res, next) => {
|
|
3384
|
-
log(`${req.method} ${req.path}`);
|
|
3385
|
-
next();
|
|
3386
|
-
});
|
|
3387
|
-
const PUBLIC_PATHS = ["/health"];
|
|
3388
|
-
if (apiKey) {
|
|
3389
|
-
app.use((req, res, next) => {
|
|
3390
|
-
if (PUBLIC_PATHS.includes(req.path))
|
|
3391
|
-
return next();
|
|
3392
|
-
const auth = req.get("Authorization");
|
|
3393
|
-
if (auth === `Bearer ${apiKey}`)
|
|
3394
|
-
return next();
|
|
3395
|
-
res.status(401).json({ error: "Unauthorized" });
|
|
3396
|
-
});
|
|
3397
|
-
}
|
|
3398
|
-
app.get("/health", (req, res) => {
|
|
3399
|
-
res.json({ status: "ok", cad: cad.connected ? "connected" : "disconnected" });
|
|
3400
|
-
});
|
|
3401
|
-
if (config_default.enableCors) {
|
|
3402
|
-
app.use((req, res, next) => {
|
|
3403
|
-
res.setHeader("Access-Control-Allow-Origin", config_default.corsOrigin);
|
|
3404
|
-
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS, CONNECT");
|
|
3405
|
-
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Accept, mcp-session-id, last-event-id");
|
|
3406
|
-
res.setHeader("Access-Control-Expose-Headers", "mcp-session-id, content-type, x-accel-buffering");
|
|
3407
|
-
res.setHeader("Access-Control-Max-Age", "86400");
|
|
3408
|
-
res.setHeader("Vary", "Accept");
|
|
3409
|
-
if (req.method === "OPTIONS")
|
|
3410
|
-
return res.status(204).end();
|
|
3411
|
-
next();
|
|
3412
|
-
});
|
|
3413
|
-
}
|
|
3406
|
+
const app = express2();
|
|
3407
|
+
setupRoutes(app);
|
|
3414
3408
|
setNotify((uri, data) => {
|
|
3415
3409
|
broadcastToAllSessions(uri);
|
|
3416
|
-
for (const session of
|
|
3417
|
-
|
|
3418
|
-
}
|
|
3419
|
-
});
|
|
3420
|
-
async function handleMcpPost(req, res) {
|
|
3421
|
-
const sessionId = req.headers["mcp-session-id"] || req.query.sessionId || randomUUID4();
|
|
3422
|
-
sessionManager.createSession(sessionId);
|
|
3423
|
-
const body = req.body;
|
|
3424
|
-
if (!body || !body.method) {
|
|
3425
|
-
return res.status(400).json({ error: "Invalid request" });
|
|
3426
|
-
}
|
|
3427
|
-
const { server, transport: transport2 } = getOrCreateSessionServer(sessionId);
|
|
3428
|
-
transport2.resetCleanupTimer();
|
|
3429
|
-
await mcpContext.run({ sessionId }, async () => {
|
|
3430
|
-
try {
|
|
3431
|
-
const response = await transport2.handleRequest(body);
|
|
3432
|
-
if (response) {
|
|
3433
|
-
const headers = { "Content-Type": "application/json" };
|
|
3434
|
-
if (transport2.sessionId) {
|
|
3435
|
-
headers["mcp-session-id"] = transport2.sessionId;
|
|
3436
|
-
}
|
|
3437
|
-
res.writeHead(200, headers);
|
|
3438
|
-
res.end(JSON.stringify(response));
|
|
3439
|
-
} else {
|
|
3440
|
-
if (!res.headersSent) {
|
|
3441
|
-
res.status(202).end();
|
|
3442
|
-
}
|
|
3443
|
-
}
|
|
3444
|
-
} catch (error) {
|
|
3445
|
-
log(`Transport error [${sessionId}]: ${error.message}`);
|
|
3446
|
-
if (!res.headersSent) {
|
|
3447
|
-
res.status(500).json({ error: "Internal server error", message: error.message });
|
|
3448
|
-
}
|
|
3449
|
-
}
|
|
3450
|
-
});
|
|
3451
|
-
}
|
|
3452
|
-
app.all("/mcp", mcpLimiter, handleMcpPost);
|
|
3453
|
-
app.all("/mcp/:path", mcpLimiter, handleMcpPost);
|
|
3454
|
-
app.get("/mcp/stream", mcpLimiter, handleMcpPost);
|
|
3455
|
-
const sseEndpoint = "/sse";
|
|
3456
|
-
app.get(sseEndpoint, async (req, res) => {
|
|
3457
|
-
const sessionId = randomUUID4();
|
|
3458
|
-
sessionManager.createSession(sessionId);
|
|
3459
|
-
getOrCreateSessionServer(sessionId);
|
|
3460
|
-
const sseSession = new SseSession(res, { clientId: sessionId });
|
|
3461
|
-
sseSessionManager.add(sessionId, sseSession);
|
|
3462
|
-
sseSession.sendEndpoint(`/message?sessionId=${sessionId}`);
|
|
3463
|
-
req.on("close", () => {
|
|
3464
|
-
sseSessionManager.remove(sessionId);
|
|
3465
|
-
removeSessionServer(sessionId);
|
|
3466
|
-
});
|
|
3467
|
-
});
|
|
3468
|
-
app.post("/message", mcpLimiter, async (req, res) => {
|
|
3469
|
-
const sessionId = req.query.sessionId;
|
|
3470
|
-
if (!sessionId) {
|
|
3471
|
-
return res.status(400).json({ error: "Missing sessionId" });
|
|
3472
|
-
}
|
|
3473
|
-
const sseSession = sseSessionManager.get(sessionId);
|
|
3474
|
-
if (!sseSession) {
|
|
3475
|
-
return res.status(404).json({ error: "Session not found" });
|
|
3476
|
-
}
|
|
3477
|
-
sessionManager.createSession(sessionId);
|
|
3478
|
-
const { server, transport: transport2 } = getOrCreateSessionServer(sessionId);
|
|
3479
|
-
transport2.resetCleanupTimer();
|
|
3480
|
-
const body = req.body;
|
|
3481
|
-
if (body && body.method) {
|
|
3482
|
-
try {
|
|
3483
|
-
const response = await transport2.handleRequest(body);
|
|
3484
|
-
if (response) {
|
|
3485
|
-
sseSession.sendResponse(response);
|
|
3486
|
-
}
|
|
3487
|
-
if (!res.headersSent) {
|
|
3488
|
-
res.status(202).end();
|
|
3489
|
-
}
|
|
3490
|
-
} catch (error) {
|
|
3491
|
-
log(`SSE message error [${sessionId}]: ${error.message}`);
|
|
3492
|
-
if (!res.headersSent) {
|
|
3493
|
-
res.status(500).json({ error: "Internal server error" });
|
|
3494
|
-
}
|
|
3495
|
-
}
|
|
3496
|
-
} else {
|
|
3497
|
-
res.status(400).json({ error: "Invalid request" });
|
|
3410
|
+
for (const session of sseSessionManager2.listActive()) {
|
|
3411
|
+
sseSessionManager2.sendToClient(session.clientId, "resource_updated", { uri });
|
|
3498
3412
|
}
|
|
3499
3413
|
});
|
|
3500
3414
|
const httpServer = app.listen(config_default.port, config_default.host, async () => {
|
|
@@ -3504,7 +3418,7 @@ async function startServer() {
|
|
|
3504
3418
|
async function shutdown(signal) {
|
|
3505
3419
|
console.error(`
|
|
3506
3420
|
\u6536\u5230 ${signal}\uFF0C\u6B63\u5728\u4F18\u96C5\u5173\u95ED...`);
|
|
3507
|
-
|
|
3421
|
+
sseSessionManager2.closeAll();
|
|
3508
3422
|
sessionManager.clear();
|
|
3509
3423
|
for (const [sid] of sessionServers) {
|
|
3510
3424
|
removeSessionServer(sid);
|
|
@@ -3518,14 +3432,285 @@ async function startServer() {
|
|
|
3518
3432
|
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
3519
3433
|
}
|
|
3520
3434
|
}
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3435
|
+
var __dirname4, require2, version, sseSessionManager2, isMcpScript, CACHE_DIR2, FUNCLIB_CACHE_FILE2, CACHE_TTL3, FUNCTION_LIB_URL, cachedFunctionLib, cachedAt2, toolInputSchema, TOOL_HANDLERS, tools;
|
|
3436
|
+
var init_atlisp_mcp = __esm({
|
|
3437
|
+
async "src/atlisp-mcp.js"() {
|
|
3438
|
+
init_cad();
|
|
3439
|
+
init_constants();
|
|
3440
|
+
init_logger();
|
|
3441
|
+
init_cad_handlers();
|
|
3442
|
+
init_package_handlers();
|
|
3443
|
+
init_function_handlers();
|
|
3444
|
+
init_resource_handlers();
|
|
3445
|
+
init_prompt_handlers();
|
|
3446
|
+
init_subscription_manager();
|
|
3447
|
+
init_subscription_manager();
|
|
3448
|
+
init_sse_session_manager();
|
|
3449
|
+
init_sse_session();
|
|
3450
|
+
init_config();
|
|
3451
|
+
init_routes();
|
|
3452
|
+
init_mcp_server_state();
|
|
3453
|
+
init_constants();
|
|
3454
|
+
__dirname4 = path7.dirname(fileURLToPath4(import.meta.url));
|
|
3455
|
+
require2 = createRequire(import.meta.url);
|
|
3456
|
+
({ version } = require2(path7.join(__dirname4, "..", "package.json")));
|
|
3457
|
+
sseSessionManager2 = new SseSessionManager();
|
|
3458
|
+
isMcpScript = process.argv[1]?.includes("atlisp-mcp");
|
|
3459
|
+
if (isMcpScript) {
|
|
3460
|
+
const args = process.argv.slice(2);
|
|
3461
|
+
for (let i = 0; i < args.length; i++) {
|
|
3462
|
+
if (args[i] === "--version" || args[i] === "-v") {
|
|
3463
|
+
console.log(`atlisp-mcp v${version}`);
|
|
3464
|
+
process.exit(0);
|
|
3465
|
+
} else if (args[i] === "--help" || args[i] === "-h") {
|
|
3466
|
+
console.log(`
|
|
3467
|
+
@lisp MCP Server v${version}
|
|
3468
|
+
|
|
3469
|
+
Usage: atlisp-mcp [options]
|
|
3470
|
+
|
|
3471
|
+
Options:
|
|
3472
|
+
-v, --version Show version number
|
|
3473
|
+
--transport <type> Transport type: http or stdio (default: http)
|
|
3474
|
+
--port <port> HTTP port (default: 8110)
|
|
3475
|
+
--host <host> HTTP host (default: 0.0.0.0)
|
|
3476
|
+
--stdio Shorthand for --transport stdio
|
|
3477
|
+
-h, --help Show this help message
|
|
3478
|
+
|
|
3479
|
+
Examples:
|
|
3480
|
+
atlisp-mcp # Start HTTP server on port 8110
|
|
3481
|
+
atlisp-mcp --port 3000 # Start HTTP server on port 3000
|
|
3482
|
+
atlisp-mcp --stdio # Start in stdio mode for MCP clients
|
|
3483
|
+
atlisp-mcp --transport stdio # Same as --stdio
|
|
3484
|
+
`);
|
|
3485
|
+
process.exit(0);
|
|
3486
|
+
} else if (args[i] === "--transport" && args[i + 1]) {
|
|
3487
|
+
process.env.TRANSPORT = args[i + 1];
|
|
3488
|
+
i++;
|
|
3489
|
+
} else if (args[i] === "--stdio") {
|
|
3490
|
+
process.env.TRANSPORT = "stdio";
|
|
3491
|
+
} else if (args[i] === "--port" && args[i + 1]) {
|
|
3492
|
+
process.env.PORT = args[i + 1];
|
|
3493
|
+
i++;
|
|
3494
|
+
} else if (args[i] === "--host" && args[i + 1]) {
|
|
3495
|
+
process.env.HOST = args[i + 1];
|
|
3496
|
+
i++;
|
|
3497
|
+
}
|
|
3498
|
+
}
|
|
3499
|
+
}
|
|
3500
|
+
if (config_default.apiKey) log("API Key authentication enabled");
|
|
3501
|
+
CACHE_DIR2 = path7.join(os4.homedir(), ".atlisp", "cache");
|
|
3502
|
+
FUNCLIB_CACHE_FILE2 = path7.join(CACHE_DIR2, "functions.json");
|
|
3503
|
+
CACHE_TTL3 = config_default.functionLibCacheTtl || 5 * 60 * 1e3;
|
|
3504
|
+
FUNCTION_LIB_URL = config_default.functionLibUrl || "http://s3.atlisp.cn/json/functions.json";
|
|
3505
|
+
cachedFunctionLib = null;
|
|
3506
|
+
cachedAt2 = null;
|
|
3507
|
+
toolInputSchema = {
|
|
3508
|
+
connect_cad: { platform: "string?" },
|
|
3509
|
+
eval_lisp: { code: "string" },
|
|
3510
|
+
eval_lisp_with_result: { code: "string", encoding: "string?" },
|
|
3511
|
+
search_packages: { query: "string" },
|
|
3512
|
+
install_package: { packageName: "string" },
|
|
3513
|
+
at_command: { command: "string" },
|
|
3514
|
+
get_function_usage: { name: "string", package: "string?" },
|
|
3515
|
+
list_symbols: { package: "string?" },
|
|
3516
|
+
import_funlib: { format: "string?" },
|
|
3517
|
+
get_prompt: { name: "string", args: "object?" }
|
|
3518
|
+
};
|
|
3519
|
+
TOOL_HANDLERS = {
|
|
3520
|
+
connect_cad: (a) => connectCad(a?.platform),
|
|
3521
|
+
eval_lisp: (a) => evalLisp(a.code),
|
|
3522
|
+
eval_lisp_with_result: (a) => evalLisp(a.code, true, a.encoding),
|
|
3523
|
+
get_cad_info: () => getCadInfo(),
|
|
3524
|
+
list_packages: () => listPackages(),
|
|
3525
|
+
search_packages: (a) => searchPackages(a.query),
|
|
3526
|
+
install_package: (a) => installPackage(a.packageName),
|
|
3527
|
+
get_platform_info: () => ({ content: [{ type: "text", text: `\u652F\u6301\u7684 CAD \u5E73\u53F0:
|
|
3528
|
+
${CAD_PLATFORMS.join("\n")}
|
|
3529
|
+
\u5F53\u524D\u8FDE\u63A5: ${cad.connected ? "\u5DF2\u8FDE\u63A5" : "\u672A\u8FDE\u63A5"}` }] }),
|
|
3530
|
+
at_command: (a) => atCommand(a.command),
|
|
3531
|
+
new_document: () => newDocument(),
|
|
3532
|
+
bring_to_front: () => bringToFront(),
|
|
3533
|
+
install_atlisp: () => installAtlisp(),
|
|
3534
|
+
init_atlisp: () => initAtlisp(),
|
|
3535
|
+
get_function_usage: (a) => getFunctionUsage(a.name, a.package),
|
|
3536
|
+
list_symbols: (a) => listSymbols(a.package),
|
|
3537
|
+
get_system_status: () => getSystemStatus(),
|
|
3538
|
+
list_prompts: async () => {
|
|
3539
|
+
const prompts = await listPrompts();
|
|
3540
|
+
return { content: [{ type: "text", text: JSON.stringify(prompts) }] };
|
|
3541
|
+
},
|
|
3542
|
+
get_prompt: async (a) => {
|
|
3543
|
+
const prompt = await getPrompt(a.name, a.args || {});
|
|
3544
|
+
return { content: prompt.messages.map((m) => ({ type: "text", text: m.content.text || m.content })) };
|
|
3545
|
+
},
|
|
3546
|
+
import_funlib: async (a) => {
|
|
3547
|
+
const data = await loadAtlibFunctionLib();
|
|
3548
|
+
if (!data) return { content: [{ type: "text", text: "\u52A0\u8F7D\u51FD\u6570\u5E93\u5931\u8D25" }], isError: true };
|
|
3549
|
+
if (a.format === "list") {
|
|
3550
|
+
const items = Object.values(data.all_functions || {}).map((f) => `${f.name}: ${f.description || ""}`);
|
|
3551
|
+
return { content: [{ type: "text", text: items.join("\n") }] };
|
|
3552
|
+
}
|
|
3553
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
3554
|
+
}
|
|
3555
|
+
};
|
|
3556
|
+
tools = [
|
|
3557
|
+
{
|
|
3558
|
+
name: "connect_cad",
|
|
3559
|
+
description: "\u8FDE\u63A5\u5230 CAD (AutoCAD/ZWCAD/GStarCAD/BricsCAD)",
|
|
3560
|
+
inputSchema: {
|
|
3561
|
+
type: "object",
|
|
3562
|
+
properties: {
|
|
3563
|
+
platform: { type: "string", description: "\u6307\u5B9A CAD \u5E73\u53F0: AutoCAD, ZWCAD, GStarCAD, BricsCAD\uFF08\u53EF\u9009\uFF0C\u81EA\u52A8\u68C0\u6D4B\uFF09" }
|
|
3564
|
+
}
|
|
3565
|
+
}
|
|
3566
|
+
},
|
|
3567
|
+
{
|
|
3568
|
+
name: "eval_lisp",
|
|
3569
|
+
description: "\u5728 CAD \u4E2D\u6267\u884C AutoLISP \u4EE3\u7801\uFF08\u4E0D\u8FD4\u56DE\u7ED3\u679C\uFF09",
|
|
3570
|
+
inputSchema: {
|
|
3571
|
+
type: "object",
|
|
3572
|
+
properties: { code: { type: "string", description: "\u8981\u6267\u884C\u7684 LISP \u4EE3\u7801" } },
|
|
3573
|
+
required: ["code"]
|
|
3574
|
+
}
|
|
3575
|
+
},
|
|
3576
|
+
{
|
|
3577
|
+
name: "eval_lisp_with_result",
|
|
3578
|
+
description: "\u5728 CAD \u4E2D\u6267\u884C AutoLISP \u4EE3\u7801\u5E76\u8FD4\u56DE\u7ED3\u679C",
|
|
3579
|
+
inputSchema: {
|
|
3580
|
+
type: "object",
|
|
3581
|
+
properties: {
|
|
3582
|
+
code: { type: "string", description: "\u8981\u6267\u884C\u7684 LISP \u4EE3\u7801" },
|
|
3583
|
+
encoding: { type: "string", description: "\u7ED3\u679C\u7F16\u7801\uFF0C\u5982 utf-8, gbk, gb2312, gb18030\uFF08\u53EF\u9009\uFF0C\u9ED8\u8BA4\u81EA\u52A8\u68C0\u6D4B\uFF09" }
|
|
3584
|
+
},
|
|
3585
|
+
required: ["code"]
|
|
3586
|
+
}
|
|
3587
|
+
},
|
|
3588
|
+
{
|
|
3589
|
+
name: "get_cad_info",
|
|
3590
|
+
description: "\u83B7\u53D6\u5F53\u524D CAD \u4FE1\u606F",
|
|
3591
|
+
inputSchema: { type: "object", properties: {} }
|
|
3592
|
+
},
|
|
3593
|
+
{
|
|
3594
|
+
name: "list_packages",
|
|
3595
|
+
description: "\u5217\u51FA\u5DF2\u5B89\u88C5\u7684 @lisp \u5305",
|
|
3596
|
+
inputSchema: { type: "object", properties: {} }
|
|
3597
|
+
},
|
|
3598
|
+
{
|
|
3599
|
+
name: "search_packages",
|
|
3600
|
+
description: "\u641C\u7D22 @lisp \u5305",
|
|
3601
|
+
inputSchema: {
|
|
3602
|
+
type: "object",
|
|
3603
|
+
properties: { query: { type: "string", description: "\u641C\u7D22\u5173\u952E\u8BCD" } },
|
|
3604
|
+
required: ["query"]
|
|
3605
|
+
}
|
|
3606
|
+
},
|
|
3607
|
+
{
|
|
3608
|
+
name: "install_package",
|
|
3609
|
+
description: "\u5B89\u88C5 @lisp \u5305\u5230 CAD",
|
|
3610
|
+
inputSchema: {
|
|
3611
|
+
type: "object",
|
|
3612
|
+
properties: { packageName: { type: "string", description: "\u5305\u540D\u79F0" } },
|
|
3613
|
+
required: ["packageName"]
|
|
3614
|
+
}
|
|
3615
|
+
},
|
|
3616
|
+
{
|
|
3617
|
+
name: "get_platform_info",
|
|
3618
|
+
description: "\u83B7\u53D6 CAD \u5E73\u53F0\u4FE1\u606F",
|
|
3619
|
+
inputSchema: { type: "object", properties: {} }
|
|
3620
|
+
},
|
|
3621
|
+
{
|
|
3622
|
+
name: "at_command",
|
|
3623
|
+
description: "\u6267\u884C @lisp \u547D\u4EE4",
|
|
3624
|
+
inputSchema: {
|
|
3625
|
+
type: "object",
|
|
3626
|
+
properties: { command: { type: "string", description: "@lisp \u547D\u4EE4" } },
|
|
3627
|
+
required: ["command"]
|
|
3628
|
+
}
|
|
3629
|
+
},
|
|
3630
|
+
{
|
|
3631
|
+
name: "new_document",
|
|
3632
|
+
description: "\u5728 CAD \u4E2D\u65B0\u5EFA\u7A7A\u767D\u6587\u6863",
|
|
3633
|
+
inputSchema: { type: "object", properties: {} }
|
|
3634
|
+
},
|
|
3635
|
+
{
|
|
3636
|
+
name: "bring_to_front",
|
|
3637
|
+
description: "\u5C06 CAD \u7A97\u53E3\u5207\u6362\u5230\u524D\u53F0\uFF08\u4ECE\u540E\u53F0\u8FD0\u884C\u72B6\u6001\u5524\u9192\uFF09",
|
|
3638
|
+
inputSchema: { type: "object", properties: {} }
|
|
3639
|
+
},
|
|
3640
|
+
{
|
|
3641
|
+
name: "install_atlisp",
|
|
3642
|
+
description: "\u5728 CAD \u4E2D\u5B89\u88C5 @lisp",
|
|
3643
|
+
inputSchema: { type: "object", properties: {} }
|
|
3644
|
+
},
|
|
3645
|
+
{
|
|
3646
|
+
name: "init_atlisp",
|
|
3647
|
+
description: "\u521D\u59CB\u5316 CAD \u4E2D\u7684 @lisp \u73AF\u5883\uFF0C\u52A0\u8F7D\u51FD\u6570\u5E93\u548C\u521D\u59CB\u53D8\u91CF",
|
|
3648
|
+
inputSchema: { type: "object", properties: {} }
|
|
3649
|
+
},
|
|
3650
|
+
{
|
|
3651
|
+
name: "get_function_usage",
|
|
3652
|
+
description: "\u83B7\u53D6 @lisp \u51FD\u6570\u7528\u6CD5",
|
|
3653
|
+
inputSchema: {
|
|
3654
|
+
type: "object",
|
|
3655
|
+
properties: {
|
|
3656
|
+
name: { type: "string", description: "\u51FD\u6570\u540D\uFF0C\u5982 string:length" },
|
|
3657
|
+
package: { type: "string", description: "\u5305\u540D\uFF0C\u5982 string\uFF08\u53EF\u9009\uFF0C\u4E0D\u4F20\u5219\u81EA\u52A8\u641C\u7D22\uFF09" }
|
|
3658
|
+
},
|
|
3659
|
+
required: ["name"]
|
|
3660
|
+
}
|
|
3661
|
+
},
|
|
3662
|
+
{
|
|
3663
|
+
name: "list_symbols",
|
|
3664
|
+
description: "\u5217\u51FA @lisp \u6240\u6709\u7B26\u53F7\uFF0C\u7528\u4E8E\u68C0\u67E5\u51FD\u6570\u548C\u53D8\u91CF",
|
|
3665
|
+
inputSchema: {
|
|
3666
|
+
type: "object",
|
|
3667
|
+
properties: {
|
|
3668
|
+
package: { type: "string", description: "\u5305\u540D\uFF08\u53EF\u9009\uFF09" }
|
|
3669
|
+
}
|
|
3670
|
+
}
|
|
3671
|
+
},
|
|
3672
|
+
{
|
|
3673
|
+
name: "import_funlib",
|
|
3674
|
+
description: "\u4ECE\u7F51\u7EDC\u5BFC\u5165 @lisp \u51FD\u6570\u5E93\u6570\u636E\u5230 AI Agent",
|
|
3675
|
+
inputSchema: {
|
|
3676
|
+
type: "object",
|
|
3677
|
+
properties: {
|
|
3678
|
+
format: { type: "string", enum: ["json", "list"], description: "\u8FD4\u56DE\u683C\u5F0F\uFF1Ajson\uFF08\u5B8C\u6574JSON\uFF09\u6216 list\uFF08\u51FD\u6570\u540D:\u63CF\u8FF0 \u5217\u8868\uFF09\uFF0C\u9ED8\u8BA4 json" }
|
|
3679
|
+
}
|
|
3680
|
+
}
|
|
3681
|
+
},
|
|
3682
|
+
{
|
|
3683
|
+
name: "get_system_status",
|
|
3684
|
+
description: "\u83B7\u53D6\u5B8C\u6574\u7CFB\u7EDF\u8BCA\u65AD\u4FE1\u606F\uFF1AMCP \u72B6\u6001\u3001CAD \u8FDE\u63A5\u3001Lisp \u6267\u884C\u3001\u5305\u5217\u8868",
|
|
3685
|
+
inputSchema: { type: "object", properties: {} }
|
|
3686
|
+
},
|
|
3687
|
+
{
|
|
3688
|
+
name: "list_prompts",
|
|
3689
|
+
description: "\u5217\u51FA\u6240\u6709\u53EF\u7528\u7684 @lisp \u63D0\u793A\u6A21\u677F",
|
|
3690
|
+
inputSchema: { type: "object", properties: {} }
|
|
3691
|
+
},
|
|
3692
|
+
{
|
|
3693
|
+
name: "get_prompt",
|
|
3694
|
+
description: "\u83B7\u53D6\u6307\u5B9A\u63D0\u793A\u6A21\u677F\u7684\u5185\u5BB9",
|
|
3695
|
+
inputSchema: {
|
|
3696
|
+
type: "object",
|
|
3697
|
+
properties: {
|
|
3698
|
+
name: { type: "string", description: "\u63D0\u793A\u6A21\u677F\u540D\u79F0" },
|
|
3699
|
+
args: { type: "object", description: "\u6A21\u677F\u53C2\u6570\uFF08\u53EF\u9009\uFF09" }
|
|
3700
|
+
},
|
|
3701
|
+
required: ["name"]
|
|
3702
|
+
}
|
|
3703
|
+
}
|
|
3704
|
+
];
|
|
3705
|
+
if (!process.env.VITEST) {
|
|
3706
|
+
await startServer();
|
|
3707
|
+
}
|
|
3708
|
+
}
|
|
3709
|
+
});
|
|
3710
|
+
await init_atlisp_mcp();
|
|
3524
3711
|
export {
|
|
3525
3712
|
CAD_PLATFORMS,
|
|
3526
3713
|
FILE_EXTENSIONS,
|
|
3527
|
-
MOCK_PACKAGES,
|
|
3528
|
-
createMcpServer,
|
|
3529
3714
|
handleToolCall,
|
|
3530
3715
|
loadAtlibFunctionLib,
|
|
3531
3716
|
startServer,
|