@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.
@@ -1,9 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/atlisp-mcp.js
4
- import path4 from "path";
5
- import { fileURLToPath as fileURLToPath2 } from "url";
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.0";
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 result = await sendMessage({ type: "connect" });
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("connect_cad called, cad.connected before: " + cad.connected);
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 null;
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
- await cad.connect();
649
- if (!cad.connected)
650
- return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
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
- return { content: [{ type: "text", text: result }] };
654
- const packages = MOCK_PACKAGES.map((p) => `${p.name} v${p.version} - ${p.description}`);
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
- ${packages.join("\n")}` }] };
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 pkg = MOCK_PACKAGES.find((p) => p.name === packageName);
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/promises";
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
- async function fileExists(filePath) {
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
- await fs2.access(filePath);
703
- return true;
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
- try {
712
- const response = await fetch(FUNCTIONS_URL, { headers: { "User-Agent": "atlisp-mcp" } });
713
- if (response.ok) {
714
- const data = await response.json();
715
- const funcs = Object.values(data.all_functions || {});
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 && await fileExists(FUNCTIONS_DIR)) {
724
- const files = (await fs2.readdir(FUNCTIONS_DIR)).filter((f2) => f2.endsWith(".json"));
725
- for (const file of files) {
726
- const raw = await fs2.readFile(path3.join(FUNCTIONS_DIR, file), "utf-8");
727
- const data = JSON.parse(raw);
728
- const func = data.functions?.find((f2) => f2.name === funcName);
729
- if (func)
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
- try {
751
- const response = await fetch(FUNCTIONS_URL, { headers: { "User-Agent": "atlisp-mcp" } });
752
- if (response.ok) {
753
- const data = await response.json();
754
- const funcs = Object.values(data.all_functions || {});
755
- if (packageName) {
756
- allFuncs = funcs.filter((f) => f.name.startsWith(`${packageName}:`));
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 && await fileExists(FUNCTIONS_DIR)) {
765
- const files = (await fs2.readdir(FUNCTIONS_DIR)).filter((f) => f.endsWith(".json"));
766
- for (const file of files) {
767
- try {
768
- const raw = await fs2.readFile(path3.join(FUNCTIONS_DIR, file), "utf-8");
769
- const data = JSON.parse(raw);
770
- const funcs = Object.values(data.all_functions || {});
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
- return null;
877
- if (t === "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
- return num;
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://cad/layers", name: "Layers", description: "\u56FE\u5C42\u5217\u8868", mimeType: "application/json", subscribable: true },
892
- { uri: "atlisp://cad/entities", name: "Entities", description: "\u5B9E\u4F53\u7EDF\u8BA1", mimeType: "application/json", subscribable: true },
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://cad/layers":
1208
+ case "atlisp://dwg/layers":
928
1209
  return await getLayers(params.name);
929
- case "atlisp://cad/entities":
930
- return await getEntities(params.type);
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
- return cached;
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(filterType = null) {
1023
- const cacheKey = filterType ? `entities:${filterType}` : "entities:all";
1307
+ async function getEntities(params = {}) {
1308
+ const cacheKey = `dwg:entities:${JSON.stringify(params)}`;
1024
1309
  const cached = getCached(cacheKey);
1025
- if (cached)
1026
- return cached;
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, byType: {} };
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
- let code;
1036
- if (filterType) {
1037
- code = `(sslength (ssget "_X" (list (cons 0 "${filterType}"))))`;
1038
- const count = await cad.sendCommandWithResult(code);
1039
- const result = { type: filterType, count: parseInt(count) || 0 };
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
- } else {
1043
- code = `(progn(setq ss(ssget "_X"))(if ss(progn(setq elst(mapcar (quote(lambda(x)(cdr(assoc 0 (entget x))))) (pickset:to-list ss)))(stat:stat elst))))`;
1044
- const resultStr = await cad.sendCommandWithResult(code);
1045
- if (!resultStr || resultStr === "" || resultStr === "nil") {
1046
- const result2 = { total: 0, byType: {} };
1047
- setCache(cacheKey, result2);
1048
- return result2;
1049
- }
1050
- let statResult = [];
1051
- try {
1052
- statResult = JSON.parse(resultStr);
1053
- } catch (e) {
1054
- const parsed = parseLispList(resultStr);
1055
- if (parsed) {
1056
- statResult = parsed;
1057
- }
1058
- }
1059
- if (!Array.isArray(statResult)) {
1060
- const result2 = { total: 0, byType: {} };
1061
- setCache(cacheKey, result2);
1062
- return result2;
1063
- }
1064
- const byType = {};
1065
- statResult.forEach((item) => {
1066
- if (Array.isArray(item) && item.length >= 2) {
1067
- byType[item[0]] = item[1];
1068
- }
1069
- });
1070
- const total = Object.values(byType).reduce((sum, n) => sum + n, 0);
1071
- const result = { total, byType };
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
- const result = { total: 0, byType: {} };
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
- await cad.connect();
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
- await cad.connect();
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
- return cached;
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
- pkg.name = v;
1161
- else if (k === ":VERSION")
1162
- pkg.version = v;
1163
- else if (k === ":DESCRIPTION")
1164
- pkg.description = v;
1165
- else if (k === ":FULL-NAME")
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
- return PROMPTS;
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
- suggestions.push("- \u56FE\u7EB8\u5B9E\u4F53\u8F83\u591A\uFF0C\u5EFA\u8BAE\u4F7F\u7528\u56FE\u5C42\u5206\u7C7B\u7BA1\u7406");
1483
- if (layers.length > 20)
1484
- suggestions.push("- \u56FE\u5C42\u8F83\u591A\uFF0C\u5EFA\u8BAE\u6E05\u7406\u672A\u4F7F\u7528\u7684\u56FE\u5C42");
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(path5) {
2293
- return this.sendEvent(SSE_EVENTS.ENDPOINT, path5);
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 __dirname2 = path4.dirname(fileURLToPath2(import.meta.url));
2855
+ var __dirname4 = path7.dirname(fileURLToPath4(import.meta.url));
2460
2856
  var require2 = createRequire(import.meta.url);
2461
- var { version } = require2(path4.join(__dirname2, "..", "package.json"));
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 null;
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
- sessionTranports.set(sessionId, transport2);
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
- sessionTranports.delete(sessionId);
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: { type: "object", properties: {} }
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://cad/layers", "atlisp://dwg/name", "atlisp://dwg/path", "atlisp://packages"],
2747
- new_document: ["atlisp://dwg/name", "atlisp://dwg/path", "atlisp://cad/layers", "atlisp://cad/entities"],
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 sessionTranports = /* @__PURE__ */ new Map();
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
  }