@atlisp/mcp 1.4.0 → 1.4.2
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 +803 -355
- package/dist/cad-worker.js +48 -0
- package/package.json +1 -1
package/dist/atlisp-mcp.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/atlisp-mcp.js
|
|
4
|
-
import
|
|
5
|
-
import
|
|
4
|
+
import path7 from "path";
|
|
5
|
+
import fs6 from "fs";
|
|
6
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
6
7
|
import { createRequire } from "module";
|
|
8
|
+
import os4 from "os";
|
|
7
9
|
import express from "express";
|
|
8
10
|
import rawBody from "raw-body";
|
|
9
11
|
import iconv from "iconv-lite";
|
|
@@ -34,8 +36,7 @@ var SessionTransport = class {
|
|
|
34
36
|
this._started = true;
|
|
35
37
|
}
|
|
36
38
|
async send(message) {
|
|
37
|
-
if (this._closed)
|
|
38
|
-
return;
|
|
39
|
+
if (this._closed) return;
|
|
39
40
|
if (this._pendingRequest) {
|
|
40
41
|
const { resolve, reject, timeout } = this._pendingRequest;
|
|
41
42
|
clearTimeout(timeout);
|
|
@@ -44,8 +45,7 @@ var SessionTransport = class {
|
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
async close() {
|
|
47
|
-
if (this._closed)
|
|
48
|
-
return;
|
|
48
|
+
if (this._closed) return;
|
|
49
49
|
this._closed = true;
|
|
50
50
|
if (this._pendingRequest) {
|
|
51
51
|
const { reject, timeout } = this._pendingRequest;
|
|
@@ -119,7 +119,7 @@ var FILE_EXTENSIONS = {
|
|
|
119
119
|
"BricsCAD": [".des"]
|
|
120
120
|
};
|
|
121
121
|
var SERVER_NAME = "atlisp-mcp-server";
|
|
122
|
-
var SERVER_VERSION = "1.4.
|
|
122
|
+
var SERVER_VERSION = "1.4.1";
|
|
123
123
|
var MOCK_PACKAGES = [
|
|
124
124
|
{ name: "base", description: "\u57FA\u7840\u51FD\u6570\u5E93", version: "1.0.0" },
|
|
125
125
|
{ name: "at-pm", description: "\u5DE5\u7A0B\u9879\u76EE\u7BA1\u7406", version: "2.1.0" },
|
|
@@ -176,6 +176,8 @@ var config_default = config;
|
|
|
176
176
|
// src/cad.js
|
|
177
177
|
var MESSAGE_TIMEOUT = config_default.messageTimeout;
|
|
178
178
|
var HEARTBEAT_INTERVAL = config_default.heartbeatInterval;
|
|
179
|
+
var MAX_RESTART_ATTEMPTS = 5;
|
|
180
|
+
var RESTART_BACKOFF_BASE = 1e3;
|
|
179
181
|
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
180
182
|
var workerPath = path.join(__dirname, "cad-worker.js");
|
|
181
183
|
var worker = null;
|
|
@@ -184,6 +186,8 @@ var heartbeatTimer = null;
|
|
|
184
186
|
var pendingRequests = /* @__PURE__ */ new Map();
|
|
185
187
|
var stdoutHandlerSetup = false;
|
|
186
188
|
var MAX_BUFFER_SIZE = 1024 * 1024;
|
|
189
|
+
var restartCount = 0;
|
|
190
|
+
var restartTimer = null;
|
|
187
191
|
function resetWorker() {
|
|
188
192
|
for (const [id, { reject, timeout }] of pendingRequests) {
|
|
189
193
|
clearTimeout(timeout);
|
|
@@ -200,6 +204,27 @@ function resetWorker() {
|
|
|
200
204
|
heartbeatTimer = null;
|
|
201
205
|
}
|
|
202
206
|
}
|
|
207
|
+
function scheduleRestart() {
|
|
208
|
+
if (restartTimer) {
|
|
209
|
+
clearTimeout(restartTimer);
|
|
210
|
+
restartTimer = null;
|
|
211
|
+
}
|
|
212
|
+
if (restartCount >= MAX_RESTART_ATTEMPTS) {
|
|
213
|
+
console.error("Max worker restart attempts reached, giving up");
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const delay = Math.min(RESTART_BACKOFF_BASE * Math.pow(2, restartCount), 3e4);
|
|
217
|
+
restartCount++;
|
|
218
|
+
console.error(`Scheduling worker restart in ${delay}ms (attempt ${restartCount}/${MAX_RESTART_ATTEMPTS})`);
|
|
219
|
+
restartTimer = setTimeout(() => {
|
|
220
|
+
restartTimer = null;
|
|
221
|
+
console.error("Attempting worker restart...");
|
|
222
|
+
getWorker(true);
|
|
223
|
+
}, delay);
|
|
224
|
+
}
|
|
225
|
+
function resetRestartCount() {
|
|
226
|
+
restartCount = 0;
|
|
227
|
+
}
|
|
203
228
|
function setupStdoutHandler(w) {
|
|
204
229
|
let buffer = "";
|
|
205
230
|
stdoutHandlerSetup = true;
|
|
@@ -212,8 +237,7 @@ function setupStdoutHandler(w) {
|
|
|
212
237
|
const lines = buffer.split("\n");
|
|
213
238
|
buffer = lines.pop() || "";
|
|
214
239
|
for (const line of lines) {
|
|
215
|
-
if (!line.trim())
|
|
216
|
-
continue;
|
|
240
|
+
if (!line.trim()) continue;
|
|
217
241
|
try {
|
|
218
242
|
const result = JSON.parse(line);
|
|
219
243
|
const rid = result.requestId;
|
|
@@ -235,21 +259,19 @@ function setupStdoutHandler(w) {
|
|
|
235
259
|
console.error("Unexpected error in worker stdout handler:", e.message);
|
|
236
260
|
}
|
|
237
261
|
});
|
|
262
|
+
w.stderr.on("data", (d) => console.error("worker stderr:", d.toString()));
|
|
238
263
|
}
|
|
239
|
-
function getWorker() {
|
|
240
|
-
if (!worker || !worker.connected) {
|
|
241
|
-
if (worker)
|
|
242
|
-
worker.kill();
|
|
264
|
+
function getWorker(force = false) {
|
|
265
|
+
if (!worker || !worker.connected || force) {
|
|
266
|
+
if (worker) worker.kill();
|
|
243
267
|
worker = spawn("node", [workerPath], {
|
|
244
268
|
stdio: ["pipe", "pipe", "pipe"]
|
|
245
269
|
});
|
|
246
270
|
worker.connected = true;
|
|
247
|
-
worker.stderr.on("data", (d) => console.error("worker stderr:", d.toString()));
|
|
248
271
|
const w = worker;
|
|
249
272
|
w.on("exit", (code) => {
|
|
250
|
-
console.error("worker exited:", code);
|
|
251
|
-
if (w !== worker)
|
|
252
|
-
return;
|
|
273
|
+
console.error("worker exited with code:", code);
|
|
274
|
+
if (w !== worker) return;
|
|
253
275
|
w.connected = false;
|
|
254
276
|
for (const [id, { reject, timeout }] of pendingRequests) {
|
|
255
277
|
clearTimeout(timeout);
|
|
@@ -261,15 +283,16 @@ function getWorker() {
|
|
|
261
283
|
clearInterval(heartbeatTimer);
|
|
262
284
|
heartbeatTimer = null;
|
|
263
285
|
}
|
|
286
|
+
scheduleRestart();
|
|
264
287
|
});
|
|
265
288
|
setupStdoutHandler(w);
|
|
266
289
|
startHeartbeat();
|
|
290
|
+
resetRestartCount();
|
|
267
291
|
}
|
|
268
292
|
return worker;
|
|
269
293
|
}
|
|
270
294
|
function startHeartbeat() {
|
|
271
|
-
if (heartbeatTimer)
|
|
272
|
-
clearInterval(heartbeatTimer);
|
|
295
|
+
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
273
296
|
heartbeatTimer = setInterval(async () => {
|
|
274
297
|
try {
|
|
275
298
|
const result = await sendMessage({ type: "ping" });
|
|
@@ -313,11 +336,11 @@ var CadConnection = class {
|
|
|
313
336
|
isAvailable() {
|
|
314
337
|
return process.platform === "win32";
|
|
315
338
|
}
|
|
316
|
-
async connect() {
|
|
317
|
-
if (!this.isAvailable())
|
|
318
|
-
return false;
|
|
339
|
+
async connect(platform = null) {
|
|
340
|
+
if (!this.isAvailable()) return false;
|
|
319
341
|
try {
|
|
320
|
-
const
|
|
342
|
+
const msg = platform ? { type: "connect", platform } : { type: "connect" };
|
|
343
|
+
const result = await sendMessage(msg);
|
|
321
344
|
if (result && result.success) {
|
|
322
345
|
this.version = result.version;
|
|
323
346
|
this.product = result.platform;
|
|
@@ -331,16 +354,13 @@ var CadConnection = class {
|
|
|
331
354
|
return false;
|
|
332
355
|
}
|
|
333
356
|
async sendCommand(code) {
|
|
334
|
-
if (!this.connected)
|
|
335
|
-
throw new Error("\u672A\u8FDE\u63A5 CAD");
|
|
357
|
+
if (!this.connected) throw new Error("\u672A\u8FDE\u63A5 CAD");
|
|
336
358
|
const result = await sendMessage({ type: "send", code, platform: _platform });
|
|
337
|
-
if (result.success)
|
|
338
|
-
return true;
|
|
359
|
+
if (result.success) return true;
|
|
339
360
|
throw new Error(result.error || "\u53D1\u9001\u5931\u8D25");
|
|
340
361
|
}
|
|
341
362
|
async sendCommandWithResult(code, encoding = null) {
|
|
342
|
-
if (!this.connected)
|
|
343
|
-
throw new Error("\u672A\u8FDE\u63A5 CAD");
|
|
363
|
+
if (!this.connected) throw new Error("\u672A\u8FDE\u63A5 CAD");
|
|
344
364
|
const result = await sendMessage({ type: "sendResult", code, platform: _platform, encoding: encoding || "" });
|
|
345
365
|
if (result.success) {
|
|
346
366
|
return result.result || "";
|
|
@@ -354,8 +374,7 @@ var CadConnection = class {
|
|
|
354
374
|
return this.product;
|
|
355
375
|
}
|
|
356
376
|
async isBusy() {
|
|
357
|
-
if (!this.connected)
|
|
358
|
-
return false;
|
|
377
|
+
if (!this.connected) return false;
|
|
359
378
|
try {
|
|
360
379
|
const result = await sendMessage({ type: "isBusy", platform: _platform });
|
|
361
380
|
return result && result.isBusy;
|
|
@@ -364,8 +383,7 @@ var CadConnection = class {
|
|
|
364
383
|
}
|
|
365
384
|
}
|
|
366
385
|
async hasDoc() {
|
|
367
|
-
if (!this.connected)
|
|
368
|
-
return { hasDoc: false, docCount: 0 };
|
|
386
|
+
if (!this.connected) return { hasDoc: false, docCount: 0 };
|
|
369
387
|
try {
|
|
370
388
|
const result = await sendMessage({ type: "hasdoc", platform: _platform });
|
|
371
389
|
return result || { hasDoc: false, docCount: 0 };
|
|
@@ -374,8 +392,7 @@ var CadConnection = class {
|
|
|
374
392
|
}
|
|
375
393
|
}
|
|
376
394
|
async newDoc() {
|
|
377
|
-
if (!this.connected)
|
|
378
|
-
throw new Error("\u672A\u8FDE\u63A5 CAD");
|
|
395
|
+
if (!this.connected) throw new Error("\u672A\u8FDE\u63A5 CAD");
|
|
379
396
|
try {
|
|
380
397
|
const result = await sendMessage({ type: "newdoc", platform: _platform });
|
|
381
398
|
return result && result.success;
|
|
@@ -384,8 +401,7 @@ var CadConnection = class {
|
|
|
384
401
|
}
|
|
385
402
|
}
|
|
386
403
|
async bringToFront() {
|
|
387
|
-
if (!this.connected)
|
|
388
|
-
throw new Error("\u672A\u8FDE\u63A5 CAD");
|
|
404
|
+
if (!this.connected) throw new Error("\u672A\u8FDE\u63A5 CAD");
|
|
389
405
|
try {
|
|
390
406
|
const result = await sendMessage({ type: "bringToFront", platform: _platform });
|
|
391
407
|
return result && result.success;
|
|
@@ -394,8 +410,7 @@ var CadConnection = class {
|
|
|
394
410
|
}
|
|
395
411
|
}
|
|
396
412
|
async getInfo() {
|
|
397
|
-
if (!this.connected)
|
|
398
|
-
return "CAD \u672A\u8FDE\u63A5";
|
|
413
|
+
if (!this.connected) return "CAD \u672A\u8FDE\u63A5";
|
|
399
414
|
return `Platform: ${this.product}, Version: ${this.version}, Status: Connected`;
|
|
400
415
|
}
|
|
401
416
|
async disconnect() {
|
|
@@ -428,9 +443,35 @@ import fs from "fs";
|
|
|
428
443
|
import path2 from "path";
|
|
429
444
|
import os from "os";
|
|
430
445
|
var DEBUG_FILE = process.env.DEBUG_FILE || path2.join(os.tmpdir(), "mcp-server-debug.log");
|
|
446
|
+
var MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
447
|
+
var MAX_LOG_FILES = 5;
|
|
431
448
|
var stream = null;
|
|
449
|
+
function rotateLog() {
|
|
450
|
+
if (!fs.existsSync(DEBUG_FILE)) return;
|
|
451
|
+
const stat = fs.statSync(DEBUG_FILE);
|
|
452
|
+
if (stat.size < MAX_LOG_SIZE) return;
|
|
453
|
+
if (stream) {
|
|
454
|
+
stream.end();
|
|
455
|
+
stream = null;
|
|
456
|
+
}
|
|
457
|
+
for (let i = MAX_LOG_FILES - 1; i > 0; i--) {
|
|
458
|
+
const oldPath = `${DEBUG_FILE}.${i}`;
|
|
459
|
+
const newPath = `${DEBUG_FILE}.${i + 1}`;
|
|
460
|
+
if (fs.existsSync(oldPath)) {
|
|
461
|
+
try {
|
|
462
|
+
fs.renameSync(oldPath, newPath);
|
|
463
|
+
} catch {
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
try {
|
|
468
|
+
fs.renameSync(DEBUG_FILE, `${DEBUG_FILE}.1`);
|
|
469
|
+
} catch {
|
|
470
|
+
}
|
|
471
|
+
}
|
|
432
472
|
function getStream() {
|
|
433
473
|
if (!stream) {
|
|
474
|
+
rotateLog();
|
|
434
475
|
stream = fs.createWriteStream(DEBUG_FILE, { flags: "a" });
|
|
435
476
|
}
|
|
436
477
|
return stream;
|
|
@@ -448,11 +489,11 @@ function closeLog() {
|
|
|
448
489
|
}
|
|
449
490
|
|
|
450
491
|
// src/handlers/cad-handlers.js
|
|
451
|
-
async function connectCad() {
|
|
452
|
-
log(
|
|
492
|
+
async function connectCad(platform = null) {
|
|
493
|
+
log(`connect_cad called${platform ? ` (target: ${platform})` : ""}, cad.connected before: ${cad.connected}`);
|
|
453
494
|
try {
|
|
454
495
|
log("calling cad.connect()...");
|
|
455
|
-
const connected = await cad.connect();
|
|
496
|
+
const connected = await cad.connect(platform);
|
|
456
497
|
log("cad.connect() returned: " + connected + ", cad.connected: " + cad.connected);
|
|
457
498
|
if (connected) {
|
|
458
499
|
return { content: [{ type: "text", text: `\u5DF2\u8FDE\u63A5\u5230 ${cad.getPlatform()} ${cad.getVersion()}` }] };
|
|
@@ -470,8 +511,7 @@ async function evalLisp(code, withResult = false, encoding = null) {
|
|
|
470
511
|
return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
|
|
471
512
|
}
|
|
472
513
|
const trimmed = (code || "").trim();
|
|
473
|
-
if (!trimmed)
|
|
474
|
-
return { content: [{ type: "text", text: "\u547D\u4EE4\u4E3A\u7A7A\uFF0C\u672A\u53D1\u9001" }] };
|
|
514
|
+
if (!trimmed) return { content: [{ type: "text", text: "\u547D\u4EE4\u4E3A\u7A7A\uFF0C\u672A\u53D1\u9001" }] };
|
|
475
515
|
try {
|
|
476
516
|
if (withResult) {
|
|
477
517
|
const result = await cad.sendCommandWithResult(trimmed, encoding);
|
|
@@ -491,17 +531,14 @@ async function getCadInfo() {
|
|
|
491
531
|
if (!cad.connected) {
|
|
492
532
|
await cad.connect();
|
|
493
533
|
}
|
|
494
|
-
if (!cad.connected)
|
|
495
|
-
return { content: [{ type: "text", text: "CAD \u672A\u8FDE\u63A5" }] };
|
|
534
|
+
if (!cad.connected) return { content: [{ type: "text", text: "CAD \u672A\u8FDE\u63A5" }] };
|
|
496
535
|
const info = await cad.getInfo();
|
|
497
536
|
return { content: [{ type: "text", text: info }] };
|
|
498
537
|
}
|
|
499
538
|
async function atCommand(command) {
|
|
500
|
-
if (!cad.connected)
|
|
501
|
-
return { content: [{ type: "text", text: "\u8BF7\u5148\u8FDE\u63A5 CAD" }], isError: true };
|
|
539
|
+
if (!cad.connected) return { content: [{ type: "text", text: "\u8BF7\u5148\u8FDE\u63A5 CAD" }], isError: true };
|
|
502
540
|
const trimmed = (command || "").trim();
|
|
503
|
-
if (!trimmed)
|
|
504
|
-
return { content: [{ type: "text", text: "\u547D\u4EE4\u4E3A\u7A7A\uFF0C\u672A\u53D1\u9001" }] };
|
|
541
|
+
if (!trimmed) return { content: [{ type: "text", text: "\u547D\u4EE4\u4E3A\u7A7A\uFF0C\u672A\u53D1\u9001" }] };
|
|
505
542
|
await cad.sendCommand(trimmed + "\n");
|
|
506
543
|
return { content: [{ type: "text", text: "\u5DF2\u53D1\u9001\u547D\u4EE4" }] };
|
|
507
544
|
}
|
|
@@ -509,8 +546,7 @@ async function newDocument() {
|
|
|
509
546
|
if (!cad.connected) {
|
|
510
547
|
await cad.connect();
|
|
511
548
|
}
|
|
512
|
-
if (!cad.connected)
|
|
513
|
-
return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
|
|
549
|
+
if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
|
|
514
550
|
const result = await cad.newDoc();
|
|
515
551
|
if (result) {
|
|
516
552
|
return { content: [{ type: "text", text: "\u5DF2\u5728 CAD \u4E2D\u65B0\u5EFA\u7A7A\u767D\u6587\u6863" }] };
|
|
@@ -521,8 +557,7 @@ async function bringToFront() {
|
|
|
521
557
|
if (!cad.connected) {
|
|
522
558
|
await cad.connect();
|
|
523
559
|
}
|
|
524
|
-
if (!cad.connected)
|
|
525
|
-
return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
|
|
560
|
+
if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
|
|
526
561
|
const result = await cad.bringToFront();
|
|
527
562
|
if (result) {
|
|
528
563
|
return { content: [{ type: "text", text: "\u5DF2\u5C06 CAD \u7A97\u53E3\u5207\u6362\u5230\u524D\u53F0" }] };
|
|
@@ -533,8 +568,7 @@ async function installAtlisp() {
|
|
|
533
568
|
if (!cad.connected) {
|
|
534
569
|
await cad.connect();
|
|
535
570
|
}
|
|
536
|
-
if (!cad.connected)
|
|
537
|
-
return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
|
|
571
|
+
if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
|
|
538
572
|
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))))`;
|
|
539
573
|
await cad.sendCommand(installCode + "\n");
|
|
540
574
|
return { content: [{ type: "text", text: "\u5DF2\u53D1\u9001 @lisp \u5B89\u88C5\u4EE3\u7801\u5230 CAD" }] };
|
|
@@ -543,12 +577,10 @@ async function listFunctionsInCad() {
|
|
|
543
577
|
if (!cad.connected) {
|
|
544
578
|
await cad.connect();
|
|
545
579
|
}
|
|
546
|
-
if (!cad.connected)
|
|
547
|
-
return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
|
|
580
|
+
if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
|
|
548
581
|
try {
|
|
549
582
|
const result = await cad.sendCommandWithResult("(atoms-family 0)", null);
|
|
550
|
-
if (result)
|
|
551
|
-
return { content: [{ type: "text", text: result }] };
|
|
583
|
+
if (result) return { content: [{ type: "text", text: result }] };
|
|
552
584
|
return { content: [{ type: "text", text: "CAD \u51FD\u6570\u5217\u8868\u4E3A\u7A7A" }] };
|
|
553
585
|
} catch (e) {
|
|
554
586
|
return { content: [{ type: "text", text: `\u9519\u8BEF: ${e.message}` }], isError: true };
|
|
@@ -603,8 +635,7 @@ async function initAtlisp() {
|
|
|
603
635
|
if (!cad.connected) {
|
|
604
636
|
await cad.connect();
|
|
605
637
|
}
|
|
606
|
-
if (!cad.connected)
|
|
607
|
-
return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
|
|
638
|
+
if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
|
|
608
639
|
const code = `(if (null @::load-module)
|
|
609
640
|
(progn
|
|
610
641
|
(vl-load-com)
|
|
@@ -641,38 +672,46 @@ async function fetchPackages() {
|
|
|
641
672
|
}
|
|
642
673
|
} catch (e) {
|
|
643
674
|
}
|
|
644
|
-
return
|
|
675
|
+
return cachedPackages || MOCK_PACKAGES;
|
|
676
|
+
}
|
|
677
|
+
function flattenPackages(data) {
|
|
678
|
+
if (Array.isArray(data)) return data;
|
|
679
|
+
if (data && Array.isArray(data.packages)) return data.packages;
|
|
680
|
+
return [];
|
|
645
681
|
}
|
|
646
682
|
async function listPackages() {
|
|
647
|
-
if (!cad.connected)
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
683
|
+
if (!cad.connected) await cad.connect();
|
|
684
|
+
if (!cad.connected) {
|
|
685
|
+
const data2 = await fetchPackages();
|
|
686
|
+
const items2 = flattenPackages(data2);
|
|
687
|
+
const text2 = items2.length ? items2.map((p) => `${p.name} v${p.version || ""} - ${p.description || ""}`).join("\n") : "\u65E0\u53EF\u7528\u5305";
|
|
688
|
+
return { content: [{ type: "text", text: text2 }] };
|
|
689
|
+
}
|
|
651
690
|
const result = await cad.sendCommandWithResult("(@::package-list-in-local)");
|
|
652
|
-
if (result && result !== "nil")
|
|
653
|
-
|
|
654
|
-
const
|
|
691
|
+
if (result && result !== "nil") return { content: [{ type: "text", text: result }] };
|
|
692
|
+
const data = await fetchPackages();
|
|
693
|
+
const items = flattenPackages(data);
|
|
694
|
+
const text = items.length ? `\u4ECE\u6CE8\u518C\u8868\u83B7\u53D6\u7684 @lisp \u5305:
|
|
695
|
+
${items.map((p) => `${p.name} v${p.version || ""} - ${p.description || ""}`).join("\n")}` : MOCK_PACKAGES.map((p) => `${p.name} v${p.version} - ${p.description}`).join("\n");
|
|
655
696
|
return { content: [{ type: "text", text: `\u5DF2\u5B89\u88C5\u7684 @lisp \u5305:
|
|
656
|
-
${
|
|
697
|
+
${text}` }] };
|
|
657
698
|
}
|
|
658
699
|
async function searchPackages(query) {
|
|
659
700
|
const data = await fetchPackages();
|
|
660
701
|
if (!data) {
|
|
661
702
|
return { content: [{ type: "text", text: "\u65E0\u6CD5\u83B7\u53D6\u5305\u5217\u8868" }], isError: true };
|
|
662
703
|
}
|
|
704
|
+
const items = flattenPackages(data);
|
|
663
705
|
const results = [];
|
|
664
|
-
const items = Array.isArray(data) ? data : data.packages || [];
|
|
665
706
|
if (query) {
|
|
666
707
|
const q = query.toLowerCase();
|
|
667
708
|
for (const p of items) {
|
|
668
709
|
const name = (p.name || "").toLowerCase();
|
|
669
710
|
const desc = (p.description || "").toLowerCase();
|
|
670
|
-
if (name.includes(q) || desc.includes(q))
|
|
671
|
-
results.push(p);
|
|
711
|
+
if (name.includes(q) || desc.includes(q)) results.push(p);
|
|
672
712
|
}
|
|
673
713
|
} else {
|
|
674
|
-
for (const p of items)
|
|
675
|
-
results.push(p);
|
|
714
|
+
for (const p of items) results.push(p);
|
|
676
715
|
}
|
|
677
716
|
if (results.length === 0) {
|
|
678
717
|
return { content: [{ type: "text", text: `\u672A\u627E\u5230\u5305\u542B "${query}" \u7684\u5305` }] };
|
|
@@ -681,7 +720,9 @@ async function searchPackages(query) {
|
|
|
681
720
|
${results.map((p) => `${p.name} v${p.version || ""} - ${p.description || ""}`).join("\n")}` }] };
|
|
682
721
|
}
|
|
683
722
|
async function installPackage(packageName) {
|
|
684
|
-
const
|
|
723
|
+
const data = await fetchPackages();
|
|
724
|
+
const items = flattenPackages(data);
|
|
725
|
+
const pkg = items.find((p) => p.name === packageName);
|
|
685
726
|
if (!pkg) {
|
|
686
727
|
return { content: [{ type: "text", text: `\u9519\u8BEF: \u5305 "${packageName}" \u4E0D\u5B58\u5728` }], isError: true };
|
|
687
728
|
}
|
|
@@ -693,41 +734,67 @@ async function installPackage(packageName) {
|
|
|
693
734
|
}
|
|
694
735
|
|
|
695
736
|
// src/handlers/function-handlers.js
|
|
696
|
-
import fs2 from "fs
|
|
737
|
+
import fs2 from "fs";
|
|
697
738
|
import path3 from "path";
|
|
739
|
+
import os2 from "os";
|
|
698
740
|
var FUNCTIONS_DIR = process.env.FUNCTIONS_DIR || "";
|
|
699
741
|
var FUNCTIONS_URL = process.env.FUNCTIONS_URL || "http://s3.atlisp.cn/json/functions.json";
|
|
700
|
-
|
|
742
|
+
var CACHE_DIR = path3.join(os2.homedir(), ".atlisp", "cache");
|
|
743
|
+
var FUNCLIB_CACHE_FILE = path3.join(CACHE_DIR, "functions.json");
|
|
744
|
+
var FUNCLIB_CACHE_TTL = 24 * 60 * 60 * 1e3;
|
|
745
|
+
function readCacheFromDisk() {
|
|
701
746
|
try {
|
|
702
|
-
|
|
703
|
-
|
|
747
|
+
const stat = fs2.statSync(FUNCLIB_CACHE_FILE);
|
|
748
|
+
if (Date.now() - stat.mtimeMs < FUNCLIB_CACHE_TTL) {
|
|
749
|
+
const raw = fs2.readFileSync(FUNCLIB_CACHE_FILE, "utf-8");
|
|
750
|
+
return JSON.parse(raw);
|
|
751
|
+
}
|
|
704
752
|
} catch {
|
|
705
|
-
return false;
|
|
706
753
|
}
|
|
754
|
+
return null;
|
|
755
|
+
}
|
|
756
|
+
function writeCacheToDisk(data) {
|
|
757
|
+
try {
|
|
758
|
+
fs2.mkdirSync(CACHE_DIR, { recursive: true });
|
|
759
|
+
fs2.writeFileSync(FUNCLIB_CACHE_FILE, JSON.stringify(data), "utf-8");
|
|
760
|
+
} catch (e) {
|
|
761
|
+
log(`function-handlers writeCache error: ${e.message}`);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
async function fetchFunctions() {
|
|
765
|
+
const diskCache = readCacheFromDisk();
|
|
766
|
+
if (diskCache) return diskCache;
|
|
767
|
+
try {
|
|
768
|
+
const response = await fetch(FUNCTIONS_URL, { headers: { "User-Agent": "atlisp-mcp" } });
|
|
769
|
+
if (response.ok) {
|
|
770
|
+
const data = await response.json();
|
|
771
|
+
writeCacheToDisk(data);
|
|
772
|
+
return data;
|
|
773
|
+
}
|
|
774
|
+
} catch (e) {
|
|
775
|
+
log(`function-handlers fetch error: ${e.message}`);
|
|
776
|
+
}
|
|
777
|
+
return diskCache;
|
|
707
778
|
}
|
|
708
779
|
async function getFunctionUsage(funcName, packageName) {
|
|
709
780
|
try {
|
|
710
781
|
let results = [];
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
const func = funcs.find((f2) => f2.name === funcName || f2.name === `${packageName}:${funcName}`);
|
|
717
|
-
if (func)
|
|
718
|
-
results.push(func);
|
|
719
|
-
}
|
|
720
|
-
} catch (e) {
|
|
721
|
-
log(`function-handlers fetch error: ${e.message}`);
|
|
782
|
+
const data = await fetchFunctions();
|
|
783
|
+
if (data) {
|
|
784
|
+
const funcs = Object.values(data.all_functions || {});
|
|
785
|
+
const func = funcs.find((f2) => f2.name === funcName || f2.name === `${packageName}:${funcName}`);
|
|
786
|
+
if (func) results.push(func);
|
|
722
787
|
}
|
|
723
|
-
if (results.length === 0 && FUNCTIONS_DIR
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
const
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
results.push(func);
|
|
788
|
+
if (results.length === 0 && FUNCTIONS_DIR) {
|
|
789
|
+
try {
|
|
790
|
+
const files = fs2.readdirSync(FUNCTIONS_DIR).filter((f2) => f2.endsWith(".json"));
|
|
791
|
+
for (const file of files) {
|
|
792
|
+
const raw = fs2.readFileSync(path3.join(FUNCTIONS_DIR, file), "utf-8");
|
|
793
|
+
const data2 = JSON.parse(raw);
|
|
794
|
+
const func = data2.functions?.find((f2) => f2.name === funcName);
|
|
795
|
+
if (func) results.push(func);
|
|
796
|
+
}
|
|
797
|
+
} catch {
|
|
731
798
|
}
|
|
732
799
|
}
|
|
733
800
|
if (results.length === 0) {
|
|
@@ -747,27 +814,22 @@ async function getFunctionUsage(funcName, packageName) {
|
|
|
747
814
|
async function listSymbols(packageName) {
|
|
748
815
|
try {
|
|
749
816
|
let allFuncs = [];
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
} else {
|
|
758
|
-
allFuncs = funcs.map((f) => `${f.name}: ${f.description || "-"}`);
|
|
759
|
-
}
|
|
817
|
+
const data = await fetchFunctions();
|
|
818
|
+
if (data) {
|
|
819
|
+
const funcs = Object.values(data.all_functions || {});
|
|
820
|
+
if (packageName) {
|
|
821
|
+
allFuncs = funcs.filter((f) => f.name.startsWith(`${packageName}:`));
|
|
822
|
+
} else {
|
|
823
|
+
allFuncs = funcs.map((f) => `${f.name}: ${f.description || "-"}`);
|
|
760
824
|
}
|
|
761
|
-
} catch (e) {
|
|
762
|
-
log(`function-handlers list fetch error: ${e.message}`);
|
|
763
825
|
}
|
|
764
|
-
if (allFuncs.length === 0 && FUNCTIONS_DIR
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
const raw =
|
|
769
|
-
const
|
|
770
|
-
const funcs = Object.values(
|
|
826
|
+
if (allFuncs.length === 0 && FUNCTIONS_DIR) {
|
|
827
|
+
try {
|
|
828
|
+
const files = fs2.readdirSync(FUNCTIONS_DIR).filter((f) => f.endsWith(".json"));
|
|
829
|
+
for (const file of files) {
|
|
830
|
+
const raw = fs2.readFileSync(path3.join(FUNCTIONS_DIR, file), "utf-8");
|
|
831
|
+
const data2 = JSON.parse(raw);
|
|
832
|
+
const funcs = Object.values(data2.all_functions || {});
|
|
771
833
|
if (packageName) {
|
|
772
834
|
allFuncs = funcs.filter((f) => f.name.startsWith(`${packageName}:`));
|
|
773
835
|
} else {
|
|
@@ -775,9 +837,9 @@ async function listSymbols(packageName) {
|
|
|
775
837
|
allFuncs.push(`${f.name}: ${f.description || "-"}`);
|
|
776
838
|
}
|
|
777
839
|
}
|
|
778
|
-
} catch (err) {
|
|
779
|
-
log(`read file ${file} error: ${err.message}`);
|
|
780
840
|
}
|
|
841
|
+
} catch (err) {
|
|
842
|
+
log(`listSymbols readDir error: ${err.message}`);
|
|
781
843
|
}
|
|
782
844
|
}
|
|
783
845
|
if (allFuncs.length === 0) {
|
|
@@ -794,6 +856,11 @@ ${allFuncs.map((f) => f.name).join("\n")}` }] };
|
|
|
794
856
|
}
|
|
795
857
|
|
|
796
858
|
// src/handlers/resource-handlers.js
|
|
859
|
+
import fs3 from "fs";
|
|
860
|
+
import path4 from "path";
|
|
861
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
862
|
+
var __dirname2 = path4.dirname(fileURLToPath2(import.meta.url));
|
|
863
|
+
var STANDARDS_DIR = path4.join(__dirname2, "..", "standards");
|
|
797
864
|
var CACHE_TTL2 = config_default.resourceCacheTtl;
|
|
798
865
|
var resourceCache = /* @__PURE__ */ new Map();
|
|
799
866
|
function getCached(key) {
|
|
@@ -819,14 +886,11 @@ function clearCache(uri) {
|
|
|
819
886
|
}
|
|
820
887
|
}
|
|
821
888
|
function parseLispList(str) {
|
|
822
|
-
if (!str || typeof str !== "string")
|
|
823
|
-
return null;
|
|
889
|
+
if (!str || typeof str !== "string") return null;
|
|
824
890
|
const trimmed = str.trim();
|
|
825
|
-
if (!trimmed.startsWith("(") || !trimmed.endsWith(")"))
|
|
826
|
-
return null;
|
|
891
|
+
if (!trimmed.startsWith("(") || !trimmed.endsWith(")")) return null;
|
|
827
892
|
const content = trimmed.slice(1, -1).trim();
|
|
828
|
-
if (!content)
|
|
829
|
-
return [];
|
|
893
|
+
if (!content) return [];
|
|
830
894
|
const result = [];
|
|
831
895
|
let depth = 0;
|
|
832
896
|
let current = "";
|
|
@@ -868,45 +932,262 @@ function parseLispList(str) {
|
|
|
868
932
|
}
|
|
869
933
|
current += ch;
|
|
870
934
|
}
|
|
871
|
-
if (current.trim())
|
|
872
|
-
result.push(current.trim());
|
|
935
|
+
if (current.trim()) result.push(current.trim());
|
|
873
936
|
return result.map((item) => {
|
|
874
937
|
const t = item.trim();
|
|
875
|
-
if (t === "nil")
|
|
876
|
-
|
|
877
|
-
if (t === "
|
|
878
|
-
return true;
|
|
879
|
-
if (t === "T")
|
|
880
|
-
return true;
|
|
938
|
+
if (t === "nil") return null;
|
|
939
|
+
if (t === "t") return true;
|
|
940
|
+
if (t === "T") return true;
|
|
881
941
|
const num = Number(t);
|
|
882
|
-
if (!isNaN(num) && t !== "")
|
|
883
|
-
|
|
884
|
-
if (t.startsWith('"') && t.endsWith('"'))
|
|
885
|
-
return t.slice(1, -1);
|
|
942
|
+
if (!isNaN(num) && t !== "") return num;
|
|
943
|
+
if (t.startsWith('"') && t.endsWith('"')) return t.slice(1, -1);
|
|
886
944
|
return t;
|
|
887
945
|
});
|
|
888
946
|
}
|
|
947
|
+
function parseLispRecursive(str) {
|
|
948
|
+
if (!str || typeof str !== "string") return null;
|
|
949
|
+
str = str.trim();
|
|
950
|
+
if (!str) return null;
|
|
951
|
+
let pos = 0;
|
|
952
|
+
function skipWs() {
|
|
953
|
+
while (pos < str.length && /\s/.test(str[pos])) pos++;
|
|
954
|
+
}
|
|
955
|
+
function parseValue() {
|
|
956
|
+
skipWs();
|
|
957
|
+
if (pos >= str.length) return null;
|
|
958
|
+
const ch = str[pos];
|
|
959
|
+
if (ch === "(") return parseList();
|
|
960
|
+
if (ch === '"') return parseString();
|
|
961
|
+
if (ch === "'") {
|
|
962
|
+
pos++;
|
|
963
|
+
return parseValue();
|
|
964
|
+
}
|
|
965
|
+
return parseAtom();
|
|
966
|
+
}
|
|
967
|
+
function parseString() {
|
|
968
|
+
pos++;
|
|
969
|
+
let r = "";
|
|
970
|
+
while (pos < str.length && str[pos] !== '"') {
|
|
971
|
+
if (str[pos] === "\\" && pos + 1 < str.length) {
|
|
972
|
+
pos++;
|
|
973
|
+
if (str[pos] === '"') r += '"';
|
|
974
|
+
else if (str[pos] === "n") r += "\n";
|
|
975
|
+
else if (str[pos] === "t") r += " ";
|
|
976
|
+
else if (str[pos] === "\\") r += "\\";
|
|
977
|
+
else r += str[pos];
|
|
978
|
+
} else {
|
|
979
|
+
r += str[pos];
|
|
980
|
+
}
|
|
981
|
+
pos++;
|
|
982
|
+
}
|
|
983
|
+
pos++;
|
|
984
|
+
return r;
|
|
985
|
+
}
|
|
986
|
+
function parseAtom() {
|
|
987
|
+
const start = pos;
|
|
988
|
+
while (pos < str.length && !/\s/.test(str[pos]) && str[pos] !== ")" && str[pos] !== "(") pos++;
|
|
989
|
+
const atom = str.slice(start, pos);
|
|
990
|
+
if (atom === "nil" || atom === "NIL") return null;
|
|
991
|
+
if (atom === "t" || atom === "T") return true;
|
|
992
|
+
const num = Number(atom);
|
|
993
|
+
if (!isNaN(num) && atom !== "") return num;
|
|
994
|
+
return atom;
|
|
995
|
+
}
|
|
996
|
+
function parseList() {
|
|
997
|
+
pos++;
|
|
998
|
+
const items = [];
|
|
999
|
+
while (pos < str.length) {
|
|
1000
|
+
skipWs();
|
|
1001
|
+
if (pos >= str.length) break;
|
|
1002
|
+
if (str[pos] === ")") {
|
|
1003
|
+
pos++;
|
|
1004
|
+
return items;
|
|
1005
|
+
}
|
|
1006
|
+
const left = parseValue();
|
|
1007
|
+
if (left === void 0) break;
|
|
1008
|
+
skipWs();
|
|
1009
|
+
if (str[pos] === "." && (pos + 1 >= str.length || /\s/.test(str[pos + 1]) || str[pos + 1] === ")")) {
|
|
1010
|
+
pos++;
|
|
1011
|
+
skipWs();
|
|
1012
|
+
if (pos < str.length && str[pos] !== ")") {
|
|
1013
|
+
const right = parseValue();
|
|
1014
|
+
items.push([left, right]);
|
|
1015
|
+
skipWs();
|
|
1016
|
+
} else {
|
|
1017
|
+
items.push(left);
|
|
1018
|
+
}
|
|
1019
|
+
} else {
|
|
1020
|
+
items.push(left);
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
return items;
|
|
1024
|
+
}
|
|
1025
|
+
return parseValue();
|
|
1026
|
+
}
|
|
1027
|
+
function lispPairsToObject(pairs) {
|
|
1028
|
+
if (!Array.isArray(pairs)) return pairs;
|
|
1029
|
+
const obj = {};
|
|
1030
|
+
for (let i = 0; i < pairs.length - 1; i += 2) {
|
|
1031
|
+
const key = pairs[i];
|
|
1032
|
+
const val = pairs[i + 1];
|
|
1033
|
+
if (typeof key === "string" || typeof key === "number") {
|
|
1034
|
+
obj[String(key)] = Array.isArray(val) ? lispPairsToObject(val) : val;
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
return obj;
|
|
1038
|
+
}
|
|
1039
|
+
var ENTITY_SCHEMAS = {
|
|
1040
|
+
LINE: { fields: ["x1", "y1", "z1", "x2", "y2", "z2", "length"], strings: [] },
|
|
1041
|
+
CIRCLE: { fields: ["cx", "cy", "cz", "radius"], strings: [] },
|
|
1042
|
+
ARC: { fields: ["cx", "cy", "cz", "radius", "startAngle", "endAngle"], strings: [] },
|
|
1043
|
+
LWPOLYLINE: { fields: ["vertices", "closed"], strings: [] },
|
|
1044
|
+
TEXT: { fields: ["x", "y", "z", "text", "height", "rotation"], strings: ["text"] },
|
|
1045
|
+
MTEXT: { fields: ["x", "y", "z", "text", "height", "rotation"], strings: ["text"] },
|
|
1046
|
+
ATTRIB: { fields: ["x", "y", "z", "text", "height", "rotation"], strings: ["text"] },
|
|
1047
|
+
TCH_TEXT: { fields: ["x", "y", "z", "text", "height", "rotation"], strings: ["text"] },
|
|
1048
|
+
INSERT: { fields: ["x", "y", "z", "blockName", "rotation", "scaleX", "scaleY"], strings: ["blockName"] },
|
|
1049
|
+
POINT: { fields: ["x", "y", "z"], strings: [] }
|
|
1050
|
+
};
|
|
1051
|
+
function parseEntity(arr) {
|
|
1052
|
+
if (!Array.isArray(arr) || arr.length < 5) return null;
|
|
1053
|
+
const [type, handle, layer, color, linetype, ...rest] = arr;
|
|
1054
|
+
const schema = ENTITY_SCHEMAS[String(type)] || { fields: [], strings: [] };
|
|
1055
|
+
const props = {};
|
|
1056
|
+
schema.fields.forEach((name, i) => {
|
|
1057
|
+
if (i < rest.length) {
|
|
1058
|
+
let val = rest[i];
|
|
1059
|
+
if (schema.strings.includes(name) && val !== null && val !== void 0) val = String(val);
|
|
1060
|
+
props[name] = val;
|
|
1061
|
+
}
|
|
1062
|
+
});
|
|
1063
|
+
return { type: String(type), handle: String(handle), layer: String(layer), color, linetype: String(linetype), ...props };
|
|
1064
|
+
}
|
|
1065
|
+
function buildEntityCode({ type, layer, bbox, offset, limit }) {
|
|
1066
|
+
const items = [];
|
|
1067
|
+
if (type) items.push(`(cons 0 "${type.replace(/"/g, '\\"')}")`);
|
|
1068
|
+
if (layer) items.push(`(cons 8 "${layer.replace(/"/g, '\\"')}")`);
|
|
1069
|
+
if (bbox) {
|
|
1070
|
+
const parts = bbox.split(",").map((s) => parseFloat(s.trim()));
|
|
1071
|
+
if (parts.length === 4) {
|
|
1072
|
+
const [x1, y1, x2, y2] = parts;
|
|
1073
|
+
items.push(
|
|
1074
|
+
`'(-4 . "<AND")`,
|
|
1075
|
+
`'(-4 . ">=,>=,*") (list 10 ${x1} ${y1} 0.0)`,
|
|
1076
|
+
`'(-4 . "<=,<=,*") (list 10 ${x2} ${y2} 0.0)`,
|
|
1077
|
+
`'(-4 . "AND>")`
|
|
1078
|
+
);
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
const filterExpr = items.length ? `(list ${items.join(" ")})` : "nil";
|
|
1082
|
+
const ssgetExpr = items.length ? `(ssget "_X" ${filterExpr})` : '(ssget "_X")';
|
|
1083
|
+
return `(progn
|
|
1084
|
+
(defun @e:props (ent / ed typ result item s e c verts closed ins)
|
|
1085
|
+
(setq ed (entget ent) typ (cdr (assoc 0 ed)))
|
|
1086
|
+
(setq result (list (vl-prin1-to-string (cdr (assoc 5 ed))) (vl-prin1-to-string (cdr (assoc 8 ed)))
|
|
1087
|
+
(if (setq item (assoc 62 ed)) (cdr item) "ByLayer")
|
|
1088
|
+
(vl-prin1-to-string (if (setq item (assoc 6 ed)) (cdr item) "ByLayer"))))
|
|
1089
|
+
(cond
|
|
1090
|
+
((= typ "LINE")
|
|
1091
|
+
(setq s (cdr (assoc 10 ed)) e (cdr (assoc 11 ed)))
|
|
1092
|
+
(append result (list (car s) (cadr s) (caddr s) (car e) (cadr e) (caddr e) (distance s e))))
|
|
1093
|
+
((= typ "CIRCLE")
|
|
1094
|
+
(setq c (cdr (assoc 10 ed)))
|
|
1095
|
+
(append result (list (car c) (cadr c) (caddr c) (cdr (assoc 40 ed)))))
|
|
1096
|
+
((= typ "ARC")
|
|
1097
|
+
(setq c (cdr (assoc 10 ed)))
|
|
1098
|
+
(append result (list (car c) (cadr c) (caddr c) (cdr (assoc 40 ed)) (cdr (assoc 50 ed)) (cdr (assoc 51 ed)))))
|
|
1099
|
+
((= typ "LWPOLYLINE")
|
|
1100
|
+
(setq verts nil closed nil)
|
|
1101
|
+
(foreach g ed
|
|
1102
|
+
(if (= (car g) 10) (setq verts (append verts (list (list (cadr g) (caddr g))))))
|
|
1103
|
+
(if (= (car g) 70) (setq closed (= (logand (cdr g) 1) 1))))
|
|
1104
|
+
(append result (list verts (if closed "T" nil))))
|
|
1105
|
+
((= typ "TEXT")
|
|
1106
|
+
(setq ins (cdr (assoc 10 ed)))
|
|
1107
|
+
(append result (list (car ins) (cadr ins) (caddr ins) (vl-prin1-to-string (cdr (assoc 1 ed))) (cdr (assoc 40 ed)) (cdr (assoc 50 ed))))))
|
|
1108
|
+
((= typ "MTEXT")
|
|
1109
|
+
(setq ins (cdr (assoc 10 ed)))
|
|
1110
|
+
(append result (list (car ins) (cadr ins) (caddr ins) (vl-prin1-to-string (cdr (assoc 1 ed))) (cdr (assoc 40 ed)) (cdr (assoc 50 ed))))))
|
|
1111
|
+
((= typ "INSERT")
|
|
1112
|
+
(setq ins (cdr (assoc 10 ed)))
|
|
1113
|
+
(append result (list (car ins) (cadr ins) (caddr ins) (vl-prin1-to-string (cdr (assoc 2 ed))) (cdr (assoc 50 ed)) (cdr (assoc 41 ed)) (cdr (assoc 42 ed))))))
|
|
1114
|
+
((= typ "POINT")
|
|
1115
|
+
(setq p (cdr (assoc 10 ed)))
|
|
1116
|
+
(append result (list (car p) (cadr p) (caddr p))))
|
|
1117
|
+
(t result)))
|
|
1118
|
+
(defun @e:run nil
|
|
1119
|
+
(setq ss ${ssgetExpr})
|
|
1120
|
+
(if (null ss)
|
|
1121
|
+
(list "total" 0 "offset" ${offset} "limit" ${limit})
|
|
1122
|
+
(progn
|
|
1123
|
+
(setq total (sslength ss) entities nil i ${offset})
|
|
1124
|
+
(while (and (< i total) (< (length entities) ${limit}))
|
|
1125
|
+
(setq entities (append entities (list (@e:props (ssname ss i)))) i (1+ i)))
|
|
1126
|
+
(list "total" total "offset" ${offset} "limit" ${limit} "entities" entities))))
|
|
1127
|
+
(@e:run))
|
|
1128
|
+
`;
|
|
1129
|
+
}
|
|
1130
|
+
function buildTextCode({ layer, bbox, offset, limit }) {
|
|
1131
|
+
const items = [`'(0 . "TEXT,MTEXT,ATTRIB,TCH_TEXT")`];
|
|
1132
|
+
if (layer) items.push(`(cons 8 "${layer.replace(/"/g, '\\"')}")`);
|
|
1133
|
+
if (bbox) {
|
|
1134
|
+
const parts = bbox.split(",").map((s) => parseFloat(s.trim()));
|
|
1135
|
+
if (parts.length === 4) {
|
|
1136
|
+
const [x1, y1, x2, y2] = parts;
|
|
1137
|
+
items.push(
|
|
1138
|
+
`'(-4 . "<AND")`,
|
|
1139
|
+
`'(-4 . ">=,>=,*") (list 10 ${x1} ${y1} 0.0)`,
|
|
1140
|
+
`'(-4 . "<=,<=,*") (list 10 ${x2} ${y2} 0.0)`,
|
|
1141
|
+
`'(-4 . "AND>")`
|
|
1142
|
+
);
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
const filterExpr = `(list ${items.join(" ")})`;
|
|
1146
|
+
return `(progn
|
|
1147
|
+
(defun @t:props (ent / ed typ result item ins)
|
|
1148
|
+
(setq ed (entget ent) typ (cdr (assoc 0 ed)))
|
|
1149
|
+
(setq result (list (vl-prin1-to-string typ) (vl-prin1-to-string (cdr (assoc 5 ed))) (vl-prin1-to-string (cdr (assoc 8 ed)))
|
|
1150
|
+
(if (setq item (assoc 62 ed)) (cdr item) "ByLayer")
|
|
1151
|
+
(vl-prin1-to-string (if (setq item (assoc 6 ed)) (cdr item) "ByLayer"))))
|
|
1152
|
+
(setq ins (cdr (assoc 10 ed)))
|
|
1153
|
+
(append result (list (car ins) (cadr ins) (caddr ins)
|
|
1154
|
+
(vl-prin1-to-string (cdr (assoc 1 ed))) (cdr (assoc 40 ed)) (cdr (assoc 50 ed))
|
|
1155
|
+
(vl-prin1-to-string (if (setq item (assoc 7 ed)) (cdr item) "Standard")))))
|
|
1156
|
+
(defun @t:run nil
|
|
1157
|
+
(setq ss (ssget "_X" ${filterExpr}))
|
|
1158
|
+
(if (null ss)
|
|
1159
|
+
(list "total" 0 "offset" ${offset} "limit" ${limit})
|
|
1160
|
+
(progn
|
|
1161
|
+
(setq total (sslength ss) texts nil i ${offset})
|
|
1162
|
+
(while (and (< i total) (< (length texts) ${limit}))
|
|
1163
|
+
(setq texts (append texts (list (@t:props (ssname ss i)))) i (1+ i)))
|
|
1164
|
+
(list "total" total "offset" ${offset} "limit" ${limit} "texts" texts))))
|
|
1165
|
+
(@t:run))
|
|
1166
|
+
`;
|
|
1167
|
+
}
|
|
889
1168
|
var RESOURCES = [
|
|
890
1169
|
{ uri: "atlisp://cad/info", name: "CAD Info", description: "CAD \u8FDE\u63A5\u4FE1\u606F", mimeType: "application/json", subscribable: true },
|
|
891
|
-
{ uri: "atlisp://
|
|
892
|
-
{ uri: "atlisp://
|
|
1170
|
+
{ uri: "atlisp://dwg/layers", name: "Layers", description: "\u56FE\u5C42\u5217\u8868", mimeType: "application/json", subscribable: true },
|
|
1171
|
+
{ uri: "atlisp://dwg/entities", name: "Entities", description: "\u5B9E\u4F53\u5C5E\u6027\u5217\u8868\uFF0C\u652F\u6301 ?type=LINE&layer=0&bbox=0,0,100,100&limit=100&offset=0", mimeType: "application/json", subscribable: true },
|
|
1172
|
+
{ 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=500&offset=0", mimeType: "application/json", subscribable: true },
|
|
893
1173
|
{ uri: "atlisp://dwg/name", name: "DWG Name", description: "\u5F53\u524D DWG \u6587\u4EF6\u540D", mimeType: "application/json", subscribable: true },
|
|
894
1174
|
{ uri: "atlisp://dwg/path", name: "DWG Path", description: "\u6587\u4EF6\u5B8C\u6574\u8DEF\u5F84", mimeType: "application/json", subscribable: true },
|
|
1175
|
+
{ uri: "atlisp://cad/dwgs", name: "DWG List", description: "\u6240\u6709\u6253\u5F00\u7684 DWG \u6587\u4EF6\u5217\u8868", mimeType: "application/json", subscribable: true },
|
|
895
1176
|
{ uri: "atlisp://packages", name: "Packages", description: "\u5DF2\u5B89\u88C5\u5305\u5217\u8868", mimeType: "application/json", subscribable: true },
|
|
896
|
-
{ uri: "atlisp://platforms", name: "Platforms", description: "\u652F\u6301\u7684\u5E73\u53F0\u4FE1\u606F", mimeType: "application/json", subscribable: false }
|
|
1177
|
+
{ uri: "atlisp://platforms", name: "Platforms", description: "\u652F\u6301\u7684\u5E73\u53F0\u4FE1\u606F", mimeType: "application/json", subscribable: false },
|
|
1178
|
+
{ uri: "atlisp://standards/drafting", name: "Drafting Standards", description: "CAD \u5236\u56FE\u89C4\u8303", mimeType: "application/json", subscribable: false },
|
|
1179
|
+
{ uri: "atlisp://standards/coding", name: "Coding Standards", description: "@lisp \u7F16\u7801\u89C4\u8303", mimeType: "application/json", subscribable: false }
|
|
897
1180
|
];
|
|
898
1181
|
function parseQuery(uri) {
|
|
899
1182
|
const qIdx = uri.indexOf("?");
|
|
900
|
-
if (qIdx === -1)
|
|
901
|
-
return { base: uri, params: {} };
|
|
1183
|
+
if (qIdx === -1) return { base: uri, params: {} };
|
|
902
1184
|
const base = uri.substring(0, qIdx);
|
|
903
1185
|
const params = {};
|
|
904
1186
|
uri.substring(qIdx + 1).split("&").forEach((p) => {
|
|
905
1187
|
const eqIdx = p.indexOf("=");
|
|
906
1188
|
const k = eqIdx === -1 ? p : p.substring(0, eqIdx);
|
|
907
1189
|
const v = eqIdx === -1 ? "" : p.substring(eqIdx + 1);
|
|
908
|
-
if (k)
|
|
909
|
-
params[k] = decodeURIComponent(v);
|
|
1190
|
+
if (k) params[k] = decodeURIComponent(v);
|
|
910
1191
|
});
|
|
911
1192
|
return { base, params };
|
|
912
1193
|
}
|
|
@@ -924,26 +1205,33 @@ async function readResource(uri) {
|
|
|
924
1205
|
switch (base) {
|
|
925
1206
|
case "atlisp://cad/info":
|
|
926
1207
|
return await getCadInfo2();
|
|
927
|
-
case "atlisp://
|
|
1208
|
+
case "atlisp://dwg/layers":
|
|
928
1209
|
return await getLayers(params.name);
|
|
929
|
-
case "atlisp://
|
|
930
|
-
return await getEntities(params
|
|
1210
|
+
case "atlisp://dwg/entities":
|
|
1211
|
+
return await getEntities(params);
|
|
1212
|
+
case "atlisp://dwg/texts":
|
|
1213
|
+
return await getTexts(params);
|
|
931
1214
|
case "atlisp://dwg/name":
|
|
932
1215
|
return await getDwgName();
|
|
933
1216
|
case "atlisp://dwg/path":
|
|
934
1217
|
return await getDwgPath();
|
|
1218
|
+
case "atlisp://cad/dwgs":
|
|
1219
|
+
return await getDwgList();
|
|
935
1220
|
case "atlisp://packages":
|
|
936
1221
|
return await getPackages(params.name);
|
|
937
1222
|
case "atlisp://platforms":
|
|
938
1223
|
return getPlatforms();
|
|
1224
|
+
case "atlisp://standards/drafting":
|
|
1225
|
+
return getDraftingStandards();
|
|
1226
|
+
case "atlisp://standards/coding":
|
|
1227
|
+
return getCodingStandards();
|
|
939
1228
|
default:
|
|
940
1229
|
throw new Error(`\u8D44\u6E90\u4E0D\u5B58\u5728: ${uri}`);
|
|
941
1230
|
}
|
|
942
1231
|
}
|
|
943
1232
|
async function getCadInfo2() {
|
|
944
1233
|
const cached = getCached("cad:info");
|
|
945
|
-
if (cached)
|
|
946
|
-
return cached;
|
|
1234
|
+
if (cached) return cached;
|
|
947
1235
|
if (!cad.connected) {
|
|
948
1236
|
await cad.connect();
|
|
949
1237
|
}
|
|
@@ -968,14 +1256,11 @@ async function getCadInfo2() {
|
|
|
968
1256
|
return result;
|
|
969
1257
|
}
|
|
970
1258
|
async function getLayers(filterName = null) {
|
|
971
|
-
const cacheKey = filterName ? `layers:${filterName}` : "layers:all";
|
|
1259
|
+
const cacheKey = filterName ? `dwg:layers:${filterName}` : "dwg:layers:all";
|
|
972
1260
|
const cached = getCached(cacheKey);
|
|
973
|
-
if (cached)
|
|
974
|
-
|
|
975
|
-
if (!cad.connected)
|
|
976
|
-
await cad.connect();
|
|
977
|
-
if (!cad.connected)
|
|
978
|
-
return [];
|
|
1261
|
+
if (cached) return cached;
|
|
1262
|
+
if (!cad.connected) await cad.connect();
|
|
1263
|
+
if (!cad.connected) return [];
|
|
979
1264
|
try {
|
|
980
1265
|
const code = filterName ? `(tblnext "LAYER" T)` : `(progn (setq res nil)(while (setq e (tblnext "LAYER" (null res)))(setq res (cons (list (cdr (assoc 2 e)) (cdr (assoc 62 e)) (cdr (assoc 70 e))) res))) (reverse res))`;
|
|
981
1266
|
const result = await cad.sendCommandWithResult(code);
|
|
@@ -1019,70 +1304,97 @@ async function getLayers(filterName = null) {
|
|
|
1019
1304
|
return [];
|
|
1020
1305
|
}
|
|
1021
1306
|
}
|
|
1022
|
-
async function getEntities(
|
|
1023
|
-
const cacheKey =
|
|
1307
|
+
async function getEntities(params = {}) {
|
|
1308
|
+
const cacheKey = `dwg:entities:${JSON.stringify(params)}`;
|
|
1024
1309
|
const cached = getCached(cacheKey);
|
|
1025
|
-
if (cached)
|
|
1026
|
-
|
|
1027
|
-
if (!cad.connected)
|
|
1028
|
-
await cad.connect();
|
|
1310
|
+
if (cached) return cached;
|
|
1311
|
+
if (!cad.connected) await cad.connect();
|
|
1029
1312
|
if (!cad.connected) {
|
|
1030
|
-
const result = { total: 0,
|
|
1313
|
+
const result = { total: 0, entities: [] };
|
|
1031
1314
|
setCache(cacheKey, result);
|
|
1032
1315
|
return result;
|
|
1033
1316
|
}
|
|
1317
|
+
const offset = Math.max(0, parseInt(params.offset) || 0);
|
|
1318
|
+
const limit = Math.min(Math.max(1, parseInt(params.limit) || 100), 1e3);
|
|
1034
1319
|
try {
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1320
|
+
const code = buildEntityCode({
|
|
1321
|
+
type: params.type || null,
|
|
1322
|
+
layer: params.layer || null,
|
|
1323
|
+
bbox: params.bbox || null,
|
|
1324
|
+
offset,
|
|
1325
|
+
limit
|
|
1326
|
+
});
|
|
1327
|
+
const resultStr = await cad.sendCommandWithResult(code);
|
|
1328
|
+
if (!resultStr || resultStr === "" || resultStr === "nil" || resultStr === "0") {
|
|
1329
|
+
const result = { total: 0, offset, limit, entities: [] };
|
|
1040
1330
|
setCache(cacheKey, result);
|
|
1041
1331
|
return result;
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1332
|
+
}
|
|
1333
|
+
const parsed = parseLispRecursive(resultStr);
|
|
1334
|
+
if (!parsed || !Array.isArray(parsed)) {
|
|
1335
|
+
const result = { total: 0, offset, limit, entities: [] };
|
|
1336
|
+
setCache(cacheKey, result);
|
|
1337
|
+
return result;
|
|
1338
|
+
}
|
|
1339
|
+
const entitiesIdx = parsed.indexOf("entities");
|
|
1340
|
+
const rawEntities = entitiesIdx !== -1 && Array.isArray(parsed[entitiesIdx + 1]) ? parsed[entitiesIdx + 1] : [];
|
|
1341
|
+
const data = lispPairsToObject(parsed);
|
|
1342
|
+
data.entities = rawEntities.length > 0 ? rawEntities.map(parseEntity).filter(Boolean) : [];
|
|
1343
|
+
data.offset = offset;
|
|
1344
|
+
data.limit = limit;
|
|
1345
|
+
setCache(cacheKey, data);
|
|
1346
|
+
return data;
|
|
1347
|
+
} catch (e) {
|
|
1348
|
+
log(`getEntities error: ${e.message}`);
|
|
1349
|
+
const result = { total: 0, offset, limit, entities: [] };
|
|
1350
|
+
setCache(cacheKey, result);
|
|
1351
|
+
return result;
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
async function getTexts(params = {}) {
|
|
1355
|
+
const cacheKey = `dwg:texts:${JSON.stringify(params)}`;
|
|
1356
|
+
const cached = getCached(cacheKey);
|
|
1357
|
+
if (cached) return cached;
|
|
1358
|
+
if (!cad.connected) await cad.connect();
|
|
1359
|
+
if (!cad.connected) {
|
|
1360
|
+
const result = { total: 0, texts: [] };
|
|
1361
|
+
setCache(cacheKey, result);
|
|
1362
|
+
return result;
|
|
1363
|
+
}
|
|
1364
|
+
const offset = Math.max(0, parseInt(params.offset) || 0);
|
|
1365
|
+
const limit = Math.min(Math.max(1, parseInt(params.limit) || 500), 5e3);
|
|
1366
|
+
try {
|
|
1367
|
+
const code = buildTextCode({
|
|
1368
|
+
layer: params.layer || null,
|
|
1369
|
+
bbox: params.bbox || null,
|
|
1370
|
+
offset,
|
|
1371
|
+
limit
|
|
1372
|
+
});
|
|
1373
|
+
const resultStr = await cad.sendCommandWithResult(code);
|
|
1374
|
+
if (!resultStr || resultStr === "" || resultStr === "nil" || resultStr === "0") {
|
|
1375
|
+
const result = { total: 0, offset, limit, texts: [] };
|
|
1072
1376
|
setCache(cacheKey, result);
|
|
1073
1377
|
return result;
|
|
1074
1378
|
}
|
|
1379
|
+
const parsed = parseLispRecursive(resultStr);
|
|
1380
|
+
const textsIdx = parsed.indexOf("texts");
|
|
1381
|
+
const rawTexts = textsIdx !== -1 && Array.isArray(parsed[textsIdx + 1]) ? parsed[textsIdx + 1] : [];
|
|
1382
|
+
const data = lispPairsToObject(parsed);
|
|
1383
|
+
data.texts = rawTexts.length > 0 ? rawTexts.map(parseEntity).filter(Boolean) : [];
|
|
1384
|
+
data.offset = offset;
|
|
1385
|
+
data.limit = limit;
|
|
1386
|
+
setCache(cacheKey, data);
|
|
1387
|
+
return data;
|
|
1075
1388
|
} catch (e) {
|
|
1076
|
-
|
|
1389
|
+
log(`getTexts error: ${e.message}`);
|
|
1390
|
+
const result = { total: 0, offset, limit, texts: [] };
|
|
1077
1391
|
setCache(cacheKey, result);
|
|
1078
1392
|
return result;
|
|
1079
1393
|
}
|
|
1080
1394
|
}
|
|
1081
1395
|
async function getDwgName() {
|
|
1082
|
-
if (!cad.connected)
|
|
1083
|
-
|
|
1084
|
-
if (!cad.connected)
|
|
1085
|
-
return { name: null };
|
|
1396
|
+
if (!cad.connected) await cad.connect();
|
|
1397
|
+
if (!cad.connected) return { name: null };
|
|
1086
1398
|
try {
|
|
1087
1399
|
const code = `(vl-princ-to-string (getvar "dwgname"))`;
|
|
1088
1400
|
const name = await cad.sendCommandWithResult(code);
|
|
@@ -1092,10 +1404,8 @@ async function getDwgName() {
|
|
|
1092
1404
|
}
|
|
1093
1405
|
}
|
|
1094
1406
|
async function getDwgPath() {
|
|
1095
|
-
if (!cad.connected)
|
|
1096
|
-
|
|
1097
|
-
if (!cad.connected)
|
|
1098
|
-
return { path: null, name: null };
|
|
1407
|
+
if (!cad.connected) await cad.connect();
|
|
1408
|
+
if (!cad.connected) return { path: null, name: null };
|
|
1099
1409
|
try {
|
|
1100
1410
|
const code = `(progn
|
|
1101
1411
|
(setq prefix (vl-princ-to-string (getvar "dwgprefix")))
|
|
@@ -1113,8 +1423,7 @@ async function getDwgPath() {
|
|
|
1113
1423
|
}
|
|
1114
1424
|
if (!parsed) {
|
|
1115
1425
|
const listResult = parseLispList(result);
|
|
1116
|
-
if (listResult && listResult.length >= 2)
|
|
1117
|
-
parsed = listResult;
|
|
1426
|
+
if (listResult && listResult.length >= 2) parsed = listResult;
|
|
1118
1427
|
}
|
|
1119
1428
|
if (parsed && Array.isArray(parsed) && parsed.length >= 2) {
|
|
1120
1429
|
return { path: String(parsed[0]), name: String(parsed[1]) };
|
|
@@ -1127,12 +1436,9 @@ async function getDwgPath() {
|
|
|
1127
1436
|
async function getPackages(filterName = null) {
|
|
1128
1437
|
const cacheKey = filterName ? `packages:${filterName}` : "packages:all";
|
|
1129
1438
|
const cached = getCached(cacheKey);
|
|
1130
|
-
if (cached)
|
|
1131
|
-
|
|
1132
|
-
if (!cad.connected)
|
|
1133
|
-
await cad.connect();
|
|
1134
|
-
if (!cad.connected)
|
|
1135
|
-
return [];
|
|
1439
|
+
if (cached) return cached;
|
|
1440
|
+
if (!cad.connected) await cad.connect();
|
|
1441
|
+
if (!cad.connected) return [];
|
|
1136
1442
|
try {
|
|
1137
1443
|
const code = `(mapcar 'cdr @::*local-pkgs*)`;
|
|
1138
1444
|
const result = await cad.sendCommandWithResult(code);
|
|
@@ -1146,8 +1452,7 @@ async function getPackages(filterName = null) {
|
|
|
1146
1452
|
} catch {
|
|
1147
1453
|
raw = parseLispList(result) || [];
|
|
1148
1454
|
}
|
|
1149
|
-
if (!Array.isArray(raw))
|
|
1150
|
-
raw = [];
|
|
1455
|
+
if (!Array.isArray(raw)) raw = [];
|
|
1151
1456
|
const packages = raw.map((item) => {
|
|
1152
1457
|
if (Array.isArray(item)) {
|
|
1153
1458
|
const pkg = { name: "", version: "0.0.0", description: "", loaded: true };
|
|
@@ -1156,20 +1461,13 @@ async function getPackages(filterName = null) {
|
|
|
1156
1461
|
const [[key, val]] = Object.entries(entry);
|
|
1157
1462
|
const k = String(key);
|
|
1158
1463
|
const v = String(val);
|
|
1159
|
-
if (k === ":NAME")
|
|
1160
|
-
|
|
1161
|
-
else if (k === ":
|
|
1162
|
-
|
|
1163
|
-
else if (k === ":
|
|
1164
|
-
|
|
1165
|
-
else if (k === ":
|
|
1166
|
-
pkg.fullName = v;
|
|
1167
|
-
else if (k === ":AUTHOR")
|
|
1168
|
-
pkg.author = v;
|
|
1169
|
-
else if (k === ":CATEGORY")
|
|
1170
|
-
pkg.category = v;
|
|
1171
|
-
else if (k === ":URL")
|
|
1172
|
-
pkg.url = v;
|
|
1464
|
+
if (k === ":NAME") pkg.name = v;
|
|
1465
|
+
else if (k === ":VERSION") pkg.version = v;
|
|
1466
|
+
else if (k === ":DESCRIPTION") pkg.description = v;
|
|
1467
|
+
else if (k === ":FULL-NAME") pkg.fullName = v;
|
|
1468
|
+
else if (k === ":AUTHOR") pkg.author = v;
|
|
1469
|
+
else if (k === ":CATEGORY") pkg.category = v;
|
|
1470
|
+
else if (k === ":URL") pkg.url = v;
|
|
1173
1471
|
}
|
|
1174
1472
|
}
|
|
1175
1473
|
return pkg;
|
|
@@ -1177,6 +1475,20 @@ async function getPackages(filterName = null) {
|
|
|
1177
1475
|
if (typeof item === "string" || typeof item === "number") {
|
|
1178
1476
|
return { name: String(item), version: "0.0.0", loaded: true };
|
|
1179
1477
|
}
|
|
1478
|
+
if (item && typeof item === "object") {
|
|
1479
|
+
const pkg = { name: "", version: "0.0.0", description: "", loaded: true };
|
|
1480
|
+
const nameVal = item.name || item.Name || item.NAME || item[":NAME"];
|
|
1481
|
+
if (nameVal) pkg.name = String(nameVal);
|
|
1482
|
+
const verVal = item.version || item.Version || item.VERSION || item[":VERSION"];
|
|
1483
|
+
if (verVal) pkg.version = String(verVal);
|
|
1484
|
+
const descVal = item.description || item.Description || item.DESCRIPTION || item[":DESCRIPTION"];
|
|
1485
|
+
if (descVal) pkg.description = String(descVal);
|
|
1486
|
+
if (item.fullName || item.full_name) pkg.fullName = String(item.fullName || item.full_name);
|
|
1487
|
+
if (item.author || item.Author) pkg.author = String(item.author || item.Author);
|
|
1488
|
+
if (item.category || item.Category) pkg.category = String(item.category || item.Category);
|
|
1489
|
+
if (item.url || item.Url || item.URL) pkg.url = String(item.url || item.Url || item.URL);
|
|
1490
|
+
return pkg;
|
|
1491
|
+
}
|
|
1180
1492
|
return null;
|
|
1181
1493
|
}).filter(Boolean);
|
|
1182
1494
|
if (filterName) {
|
|
@@ -1197,26 +1509,123 @@ function getPlatforms() {
|
|
|
1197
1509
|
extensions: FILE_EXTENSIONS[name] || []
|
|
1198
1510
|
}));
|
|
1199
1511
|
}
|
|
1512
|
+
async function getDwgList() {
|
|
1513
|
+
const cacheKey = "cad:dwgs";
|
|
1514
|
+
const cached = getCached(cacheKey);
|
|
1515
|
+
if (cached) return cached;
|
|
1516
|
+
if (!cad.connected) await cad.connect();
|
|
1517
|
+
if (!cad.connected) return [];
|
|
1518
|
+
try {
|
|
1519
|
+
const code = `(progn
|
|
1520
|
+
(vl-load-com)
|
|
1521
|
+
(setq result nil)
|
|
1522
|
+
(vlax-for doc (vla-get-Documents (vla-get-Application (vlax-get-acad-object)))
|
|
1523
|
+
(setq result (cons (list (vla-get-Name doc) (vla-get-FullName doc)) result))
|
|
1524
|
+
)
|
|
1525
|
+
(reverse result)
|
|
1526
|
+
)`;
|
|
1527
|
+
const result = await cad.sendCommandWithResult(code);
|
|
1528
|
+
if (!result || result === "" || result === "nil") {
|
|
1529
|
+
setCache(cacheKey, []);
|
|
1530
|
+
return [];
|
|
1531
|
+
}
|
|
1532
|
+
let parsed = [];
|
|
1533
|
+
try {
|
|
1534
|
+
parsed = JSON.parse(result);
|
|
1535
|
+
} catch (e) {
|
|
1536
|
+
const listResult = parseLispList(result);
|
|
1537
|
+
if (listResult) {
|
|
1538
|
+
parsed = listResult;
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
if (!Array.isArray(parsed)) parsed = [];
|
|
1542
|
+
const dwgList = parsed.map((item) => {
|
|
1543
|
+
if (Array.isArray(item) && item.length >= 2) {
|
|
1544
|
+
return { name: String(item[0]), path: String(item[1]) };
|
|
1545
|
+
}
|
|
1546
|
+
return null;
|
|
1547
|
+
}).filter(Boolean);
|
|
1548
|
+
setCache(cacheKey, dwgList);
|
|
1549
|
+
return dwgList;
|
|
1550
|
+
} catch (e) {
|
|
1551
|
+
setCache(cacheKey, []);
|
|
1552
|
+
return [];
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
function loadStandardsFile(filename) {
|
|
1556
|
+
const filePath = path4.join(STANDARDS_DIR, filename);
|
|
1557
|
+
try {
|
|
1558
|
+
const raw = fs3.readFileSync(filePath, "utf-8");
|
|
1559
|
+
return JSON.parse(raw);
|
|
1560
|
+
} catch (e) {
|
|
1561
|
+
log(`Error loading standards file ${filename}: ${e.message}`);
|
|
1562
|
+
return null;
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
var standardsCache = /* @__PURE__ */ new Map();
|
|
1566
|
+
function getCachedStandards(key) {
|
|
1567
|
+
const entry = standardsCache.get(key);
|
|
1568
|
+
if (entry && Date.now() - entry.timestamp < 3e4) return entry.data;
|
|
1569
|
+
standardsCache.delete(key);
|
|
1570
|
+
return null;
|
|
1571
|
+
}
|
|
1572
|
+
function setCachedStandards(key, data) {
|
|
1573
|
+
standardsCache.set(key, { data, timestamp: Date.now() });
|
|
1574
|
+
}
|
|
1575
|
+
function getDraftingStandards() {
|
|
1576
|
+
const cached = getCachedStandards("drafting");
|
|
1577
|
+
if (cached) return cached;
|
|
1578
|
+
const data = loadStandardsFile("drafting.json");
|
|
1579
|
+
if (data) setCachedStandards("drafting", data);
|
|
1580
|
+
return data || { error: "\u5236\u56FE\u89C4\u8303\u6587\u4EF6\u52A0\u8F7D\u5931\u8D25" };
|
|
1581
|
+
}
|
|
1582
|
+
function getCodingStandards() {
|
|
1583
|
+
const cached = getCachedStandards("coding");
|
|
1584
|
+
if (cached) return cached;
|
|
1585
|
+
const data = loadStandardsFile("coding.json");
|
|
1586
|
+
if (data) setCachedStandards("coding", data);
|
|
1587
|
+
return data || { error: "\u7F16\u7801\u89C4\u8303\u6587\u4EF6\u52A0\u8F7D\u5931\u8D25" };
|
|
1588
|
+
}
|
|
1200
1589
|
|
|
1201
1590
|
// src/handlers/prompt-handlers.js
|
|
1591
|
+
import fs4 from "fs";
|
|
1592
|
+
import path5 from "path";
|
|
1593
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1594
|
+
var __dirname3 = path5.dirname(fileURLToPath3(import.meta.url));
|
|
1595
|
+
var PROMPTS_DIR = path5.join(__dirname3, "..", "prompts");
|
|
1596
|
+
var externalPrompts = null;
|
|
1597
|
+
var externalPromptsAt = 0;
|
|
1598
|
+
var EXTERNAL_PROMPTS_TTL = 3e4;
|
|
1599
|
+
function loadExternalPrompts() {
|
|
1600
|
+
const now = Date.now();
|
|
1601
|
+
if (externalPrompts && now - externalPromptsAt < EXTERNAL_PROMPTS_TTL) {
|
|
1602
|
+
return externalPrompts;
|
|
1603
|
+
}
|
|
1604
|
+
externalPrompts = [];
|
|
1605
|
+
try {
|
|
1606
|
+
if (!fs4.existsSync(PROMPTS_DIR)) {
|
|
1607
|
+
fs4.mkdirSync(PROMPTS_DIR, { recursive: true });
|
|
1608
|
+
return externalPrompts;
|
|
1609
|
+
}
|
|
1610
|
+
const files = fs4.readdirSync(PROMPTS_DIR).filter((f) => f.endsWith(".json"));
|
|
1611
|
+
for (const file of files) {
|
|
1612
|
+
try {
|
|
1613
|
+
const raw = fs4.readFileSync(path5.join(PROMPTS_DIR, file), "utf-8");
|
|
1614
|
+
const prompt = JSON.parse(raw);
|
|
1615
|
+
if (prompt && prompt.name) {
|
|
1616
|
+
externalPrompts.push(prompt);
|
|
1617
|
+
}
|
|
1618
|
+
} catch (e) {
|
|
1619
|
+
console.error(`Error loading prompt file ${file}: ${e.message}`);
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
} catch (e) {
|
|
1623
|
+
console.error(`Error reading prompts directory: ${e.message}`);
|
|
1624
|
+
}
|
|
1625
|
+
externalPromptsAt = now;
|
|
1626
|
+
return externalPrompts;
|
|
1627
|
+
}
|
|
1202
1628
|
var PROMPTS = [
|
|
1203
|
-
{
|
|
1204
|
-
name: "draw-residential",
|
|
1205
|
-
description: "\u751F\u6210\u519C\u6751\u6C11\u5C45\u5E73\u9762\u5E03\u7F6E\u56FE",
|
|
1206
|
-
arguments: [
|
|
1207
|
-
{ name: "width", description: "\u5EFA\u7B51\u5BBD\u5EA6 (mm)", required: true },
|
|
1208
|
-
{ name: "depth", description: "\u5EFA\u7B51\u6DF1\u5EA6 (mm)", required: true },
|
|
1209
|
-
{ name: "rooms", description: "\u623F\u95F4\u6570\u91CF", required: false }
|
|
1210
|
-
]
|
|
1211
|
-
},
|
|
1212
|
-
{
|
|
1213
|
-
name: "draw-floor-plan",
|
|
1214
|
-
description: "\u751F\u6210\u5EFA\u7B51\u697C\u5C42\u5E73\u9762\u56FE",
|
|
1215
|
-
arguments: [
|
|
1216
|
-
{ name: "floor", description: "\u697C\u5C42\u540D\u79F0", required: true },
|
|
1217
|
-
{ name: "scale", description: "\u56FE\u7EB8\u6BD4\u4F8B", required: false }
|
|
1218
|
-
]
|
|
1219
|
-
},
|
|
1220
1629
|
{
|
|
1221
1630
|
name: "analyze-drawing",
|
|
1222
1631
|
description: "\u5206\u6790\u5F53\u524D\u56FE\u7EB8\u7EDF\u8BA1\u4FE1\u606F",
|
|
@@ -1331,15 +1740,24 @@ var PROMPTS = [
|
|
|
1331
1740
|
]
|
|
1332
1741
|
}
|
|
1333
1742
|
];
|
|
1743
|
+
function findExternalPrompt(name) {
|
|
1744
|
+
const ext = loadExternalPrompts();
|
|
1745
|
+
return ext.find((p) => p.name === name);
|
|
1746
|
+
}
|
|
1334
1747
|
async function listPrompts() {
|
|
1335
|
-
|
|
1748
|
+
const ext = loadExternalPrompts();
|
|
1749
|
+
const extNames = new Set(ext.map((p) => p.name));
|
|
1750
|
+
const builtIn = PROMPTS.filter((p) => !extNames.has(p.name));
|
|
1751
|
+
return [...builtIn, ...ext];
|
|
1336
1752
|
}
|
|
1337
1753
|
async function getPrompt(name, arguments_ = {}) {
|
|
1754
|
+
const extPrompt = findExternalPrompt(name);
|
|
1755
|
+
if (extPrompt) {
|
|
1756
|
+
return {
|
|
1757
|
+
messages: extPrompt.messages || []
|
|
1758
|
+
};
|
|
1759
|
+
}
|
|
1338
1760
|
switch (name) {
|
|
1339
|
-
case "draw-residential":
|
|
1340
|
-
return generateResidentialPrompt(arguments_);
|
|
1341
|
-
case "draw-floor-plan":
|
|
1342
|
-
return generateFloorPlanPrompt(arguments_);
|
|
1343
1761
|
case "analyze-drawing":
|
|
1344
1762
|
return generateAnalyzePrompt();
|
|
1345
1763
|
case "batch-draw-lines":
|
|
@@ -1372,65 +1790,6 @@ async function getPrompt(name, arguments_ = {}) {
|
|
|
1372
1790
|
throw new Error(`Unknown prompt: ${name}`);
|
|
1373
1791
|
}
|
|
1374
1792
|
}
|
|
1375
|
-
async function generateResidentialPrompt(args) {
|
|
1376
|
-
const { width = 15e3, depth = 12e3, rooms = 6 } = args;
|
|
1377
|
-
const code = `(defun c:draw-residential (/ old-layer p)
|
|
1378
|
-
(setq old-layer (getvar "clayer"))
|
|
1379
|
-
(setvar "clayer" "0")
|
|
1380
|
-
(setq p (list 0 0))
|
|
1381
|
-
(command "_.rectangle" p (list ${width} ${depth}))
|
|
1382
|
-
${generateRoomLayout(width, depth, rooms)}
|
|
1383
|
-
(setvar "clayer" old-layer)
|
|
1384
|
-
(princ)
|
|
1385
|
-
)
|
|
1386
|
-
(c:draw-residential)`;
|
|
1387
|
-
return {
|
|
1388
|
-
messages: [{
|
|
1389
|
-
role: "user",
|
|
1390
|
-
content: {
|
|
1391
|
-
type: "text",
|
|
1392
|
-
text: `\u8BF7\u5728 CAD \u4E2D\u7ED8\u5236\u519C\u6751\u6C11\u5C45\u5E73\u9762\u56FE\uFF0C\u5C3A\u5BF8 ${width}x${depth}mm\uFF0C${rooms} \u4E2A\u623F\u95F4\u3002
|
|
1393
|
-
|
|
1394
|
-
\u751F\u6210\u7684 AutoLISP \u4EE3\u7801\uFF1A
|
|
1395
|
-
\`\`\`lisp
|
|
1396
|
-
${code}
|
|
1397
|
-
\`\`\`
|
|
1398
|
-
|
|
1399
|
-
\u76F4\u63A5\u6267\u884C\u6B64\u4EE3\u7801\u5373\u53EF\u751F\u6210\u56FE\u7EB8\u3002`
|
|
1400
|
-
}
|
|
1401
|
-
}]
|
|
1402
|
-
};
|
|
1403
|
-
}
|
|
1404
|
-
function generateRoomLayout(width, depth, rooms) {
|
|
1405
|
-
if (rooms === 6) {
|
|
1406
|
-
return `(command "_.line" (list 5000 0) (list 5000 ${depth}) "")
|
|
1407
|
-
(command "_.line" (list 0 4000) (list ${width} 4000) "")
|
|
1408
|
-
(command "_.text" "j" "c" "2500,2000" 800 0 "\u5BA2\u5385")
|
|
1409
|
-
(command "_.text" "j" "c" "7500,6000" 800 0 "\u9910\u5385")
|
|
1410
|
-
(command "_.text" "j" "c" "12500,2000" 800 0 "\u4E3B\u5367")`;
|
|
1411
|
-
}
|
|
1412
|
-
return `(command "_.text" "j" "c" (list (/ ${width} 2) (/ ${depth} 2)) 800 0 "\u623F\u95F4")`;
|
|
1413
|
-
}
|
|
1414
|
-
async function generateFloorPlanPrompt(args) {
|
|
1415
|
-
const { floor = "\u4E00\u5C42", scale = "1:100" } = args;
|
|
1416
|
-
return {
|
|
1417
|
-
messages: [{
|
|
1418
|
-
role: "user",
|
|
1419
|
-
content: {
|
|
1420
|
-
type: "text",
|
|
1421
|
-
text: `\u751F\u6210 ${floor} \u5E73\u9762\u56FE\uFF0C\u6BD4\u4F8B ${scale}\u3002
|
|
1422
|
-
|
|
1423
|
-
\u63D0\u793A\uFF1A
|
|
1424
|
-
1. \u4F7F\u7528 RECTANGLE \u547D\u4EE4\u7ED8\u5236\u5916\u5899
|
|
1425
|
-
2. \u4F7F\u7528 LINE \u547D\u4EE4\u7ED8\u5236\u5185\u90E8\u9694\u5899
|
|
1426
|
-
3. \u4F7F\u7528 TEXT \u547D\u4EE4\u6DFB\u52A0\u623F\u95F4\u6807\u6CE8
|
|
1427
|
-
4. \u4F7F\u7528 DIMLINEAR \u6216 DIMALIGNED \u6DFB\u52A0\u5C3A\u5BF8\u6807\u6CE8
|
|
1428
|
-
|
|
1429
|
-
\u9700\u8981\u6211\u6267\u884C\u54EA\u4E9B\u64CD\u4F5C\uFF1F`
|
|
1430
|
-
}
|
|
1431
|
-
}]
|
|
1432
|
-
};
|
|
1433
|
-
}
|
|
1434
1793
|
async function generateAnalyzePrompt() {
|
|
1435
1794
|
let entities = { total: 0, byType: {} };
|
|
1436
1795
|
let layers = [];
|
|
@@ -1478,14 +1837,10 @@ ${generateSuggestions(entities, layers)}`
|
|
|
1478
1837
|
}
|
|
1479
1838
|
function generateSuggestions(entities, layers) {
|
|
1480
1839
|
const suggestions = [];
|
|
1481
|
-
if (entities.total > 1e3)
|
|
1482
|
-
|
|
1483
|
-
if (
|
|
1484
|
-
|
|
1485
|
-
if (entities.byType.LINE && entities.byType.LINE > 500)
|
|
1486
|
-
suggestions.push("- \u7EBF\u6761\u8F83\u591A\uFF0C\u53EF\u8003\u8651\u4F7F\u7528 BLOCK \u51CF\u5C11\u5B9E\u4F53\u6570\u91CF");
|
|
1487
|
-
if (!entities.byType.DIMENSION)
|
|
1488
|
-
suggestions.push("- \u672A\u53D1\u73B0\u5C3A\u5BF8\u6807\u6CE8\uFF0C\u5EFA\u8BAE\u6DFB\u52A0\u6807\u6CE8");
|
|
1840
|
+
if (entities.total > 1e3) suggestions.push("- \u56FE\u7EB8\u5B9E\u4F53\u8F83\u591A\uFF0C\u5EFA\u8BAE\u4F7F\u7528\u56FE\u5C42\u5206\u7C7B\u7BA1\u7406");
|
|
1841
|
+
if (layers.length > 20) suggestions.push("- \u56FE\u5C42\u8F83\u591A\uFF0C\u5EFA\u8BAE\u6E05\u7406\u672A\u4F7F\u7528\u7684\u56FE\u5C42");
|
|
1842
|
+
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");
|
|
1843
|
+
if (!entities.byType.DIMENSION) suggestions.push("- \u672A\u53D1\u73B0\u5C3A\u5BF8\u6807\u6CE8\uFF0C\u5EFA\u8BAE\u6DFB\u52A0\u6807\u6CE8");
|
|
1489
1844
|
return suggestions.length ? suggestions.join("\n") : "- \u56FE\u7EB8\u7ED3\u6784\u826F\u597D";
|
|
1490
1845
|
}
|
|
1491
1846
|
async function generateBatchLinesPrompt(args) {
|
|
@@ -2213,11 +2568,9 @@ var SseSession = class {
|
|
|
2213
2568
|
return lines.join("\n") + "\n\n";
|
|
2214
2569
|
}
|
|
2215
2570
|
_setupDrain() {
|
|
2216
|
-
if (typeof this.#res.on !== "function")
|
|
2217
|
-
return;
|
|
2571
|
+
if (typeof this.#res.on !== "function") return;
|
|
2218
2572
|
this.#drainHandler = () => {
|
|
2219
|
-
if (!this.#active)
|
|
2220
|
-
return;
|
|
2573
|
+
if (!this.#active) return;
|
|
2221
2574
|
if (this.#backpressureBuffer.length > 0) {
|
|
2222
2575
|
this._drainBuffer();
|
|
2223
2576
|
}
|
|
@@ -2239,8 +2592,7 @@ var SseSession = class {
|
|
|
2239
2592
|
}
|
|
2240
2593
|
}
|
|
2241
2594
|
_write(content) {
|
|
2242
|
-
if (!this.#active)
|
|
2243
|
-
return false;
|
|
2595
|
+
if (!this.#active) return false;
|
|
2244
2596
|
try {
|
|
2245
2597
|
if (this.#backpressureBuffer.length > 0) {
|
|
2246
2598
|
this.#backpressureBuffer.push(content);
|
|
@@ -2289,8 +2641,8 @@ var SseSession = class {
|
|
|
2289
2641
|
sendMessage(data) {
|
|
2290
2642
|
return this.sendEvent(SSE_EVENTS.MESSAGE, data);
|
|
2291
2643
|
}
|
|
2292
|
-
sendEndpoint(
|
|
2293
|
-
return this.sendEvent(SSE_EVENTS.ENDPOINT,
|
|
2644
|
+
sendEndpoint(path8) {
|
|
2645
|
+
return this.sendEvent(SSE_EVENTS.ENDPOINT, path8);
|
|
2294
2646
|
}
|
|
2295
2647
|
sendError(code, message, id = null) {
|
|
2296
2648
|
return this.sendEvent(SSE_EVENTS.ERROR, { code, message }, id);
|
|
@@ -2344,6 +2696,42 @@ var SseSession = class {
|
|
|
2344
2696
|
};
|
|
2345
2697
|
|
|
2346
2698
|
// src/sse-session-manager.js
|
|
2699
|
+
import fs5 from "fs";
|
|
2700
|
+
import path6 from "path";
|
|
2701
|
+
import os3 from "os";
|
|
2702
|
+
var SESSION_STORE_DIR = path6.join(os3.homedir(), ".atlisp", "sessions");
|
|
2703
|
+
var SESSION_STORE_TTL = 24 * 60 * 60 * 1e3;
|
|
2704
|
+
function getSessionStorePath(clientId) {
|
|
2705
|
+
return path6.join(SESSION_STORE_DIR, `${clientId}.json`);
|
|
2706
|
+
}
|
|
2707
|
+
function persistSessionData(clientId, sessionData) {
|
|
2708
|
+
try {
|
|
2709
|
+
fs5.mkdirSync(SESSION_STORE_DIR, { recursive: true });
|
|
2710
|
+
const store = {
|
|
2711
|
+
clientId,
|
|
2712
|
+
sessionData,
|
|
2713
|
+
savedAt: Date.now()
|
|
2714
|
+
};
|
|
2715
|
+
fs5.writeFileSync(getSessionStorePath(clientId), JSON.stringify(store), "utf-8");
|
|
2716
|
+
} catch (e) {
|
|
2717
|
+
console.error("Failed to persist session:", e.message);
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2720
|
+
function loadPersistedSessionData(clientId) {
|
|
2721
|
+
try {
|
|
2722
|
+
const filePath = getSessionStorePath(clientId);
|
|
2723
|
+
if (!fs5.existsSync(filePath)) return null;
|
|
2724
|
+
const raw = fs5.readFileSync(filePath, "utf-8");
|
|
2725
|
+
const store = JSON.parse(raw);
|
|
2726
|
+
if (Date.now() - store.savedAt > SESSION_STORE_TTL) {
|
|
2727
|
+
fs5.unlinkSync(filePath);
|
|
2728
|
+
return null;
|
|
2729
|
+
}
|
|
2730
|
+
return store.sessionData || {};
|
|
2731
|
+
} catch (e) {
|
|
2732
|
+
return null;
|
|
2733
|
+
}
|
|
2734
|
+
}
|
|
2347
2735
|
var SseSessionManager = class {
|
|
2348
2736
|
#sessions = /* @__PURE__ */ new Map();
|
|
2349
2737
|
#eventHandlers = /* @__PURE__ */ new Map();
|
|
@@ -2355,12 +2743,19 @@ var SseSessionManager = class {
|
|
|
2355
2743
|
if (!(session instanceof SseSession)) {
|
|
2356
2744
|
throw new Error("session must be an SseSession instance");
|
|
2357
2745
|
}
|
|
2746
|
+
const persisted = loadPersistedSessionData(clientId);
|
|
2747
|
+
if (persisted) {
|
|
2748
|
+
for (const [key, value] of Object.entries(persisted)) {
|
|
2749
|
+
session.setSessionData(key, value);
|
|
2750
|
+
}
|
|
2751
|
+
}
|
|
2358
2752
|
this.#sessions.set(clientId, session);
|
|
2359
2753
|
session.startHeartbeat();
|
|
2360
2754
|
}
|
|
2361
2755
|
remove(clientId) {
|
|
2362
2756
|
const session = this.#sessions.get(clientId);
|
|
2363
2757
|
if (session) {
|
|
2758
|
+
persistSessionData(clientId, session.sessionData);
|
|
2364
2759
|
session.close();
|
|
2365
2760
|
this.#sessions.delete(clientId);
|
|
2366
2761
|
return true;
|
|
@@ -2449,6 +2844,7 @@ var SseSessionManager = class {
|
|
|
2449
2844
|
}
|
|
2450
2845
|
closeAll() {
|
|
2451
2846
|
for (const [clientId, session] of this.#sessions) {
|
|
2847
|
+
persistSessionData(clientId, session.sessionData);
|
|
2452
2848
|
session.close();
|
|
2453
2849
|
}
|
|
2454
2850
|
this.#sessions.clear();
|
|
@@ -2456,9 +2852,12 @@ var SseSessionManager = class {
|
|
|
2456
2852
|
};
|
|
2457
2853
|
|
|
2458
2854
|
// src/atlisp-mcp.js
|
|
2459
|
-
var
|
|
2855
|
+
var __dirname4 = path7.dirname(fileURLToPath4(import.meta.url));
|
|
2460
2856
|
var require2 = createRequire(import.meta.url);
|
|
2461
|
-
var { version } = require2(
|
|
2857
|
+
var { version } = require2(path7.join(__dirname4, "..", "package.json"));
|
|
2858
|
+
var CACHE_DIR2 = path7.join(os4.homedir(), ".atlisp", "cache");
|
|
2859
|
+
var FUNCLIB_CACHE_FILE2 = path7.join(CACHE_DIR2, "functions.json");
|
|
2860
|
+
var FUNCLIB_CACHE_TTL2 = 24 * 60 * 60 * 1e3;
|
|
2462
2861
|
var isMcpScript = process.argv[1]?.includes("atlisp-mcp");
|
|
2463
2862
|
if (isMcpScript) {
|
|
2464
2863
|
const args = process.argv.slice(2);
|
|
@@ -2510,8 +2909,7 @@ var SERVER_CAPABILITIES = {
|
|
|
2510
2909
|
prompts: {}
|
|
2511
2910
|
};
|
|
2512
2911
|
var { port, host, transport, apiKey, rateLimitWindow, rateLimitMax } = config_default;
|
|
2513
|
-
if (apiKey)
|
|
2514
|
-
log("API Key authentication enabled");
|
|
2912
|
+
if (apiKey) log("API Key authentication enabled");
|
|
2515
2913
|
var mcpLimiter = rateLimit({
|
|
2516
2914
|
windowMs: rateLimitWindow,
|
|
2517
2915
|
max: rateLimitMax,
|
|
@@ -2523,26 +2921,52 @@ var FUNCTION_LIB_URL = process.env.FUNCTION_LIB_URL || "http://s3.atlisp.cn/json
|
|
|
2523
2921
|
var cachedFunctionLib = null;
|
|
2524
2922
|
var cachedAt2 = null;
|
|
2525
2923
|
var CACHE_TTL3 = 5 * 60 * 1e3;
|
|
2924
|
+
async function readCacheFromDisk2() {
|
|
2925
|
+
try {
|
|
2926
|
+
const stat = fs6.statSync(FUNCLIB_CACHE_FILE2);
|
|
2927
|
+
if (Date.now() - stat.mtimeMs < FUNCLIB_CACHE_TTL2) {
|
|
2928
|
+
const raw = fs6.readFileSync(FUNCLIB_CACHE_FILE2, "utf-8");
|
|
2929
|
+
return JSON.parse(raw);
|
|
2930
|
+
}
|
|
2931
|
+
} catch {
|
|
2932
|
+
}
|
|
2933
|
+
return null;
|
|
2934
|
+
}
|
|
2935
|
+
function writeCacheToDisk2(data) {
|
|
2936
|
+
try {
|
|
2937
|
+
fs6.mkdirSync(CACHE_DIR2, { recursive: true });
|
|
2938
|
+
fs6.writeFileSync(FUNCLIB_CACHE_FILE2, JSON.stringify(data), "utf-8");
|
|
2939
|
+
} catch (e) {
|
|
2940
|
+
log(`writeCacheToDisk error: ${e.message}`);
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
2526
2943
|
async function loadAtlibFunctionLib() {
|
|
2527
2944
|
const now = Date.now();
|
|
2528
2945
|
if (cachedFunctionLib && cachedAt2 && now - cachedAt2 < CACHE_TTL3) {
|
|
2529
2946
|
return cachedFunctionLib;
|
|
2530
2947
|
}
|
|
2948
|
+
const diskCache = await readCacheFromDisk2();
|
|
2949
|
+
if (diskCache) {
|
|
2950
|
+
cachedFunctionLib = diskCache;
|
|
2951
|
+
cachedAt2 = now;
|
|
2952
|
+
return diskCache;
|
|
2953
|
+
}
|
|
2531
2954
|
try {
|
|
2532
2955
|
const response = await fetch(FUNCTION_LIB_URL, { headers: { "User-Agent": "atlisp-mcp" } });
|
|
2533
2956
|
if (response.ok) {
|
|
2534
2957
|
const data = await response.json();
|
|
2535
2958
|
cachedFunctionLib = data;
|
|
2536
2959
|
cachedAt2 = now;
|
|
2960
|
+
writeCacheToDisk2(data);
|
|
2537
2961
|
return data;
|
|
2538
2962
|
}
|
|
2539
2963
|
} catch (e) {
|
|
2540
2964
|
log(`loadAtlibFunctionLib error: ${e.message}`);
|
|
2541
2965
|
}
|
|
2542
|
-
return
|
|
2966
|
+
return cachedFunctionLib;
|
|
2543
2967
|
}
|
|
2544
2968
|
var TOOL_HANDLERS = {
|
|
2545
|
-
connect_cad: () => connectCad(),
|
|
2969
|
+
connect_cad: (a) => connectCad(a?.platform),
|
|
2546
2970
|
eval_lisp: (a) => evalLisp(a.code),
|
|
2547
2971
|
eval_lisp_with_result: (a) => evalLisp(a.code, true, a.encoding),
|
|
2548
2972
|
get_cad_info: () => getCadInfo(),
|
|
@@ -2560,10 +2984,17 @@ ${CAD_PLATFORMS.join("\n")}
|
|
|
2560
2984
|
get_function_usage: (a) => getFunctionUsage(a.name, a.package),
|
|
2561
2985
|
list_symbols: (a) => listSymbols(a.package),
|
|
2562
2986
|
get_system_status: () => getSystemStatus(),
|
|
2987
|
+
list_prompts: async () => {
|
|
2988
|
+
const prompts = await listPrompts();
|
|
2989
|
+
return { content: [{ type: "text", text: JSON.stringify(prompts) }] };
|
|
2990
|
+
},
|
|
2991
|
+
get_prompt: async (a) => {
|
|
2992
|
+
const prompt = await getPrompt(a.name, a.args || {});
|
|
2993
|
+
return { content: prompt.messages.map((m) => ({ type: "text", text: m.content.text || m.content })) };
|
|
2994
|
+
},
|
|
2563
2995
|
import_funlib: async (a) => {
|
|
2564
2996
|
const data = await loadAtlibFunctionLib();
|
|
2565
|
-
if (!data)
|
|
2566
|
-
return { content: [{ type: "text", text: "\u52A0\u8F7D\u51FD\u6570\u5E93\u5931\u8D25" }], isError: true };
|
|
2997
|
+
if (!data) return { content: [{ type: "text", text: "\u52A0\u8F7D\u51FD\u6570\u5E93\u5931\u8D25" }], isError: true };
|
|
2567
2998
|
if (a.format === "list") {
|
|
2568
2999
|
const items = Object.values(data.all_functions || {}).map((f) => `${f.name}: ${f.description || ""}`);
|
|
2569
3000
|
return { content: [{ type: "text", text: items.join("\n") }] };
|
|
@@ -2579,7 +3010,7 @@ function getOrCreateSessionServer(sessionId) {
|
|
|
2579
3010
|
server.connect(transport2);
|
|
2580
3011
|
entry = { server, transport: transport2 };
|
|
2581
3012
|
sessionServers.set(sessionId, entry);
|
|
2582
|
-
|
|
3013
|
+
sessionTransports.set(sessionId, transport2);
|
|
2583
3014
|
}
|
|
2584
3015
|
return entry;
|
|
2585
3016
|
}
|
|
@@ -2591,7 +3022,7 @@ function removeSessionServer(sessionId) {
|
|
|
2591
3022
|
entry.server.close().catch(() => {
|
|
2592
3023
|
});
|
|
2593
3024
|
sessionServers.delete(sessionId);
|
|
2594
|
-
|
|
3025
|
+
sessionTransports.delete(sessionId);
|
|
2595
3026
|
}
|
|
2596
3027
|
}
|
|
2597
3028
|
function broadcastToAllSessions(uri) {
|
|
@@ -2605,7 +3036,12 @@ var tools = [
|
|
|
2605
3036
|
{
|
|
2606
3037
|
name: "connect_cad",
|
|
2607
3038
|
description: "\u8FDE\u63A5\u5230 CAD (AutoCAD/ZWCAD/GStarCAD/BricsCAD)",
|
|
2608
|
-
inputSchema: {
|
|
3039
|
+
inputSchema: {
|
|
3040
|
+
type: "object",
|
|
3041
|
+
properties: {
|
|
3042
|
+
platform: { type: "string", description: "\u6307\u5B9A CAD \u5E73\u53F0: AutoCAD, ZWCAD, GStarCAD, BricsCAD\uFF08\u53EF\u9009\uFF0C\u81EA\u52A8\u68C0\u6D4B\uFF09" }
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
2609
3045
|
},
|
|
2610
3046
|
{
|
|
2611
3047
|
name: "eval_lisp",
|
|
@@ -2726,12 +3162,28 @@ var tools = [
|
|
|
2726
3162
|
name: "get_system_status",
|
|
2727
3163
|
description: "\u83B7\u53D6\u5B8C\u6574\u7CFB\u7EDF\u8BCA\u65AD\u4FE1\u606F\uFF1AMCP \u72B6\u6001\u3001CAD \u8FDE\u63A5\u3001Lisp \u6267\u884C\u3001\u5305\u5217\u8868",
|
|
2728
3164
|
inputSchema: { type: "object", properties: {} }
|
|
3165
|
+
},
|
|
3166
|
+
{
|
|
3167
|
+
name: "list_prompts",
|
|
3168
|
+
description: "\u5217\u51FA\u6240\u6709\u53EF\u7528\u7684 @lisp \u63D0\u793A\u6A21\u677F",
|
|
3169
|
+
inputSchema: { type: "object", properties: {} }
|
|
3170
|
+
},
|
|
3171
|
+
{
|
|
3172
|
+
name: "get_prompt",
|
|
3173
|
+
description: "\u83B7\u53D6\u6307\u5B9A\u63D0\u793A\u6A21\u677F\u7684\u5185\u5BB9",
|
|
3174
|
+
inputSchema: {
|
|
3175
|
+
type: "object",
|
|
3176
|
+
properties: {
|
|
3177
|
+
name: { type: "string", description: "\u63D0\u793A\u6A21\u677F\u540D\u79F0" },
|
|
3178
|
+
args: { type: "object", description: "\u6A21\u677F\u53C2\u6570\uFF08\u53EF\u9009\uFF09" }
|
|
3179
|
+
},
|
|
3180
|
+
required: ["name"]
|
|
3181
|
+
}
|
|
2729
3182
|
}
|
|
2730
3183
|
];
|
|
2731
3184
|
async function handleToolCall(name, args) {
|
|
2732
3185
|
const handler = TOOL_HANDLERS[name];
|
|
2733
|
-
if (!handler)
|
|
2734
|
-
return { content: [{ type: "text", text: `\u672A\u77E5\u5DE5\u5177: ${name}` }], isError: true };
|
|
3186
|
+
if (!handler) return { content: [{ type: "text", text: `\u672A\u77E5\u5DE5\u5177: ${name}` }], isError: true };
|
|
2735
3187
|
try {
|
|
2736
3188
|
const result = await handler(args || {});
|
|
2737
3189
|
notifyResourceChanges(name);
|
|
@@ -2743,8 +3195,8 @@ async function handleToolCall(name, args) {
|
|
|
2743
3195
|
}
|
|
2744
3196
|
function notifyResourceChanges(toolName) {
|
|
2745
3197
|
const resourceMap = {
|
|
2746
|
-
connect_cad: ["atlisp://cad/info", "atlisp://
|
|
2747
|
-
new_document: ["atlisp://dwg/name", "atlisp://dwg/path", "atlisp://cad/layers", "atlisp://
|
|
3198
|
+
connect_cad: ["atlisp://cad/info", "atlisp://dwg/layers", "atlisp://dwg/name", "atlisp://dwg/path", "atlisp://cad/dwgs", "atlisp://packages"],
|
|
3199
|
+
new_document: ["atlisp://dwg/name", "atlisp://dwg/path", "atlisp://cad/dwgs", "atlisp://dwg/layers", "atlisp://dwg/entities", "atlisp://dwg/texts"],
|
|
2748
3200
|
install_package: ["atlisp://packages"],
|
|
2749
3201
|
install_atlisp: ["atlisp://packages", "atlisp://cad/info"],
|
|
2750
3202
|
init_atlisp: ["atlisp://packages", "atlisp://cad/info"]
|
|
@@ -2774,7 +3226,7 @@ async function initCadConnection() {
|
|
|
2774
3226
|
}
|
|
2775
3227
|
}
|
|
2776
3228
|
var sessionServers = /* @__PURE__ */ new Map();
|
|
2777
|
-
var
|
|
3229
|
+
var sessionTransports = /* @__PURE__ */ new Map();
|
|
2778
3230
|
function createMcpServer() {
|
|
2779
3231
|
const server = new Server(
|
|
2780
3232
|
{ name: SERVER_NAME, version: SERVER_VERSION },
|
|
@@ -2854,8 +3306,7 @@ async function startServer() {
|
|
|
2854
3306
|
const JSON_CONTENT_TYPES = ["application/json", "application/json-rpc", "application/vnd.api+json"];
|
|
2855
3307
|
app.use(async (req, res, next) => {
|
|
2856
3308
|
const ct = (req.headers["content-type"] || "").toLowerCase();
|
|
2857
|
-
if (!JSON_CONTENT_TYPES.some((t) => ct.startsWith(t)))
|
|
2858
|
-
return next();
|
|
3309
|
+
if (!JSON_CONTENT_TYPES.some((t) => ct.startsWith(t))) return next();
|
|
2859
3310
|
try {
|
|
2860
3311
|
const buf = await rawBody(req, { limit: "10mb" });
|
|
2861
3312
|
const m = ct.match(/charset\s*=\s*([^\s;]+)/i);
|
|
@@ -2876,11 +3327,9 @@ async function startServer() {
|
|
|
2876
3327
|
const PUBLIC_PATHS = ["/health"];
|
|
2877
3328
|
if (apiKey) {
|
|
2878
3329
|
app.use((req, res, next) => {
|
|
2879
|
-
if (PUBLIC_PATHS.includes(req.path))
|
|
2880
|
-
return next();
|
|
3330
|
+
if (PUBLIC_PATHS.includes(req.path)) return next();
|
|
2881
3331
|
const auth = req.get("Authorization");
|
|
2882
|
-
if (auth === `Bearer ${apiKey}`)
|
|
2883
|
-
return next();
|
|
3332
|
+
if (auth === `Bearer ${apiKey}`) return next();
|
|
2884
3333
|
res.status(401).json({ error: "Unauthorized" });
|
|
2885
3334
|
});
|
|
2886
3335
|
}
|
|
@@ -2895,8 +3344,7 @@ async function startServer() {
|
|
|
2895
3344
|
res.setHeader("Access-Control-Expose-Headers", "mcp-session-id, content-type, x-accel-buffering");
|
|
2896
3345
|
res.setHeader("Access-Control-Max-Age", "86400");
|
|
2897
3346
|
res.setHeader("Vary", "Accept");
|
|
2898
|
-
if (req.method === "OPTIONS")
|
|
2899
|
-
return res.status(204).end();
|
|
3347
|
+
if (req.method === "OPTIONS") return res.status(204).end();
|
|
2900
3348
|
next();
|
|
2901
3349
|
});
|
|
2902
3350
|
}
|