@atlisp/mcp 1.8.21 → 1.8.25

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.
@@ -7,10 +7,10 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
7
7
  if (typeof require !== "undefined") return require.apply(this, arguments);
8
8
  throw Error('Dynamic require of "' + x + '" is not supported');
9
9
  });
10
- var __glob = (map) => (path20) => {
11
- var fn = map[path20];
10
+ var __glob = (map) => (path21) => {
11
+ var fn = map[path21];
12
12
  if (fn) return fn();
13
- throw new Error("Module not found in bundle: " + path20);
13
+ throw new Error("Module not found in bundle: " + path21);
14
14
  };
15
15
  var __esm = (fn, res) => function __init() {
16
16
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
@@ -37,7 +37,7 @@ function parseArgs() {
37
37
  } else if (args[i] === "--ws") {
38
38
  result.transport = "ws";
39
39
  } else if (args[i] === "--port" && args[i + 1]) {
40
- result.port = args[i + 1];
40
+ result.port = parseInt(args[i + 1], 10);
41
41
  i++;
42
42
  } else if (args[i] === "--host" && args[i + 1]) {
43
43
  result.host = args[i + 1];
@@ -111,41 +111,42 @@ var envSchema, configSchema, cliArgs, envResult, env, configFilePath, fileConfig
111
111
  var init_config = __esm({
112
112
  "src/config.js"() {
113
113
  envSchema = z.object({
114
- PORT: z.string().optional().default("8110"),
114
+ PORT: z.coerce.number().optional().default(8110),
115
115
  HOST: z.string().optional().default("0.0.0.0"),
116
116
  TRANSPORT: z.enum(["http", "stdio", "ws"]).optional().default("http"),
117
117
  MCP_API_KEY: z.string().optional().default(""),
118
- ENABLE_SSE: z.string().optional().default("true"),
119
- MESSAGE_TIMEOUT: z.string().optional().default("60000"),
120
- HEARTBEAT_INTERVAL: z.string().optional().default("30000"),
121
- BUSY_RETRIES: z.string().optional().default("10"),
122
- BUSY_DELAY: z.string().optional().default("500"),
123
- ENABLE_CORS: z.string().optional().default("true"),
124
- CORS_ORIGIN: z.string().optional().default("*"),
125
- RATE_LIMIT_WINDOW: z.string().optional().default("60000"),
126
- RATE_LIMIT_MAX: z.string().optional().default("100"),
127
- RESOURCE_CACHE_TTL: z.string().optional().default("5000"),
128
- FUNCTION_LIB_CACHE_TTL: z.string().optional().default("300000"),
129
- DEBUG: z.string().optional().default("false"),
118
+ ENABLE_SSE: z.coerce.boolean().optional().default(true),
119
+ MESSAGE_TIMEOUT: z.coerce.number().optional().default(6e4),
120
+ HEARTBEAT_INTERVAL: z.coerce.number().optional().default(3e4),
121
+ BUSY_RETRIES: z.coerce.number().optional().default(10),
122
+ BUSY_DELAY: z.coerce.number().optional().default(500),
123
+ ENABLE_CORS: z.coerce.boolean().optional().default(true),
124
+ CORS_ORIGIN: z.string().optional().default("https://atlisp.cn"),
125
+ RATE_LIMIT_WINDOW: z.coerce.number().optional().default(6e4),
126
+ RATE_LIMIT_MAX: z.coerce.number().optional().default(100),
127
+ RESOURCE_CACHE_TTL: z.coerce.number().optional().default(5e3),
128
+ FUNCTION_LIB_CACHE_TTL: z.coerce.number().optional().default(3e5),
129
+ DEBUG: z.coerce.boolean().optional().default(false),
130
130
  DEBUG_FILE: z.string().optional().default(""),
131
131
  FUNCTION_LIB_URL: z.string().optional().default("http://s3.atlisp.cn/json/functions.json"),
132
132
  CONFIG_FILE: z.string().optional().default(""),
133
- API_KEY_RATE_LIMIT: z.string().optional().default("1000"),
134
- METRICS_ENABLED: z.string().optional().default("true"),
135
- REQUEST_LOG_ENABLED: z.string().optional().default("true"),
133
+ API_KEY_RATE_LIMIT: z.coerce.number().optional().default(1e3),
134
+ METRICS_ENABLED: z.coerce.boolean().optional().default(true),
135
+ REQUEST_LOG_ENABLED: z.coerce.boolean().optional().default(true),
136
136
  SSL_KEY_PATH: z.string().optional().default(""),
137
137
  SSL_CERT_PATH: z.string().optional().default(""),
138
138
  LLM_API_KEY: z.string().optional().default(""),
139
139
  LLM_API_URL: z.string().optional().default("https://api.openai.com/v1/chat/completions"),
140
140
  LLM_MODEL: z.string().optional().default("gpt-4o-mini"),
141
- LLM_MAX_TOKENS: z.string().optional().default("4096"),
142
- LLM_TEMPERATURE: z.string().optional().default("0.7"),
143
- WORKER_POOL_SIZE: z.string().optional().default("2"),
141
+ LLM_MAX_TOKENS: z.coerce.number().optional().default(4096),
142
+ LLM_TEMPERATURE: z.coerce.number().optional().default(0.7),
143
+ WORKER_POOL_SIZE: z.coerce.number().optional().default(2),
144
144
  LINT_PRESET: z.string().optional().default(""),
145
145
  LINT_CONFIG_PATH: z.string().optional().default(""),
146
146
  LINT_LEVEL: z.string().optional().default(""),
147
- NO_LINT: z.string().optional().default(""),
148
- SECURITY_LEVEL: z.string().optional().default("strict")
147
+ NO_LINT: z.coerce.boolean().optional().default(false),
148
+ SECURITY_LEVEL: z.string().optional().default("strict"),
149
+ CLIENT_ID: z.string().optional().default("")
149
150
  });
150
151
  configSchema = z.object({
151
152
  port: z.number().optional(),
@@ -188,7 +189,8 @@ var init_config = __esm({
188
189
  lintConfigPath: z.string().optional(),
189
190
  lintLevel: z.string().optional(),
190
191
  noLint: z.boolean().optional(),
191
- securityLevel: z.enum(["strict", "standard", "relaxed"]).optional()
192
+ securityLevel: z.enum(["strict", "standard", "relaxed"]).optional(),
193
+ clientId: z.string().optional()
192
194
  });
193
195
  cliArgs = parseArgs();
194
196
  envResult = envSchema.safeParse(process.env);
@@ -200,43 +202,44 @@ var init_config = __esm({
200
202
  configFilePath = env.CONFIG_FILE || cliArgs.configFile || path.join(os.homedir(), ".atlisp", "atlisp.config.json");
201
203
  fileConfig = loadConfigFile(configFilePath);
202
204
  config = {
203
- port: fileConfig?.port || parseInt(cliArgs.port || env.PORT, 10),
204
- host: fileConfig?.host || cliArgs.host || env.HOST,
205
+ port: fileConfig?.port ?? cliArgs.port ?? env.PORT,
206
+ host: fileConfig?.host ?? cliArgs.host ?? env.HOST,
205
207
  transport: fileConfig?.transport ?? cliArgs.transport ?? env.TRANSPORT,
206
- apiKey: fileConfig?.apiKey || env.MCP_API_KEY,
207
- enableSse: fileConfig?.enableSse ?? env.ENABLE_SSE === "true",
208
- messageTimeout: fileConfig?.messageTimeout || parseInt(env.MESSAGE_TIMEOUT, 10),
209
- heartbeatInterval: fileConfig?.heartbeatInterval || parseInt(env.HEARTBEAT_INTERVAL, 10),
210
- busyRetries: fileConfig?.busyRetries || parseInt(env.BUSY_RETRIES, 10),
211
- busyDelay: fileConfig?.busyDelay || parseInt(env.BUSY_DELAY, 10),
212
- enableCors: fileConfig?.enableCors ?? env.ENABLE_CORS === "true",
213
- corsOrigin: fileConfig?.corsOrigin || env.CORS_ORIGIN,
214
- rateLimitWindow: fileConfig?.rateLimitWindow || parseInt(env.RATE_LIMIT_WINDOW, 10),
215
- rateLimitMax: fileConfig?.rateLimitMax || parseInt(env.RATE_LIMIT_MAX, 10),
216
- resourceCacheTtl: fileConfig?.resourceCacheTtl || parseInt(env.RESOURCE_CACHE_TTL, 10),
217
- functionLibCacheTtl: fileConfig?.functionLibCacheTtl || parseInt(env.FUNCTION_LIB_CACHE_TTL, 10),
218
- debug: fileConfig?.debug ?? env.DEBUG === "true",
219
- debugFile: fileConfig?.debugFile || env.DEBUG_FILE,
220
- functionLibUrl: fileConfig?.functionLibUrl || env.FUNCTION_LIB_URL,
208
+ apiKey: fileConfig?.apiKey ?? env.MCP_API_KEY,
209
+ enableSse: fileConfig?.enableSse ?? env.ENABLE_SSE,
210
+ messageTimeout: fileConfig?.messageTimeout ?? env.MESSAGE_TIMEOUT,
211
+ heartbeatInterval: fileConfig?.heartbeatInterval ?? env.HEARTBEAT_INTERVAL,
212
+ busyRetries: fileConfig?.busyRetries ?? env.BUSY_RETRIES,
213
+ busyDelay: fileConfig?.busyDelay ?? env.BUSY_DELAY,
214
+ enableCors: fileConfig?.enableCors ?? env.ENABLE_CORS,
215
+ corsOrigin: fileConfig?.corsOrigin ?? env.CORS_ORIGIN,
216
+ rateLimitWindow: fileConfig?.rateLimitWindow ?? env.RATE_LIMIT_WINDOW,
217
+ rateLimitMax: fileConfig?.rateLimitMax ?? env.RATE_LIMIT_MAX,
218
+ resourceCacheTtl: fileConfig?.resourceCacheTtl ?? env.RESOURCE_CACHE_TTL,
219
+ functionLibCacheTtl: fileConfig?.functionLibCacheTtl ?? env.FUNCTION_LIB_CACHE_TTL,
220
+ debug: fileConfig?.debug ?? env.DEBUG,
221
+ debugFile: fileConfig?.debugFile ?? env.DEBUG_FILE,
222
+ functionLibUrl: fileConfig?.functionLibUrl ?? env.FUNCTION_LIB_URL,
221
223
  configFile: configFilePath,
222
- apiKeyRateLimit: fileConfig?.apiKeyRateLimit || parseInt(env.API_KEY_RATE_LIMIT, 10),
223
- apiKeys: fileConfig?.apiKeys || [],
224
- metricsEnabled: fileConfig?.metricsEnabled ?? env.METRICS_ENABLED === "true",
225
- requestLogEnabled: fileConfig?.requestLogEnabled ?? env.REQUEST_LOG_ENABLED !== "false",
226
- sslKey: fileConfig?.sslKey || env.SSL_KEY_PATH || cliArgs.sslKey,
227
- sslCert: fileConfig?.sslCert || env.SSL_CERT_PATH || cliArgs.sslCert,
228
- llmApiKey: fileConfig?.llmApiKey || env.LLM_API_KEY,
229
- llmApiUrl: fileConfig?.llmApiUrl || env.LLM_API_URL,
230
- llmModel: fileConfig?.llmModel || env.LLM_MODEL,
231
- llmMaxTokens: fileConfig?.llmMaxTokens || parseInt(env.LLM_MAX_TOKENS, 10),
232
- llmTemperature: fileConfig?.llmTemperature || parseFloat(env.LLM_TEMPERATURE),
233
- maxPoolSize: fileConfig?.maxPoolSize || 3,
234
- workerPoolSize: fileConfig?.workerPoolSize || parseInt(env.WORKER_POOL_SIZE, 10),
235
- lintPreset: fileConfig?.lintPreset || cliArgs.lintPreset || env.LINT_PRESET || void 0,
236
- lintConfigPath: fileConfig?.lintConfigPath || cliArgs.lintConfigPath || env.LINT_CONFIG_PATH || void 0,
237
- lintLevel: fileConfig?.lintLevel || cliArgs.lintLevel || env.LINT_LEVEL || void 0,
238
- noLint: fileConfig?.noLint ?? cliArgs.noLint ?? env.NO_LINT === "true",
239
- securityLevel: fileConfig?.securityLevel ?? cliArgs.securityLevel ?? (env.SECURITY_LEVEL || "strict")
224
+ apiKeyRateLimit: fileConfig?.apiKeyRateLimit ?? env.API_KEY_RATE_LIMIT,
225
+ apiKeys: fileConfig?.apiKeys ?? [],
226
+ metricsEnabled: fileConfig?.metricsEnabled ?? env.METRICS_ENABLED,
227
+ requestLogEnabled: fileConfig?.requestLogEnabled ?? env.REQUEST_LOG_ENABLED,
228
+ sslKey: fileConfig?.sslKey ?? env.SSL_KEY_PATH ?? cliArgs.sslKey,
229
+ sslCert: fileConfig?.sslCert ?? env.SSL_CERT_PATH ?? cliArgs.sslCert,
230
+ llmApiKey: fileConfig?.llmApiKey ?? env.LLM_API_KEY,
231
+ llmApiUrl: fileConfig?.llmApiUrl ?? env.LLM_API_URL,
232
+ llmModel: fileConfig?.llmModel ?? env.LLM_MODEL,
233
+ llmMaxTokens: fileConfig?.llmMaxTokens ?? env.LLM_MAX_TOKENS,
234
+ llmTemperature: fileConfig?.llmTemperature ?? env.LLM_TEMPERATURE,
235
+ maxPoolSize: fileConfig?.maxPoolSize ?? 3,
236
+ workerPoolSize: fileConfig?.workerPoolSize ?? env.WORKER_POOL_SIZE,
237
+ lintPreset: fileConfig?.lintPreset ?? cliArgs.lintPreset ?? (env.LINT_PRESET || void 0),
238
+ lintConfigPath: fileConfig?.lintConfigPath ?? cliArgs.lintConfigPath ?? (env.LINT_CONFIG_PATH || void 0),
239
+ lintLevel: fileConfig?.lintLevel ?? cliArgs.lintLevel ?? (env.LINT_LEVEL || void 0),
240
+ noLint: fileConfig?.noLint ?? cliArgs.noLint ?? env.NO_LINT,
241
+ securityLevel: fileConfig?.securityLevel ?? cliArgs.securityLevel ?? (env.SECURITY_LEVEL || "strict"),
242
+ clientId: fileConfig?.clientId ?? (env.CLIENT_ID || "")
240
243
  };
241
244
  if (config.lintConfigPath) process.env.LINT_CONFIG_PATH = config.lintConfigPath;
242
245
  if (config.lintPreset) process.env.LINT_PRESET = config.lintPreset;
@@ -453,15 +456,89 @@ var init_metrics = __esm({
453
456
  }
454
457
  });
455
458
 
459
+ // src/cad-circuit-breaker.js
460
+ var CB_CLOSED, CB_OPEN, CB_HALF_OPEN, CB_FAILURE_THRESHOLD, CB_RESET_TIMEOUT, CircuitBreaker;
461
+ var init_cad_circuit_breaker = __esm({
462
+ "src/cad-circuit-breaker.js"() {
463
+ CB_CLOSED = 0;
464
+ CB_OPEN = 1;
465
+ CB_HALF_OPEN = 2;
466
+ CB_FAILURE_THRESHOLD = 5;
467
+ CB_RESET_TIMEOUT = 1e4;
468
+ CircuitBreaker = class {
469
+ constructor(connectionId, onOpen, onHalfOpen) {
470
+ this._connId = connectionId;
471
+ this._state = CB_CLOSED;
472
+ this._failureCount = 0;
473
+ this._lastFailureTime = 0;
474
+ this._timer = null;
475
+ this._onOpen = onOpen;
476
+ this._onHalfOpen = onHalfOpen;
477
+ }
478
+ get state() {
479
+ return this._state;
480
+ }
481
+ get failureCount() {
482
+ return this._failureCount;
483
+ }
484
+ allowRequest() {
485
+ if (this._state === CB_OPEN) {
486
+ const elapsed = Date.now() - this._lastFailureTime;
487
+ if (elapsed >= CB_RESET_TIMEOUT) {
488
+ this._state = CB_HALF_OPEN;
489
+ if (this._onHalfOpen) this._onHalfOpen(this._connId);
490
+ } else {
491
+ return false;
492
+ }
493
+ }
494
+ return true;
495
+ }
496
+ recordSuccess() {
497
+ this._state = CB_CLOSED;
498
+ this._failureCount = 0;
499
+ if (this._timer) {
500
+ clearTimeout(this._timer);
501
+ this._timer = null;
502
+ }
503
+ }
504
+ recordFailure() {
505
+ this._failureCount++;
506
+ this._lastFailureTime = Date.now();
507
+ if (this._failureCount >= CB_FAILURE_THRESHOLD && this._state !== CB_OPEN) {
508
+ this._state = CB_OPEN;
509
+ if (this._onOpen) this._onOpen(this._connId);
510
+ this._timer = setTimeout(() => {
511
+ this._state = CB_HALF_OPEN;
512
+ if (this._onHalfOpen) this._onHalfOpen(this._connId);
513
+ }, CB_RESET_TIMEOUT);
514
+ }
515
+ }
516
+ reset() {
517
+ this._state = CB_CLOSED;
518
+ this._failureCount = 0;
519
+ this._lastFailureTime = 0;
520
+ if (this._timer) {
521
+ clearTimeout(this._timer);
522
+ this._timer = null;
523
+ }
524
+ }
525
+ };
526
+ }
527
+ });
528
+
456
529
  // src/cad.js
457
530
  var cad_exports = {};
458
531
  __export(cad_exports, {
532
+ CommandQueue: () => CommandQueue,
533
+ ReadBatcher: () => ReadBatcher,
459
534
  _setInstanceResolver: () => _setInstanceResolver,
460
535
  cad: () => cad,
461
536
  createCadConnection: () => createCadConnection,
462
537
  default: () => cad_default,
463
538
  getActiveConnection: () => getActiveConnection,
539
+ isReadCommand: () => isReadCommand,
464
540
  onDocumentChanged: () => onDocumentChanged,
541
+ resolveCadConnection: () => resolveCadConnection,
465
542
  setActiveConnection: () => setActiveConnection
466
543
  });
467
544
  import { spawn } from "child_process";
@@ -506,22 +583,21 @@ function _resolveConn() {
506
583
  }
507
584
  return _activeConnection;
508
585
  }
509
- var __dirname, workerPath, MESSAGE_TIMEOUT, HEARTBEAT_INTERVAL, MAX_RESTART_ATTEMPTS, RESTART_BACKOFF_BASE, CB_CLOSED, CB_OPEN, CB_HALF_OPEN, CB_FAILURE_THRESHOLD, CB_RESET_TIMEOUT, MAX_BUFFER_SIZE, PRIORITY_HIGH, PRIORITY_NORMAL, MAX_QUEUE_SIZE, READ_PREFIXES, _docChangeCallbacks, ReadBatcher, CommandQueue, CadConnection, _activeConnection, _resolveInstanceConn, cad, cad_default;
586
+ function resolveCadConnection() {
587
+ return _resolveConn();
588
+ }
589
+ var __dirname, workerPath, MESSAGE_TIMEOUT, HEARTBEAT_INTERVAL, MAX_RESTART_ATTEMPTS, RESTART_BACKOFF_BASE, MAX_BUFFER_SIZE, PRIORITY_HIGH, PRIORITY_NORMAL, MAX_QUEUE_SIZE, READ_PREFIXES, _docChangeCallbacks, ReadBatcher, CommandQueue, CadConnection, _activeConnection, _resolveInstanceConn, cad, cad_default;
510
590
  var init_cad = __esm({
511
591
  "src/cad.js"() {
512
592
  init_config();
513
593
  init_metrics();
594
+ init_cad_circuit_breaker();
514
595
  __dirname = path2.dirname(fileURLToPath(import.meta.url));
515
596
  workerPath = path2.join(__dirname, "cad-worker.js");
516
597
  MESSAGE_TIMEOUT = config_default.messageTimeout;
517
598
  HEARTBEAT_INTERVAL = config_default.heartbeatInterval;
518
599
  MAX_RESTART_ATTEMPTS = 5;
519
600
  RESTART_BACKOFF_BASE = 1e3;
520
- CB_CLOSED = 0;
521
- CB_OPEN = 1;
522
- CB_HALF_OPEN = 2;
523
- CB_FAILURE_THRESHOLD = 5;
524
- CB_RESET_TIMEOUT = 1e4;
525
601
  MAX_BUFFER_SIZE = 1024 * 1024;
526
602
  PRIORITY_HIGH = 0;
527
603
  PRIORITY_NORMAL = 1;
@@ -708,10 +784,6 @@ var init_cad = __esm({
708
784
  _heartbeatTimer = null;
709
785
  _pendingRequests = /* @__PURE__ */ new Map();
710
786
  _commandQueue = null;
711
- _cbState = CB_CLOSED;
712
- _cbFailureCount = 0;
713
- _cbLastFailureTime = 0;
714
- _cbTimer = null;
715
787
  _restartCount = 0;
716
788
  _restartTimer = null;
717
789
  _activeDocName = null;
@@ -721,6 +793,11 @@ var init_cad = __esm({
721
793
  constructor() {
722
794
  this._connectionId = randomUUID().slice(0, 8);
723
795
  this._commandQueue = new CommandQueue(this);
796
+ this._cb = new CircuitBreaker(
797
+ this._connectionId,
798
+ (id) => console.error(`[${id}] Circuit breaker OPEN`),
799
+ (id) => console.error(`[${id}] Circuit breaker HALF_OPEN, allowing trial request`)
800
+ );
724
801
  }
725
802
  get _workerPoolSize() {
726
803
  return config_default.workerPoolSize || 2;
@@ -728,11 +805,15 @@ var init_cad = __esm({
728
805
  isAvailable() {
729
806
  return process.platform === "win32";
730
807
  }
731
- async connect(platform = null) {
808
+ async connect(platform = null, options = {}) {
732
809
  if (!this.isAvailable()) return false;
733
- this._cbSuccess();
810
+ this._cb.recordSuccess();
734
811
  try {
735
812
  const msg = platform ? { type: "connect", platform } : { type: "connect" };
813
+ if (options.moniker) {
814
+ msg.moniker = options.moniker;
815
+ msg.monikerKey = options.key || "moniker";
816
+ }
736
817
  const result = await this._sendMessage(msg);
737
818
  if (result && result.success) {
738
819
  this._version = result.version;
@@ -845,21 +926,8 @@ var init_cad = __esm({
845
926
  return `Platform: ${this._product}, Version: ${this._version}, Status: Connected`;
846
927
  }
847
928
  async disconnect() {
848
- this.connected = false;
849
- setCadConnected(false);
850
- this._version = null;
851
- this._product = null;
852
- this._platform = null;
853
- this._activeDocName = null;
854
- this._monitorStarted = false;
855
- this._commandQueue.clear();
856
- this._cbState = CB_CLOSED;
857
- this._cbFailureCount = 0;
858
- if (this._cbTimer) {
859
- clearTimeout(this._cbTimer);
860
- this._cbTimer = null;
861
- }
862
- this._resetWorker();
929
+ this._reset();
930
+ this._cb.reset();
863
931
  }
864
932
  async ping() {
865
933
  try {
@@ -878,12 +946,6 @@ var init_cad = __esm({
878
946
  this._activeDocName = null;
879
947
  this._monitorStarted = false;
880
948
  this._commandQueue.clear();
881
- this._cbState = CB_CLOSED;
882
- this._cbFailureCount = 0;
883
- if (this._cbTimer) {
884
- clearTimeout(this._cbTimer);
885
- this._cbTimer = null;
886
- }
887
949
  this._resetWorker();
888
950
  }
889
951
  _getQueueSize() {
@@ -892,44 +954,6 @@ var init_cad = __esm({
892
954
  _getRestartCount() {
893
955
  return this._restartCount;
894
956
  }
895
- // --- Circuit breaker ---
896
- _cbSuccess() {
897
- this._cbState = CB_CLOSED;
898
- this._cbFailureCount = 0;
899
- if (this._cbTimer) {
900
- clearTimeout(this._cbTimer);
901
- this._cbTimer = null;
902
- }
903
- }
904
- _cbFailure() {
905
- this._cbFailureCount++;
906
- this._cbLastFailureTime = Date.now();
907
- if (this._cbFailureCount >= CB_FAILURE_THRESHOLD && this._cbState !== CB_OPEN) {
908
- this._cbState = CB_OPEN;
909
- console.error(`[${this._connectionId}] Circuit breaker OPEN after ${this._cbFailureCount} failures`);
910
- this._cbTimer = setTimeout(() => {
911
- this._cbState = CB_HALF_OPEN;
912
- console.error(`[${this._connectionId}] Circuit breaker HALF_OPEN, allowing trial request`);
913
- }, CB_RESET_TIMEOUT);
914
- }
915
- }
916
- _cbAllowRequest() {
917
- if (this._cbState === CB_OPEN) {
918
- const elapsed = Date.now() - this._cbLastFailureTime;
919
- if (elapsed >= CB_RESET_TIMEOUT) {
920
- this._cbState = CB_HALF_OPEN;
921
- console.error(`[${this._connectionId}] Circuit breaker auto-transition to HALF_OPEN`);
922
- } else {
923
- return false;
924
- }
925
- }
926
- return true;
927
- }
928
- // --- Worker management ---
929
- _isReadOnlyMessage(msg) {
930
- if (msg.code) return isReadCommand(msg.code);
931
- return false;
932
- }
933
957
  _spawnWorker(type) {
934
958
  const w = spawn("node", [workerPath], { stdio: ["pipe", "pipe", "pipe"] });
935
959
  w.connected = true;
@@ -940,7 +964,7 @@ var init_cad = __esm({
940
964
  w.connected = false;
941
965
  if (type === "primary") {
942
966
  if (w !== this._primaryWorker) return;
943
- this._cbFailure();
967
+ this._cb.recordFailure();
944
968
  const exitError = new Error(`Primary worker exited with code ${code}`);
945
969
  for (const [id, entry] of this._pendingRequests) {
946
970
  clearTimeout(entry.timeout);
@@ -977,7 +1001,7 @@ var init_cad = __esm({
977
1001
  this._primaryWorker = null;
978
1002
  }
979
1003
  this._primaryWorker = this._spawnWorker("primary");
980
- this._cbSuccess();
1004
+ this._cb.recordSuccess();
981
1005
  if (!this._heartbeatTimer) this._startHeartbeat();
982
1006
  this._restartCount = 0;
983
1007
  }
@@ -1118,14 +1142,9 @@ var init_cad = __esm({
1118
1142
  }, delay);
1119
1143
  }
1120
1144
  async _sendMessage(msg) {
1121
- if (!this._cbAllowRequest()) {
1145
+ if (!this._cb.allowRequest()) {
1122
1146
  console.error(`[${this._connectionId}] Circuit breaker OPEN, auto-recovering...`);
1123
- this._cbState = CB_CLOSED;
1124
- this._cbFailureCount = 0;
1125
- if (this._cbTimer) {
1126
- clearTimeout(this._cbTimer);
1127
- this._cbTimer = null;
1128
- }
1147
+ this._cb.reset();
1129
1148
  await this._resetWorker();
1130
1149
  await this._getWorker(false);
1131
1150
  }
@@ -1141,17 +1160,17 @@ var init_cad = __esm({
1141
1160
  const p = self._pendingRequests.get(requestId);
1142
1161
  if (p) {
1143
1162
  self._pendingRequests.delete(requestId);
1144
- if (!noCircuitBreaker) self._cbFailure();
1163
+ if (!noCircuitBreaker) self._cb.recordFailure();
1145
1164
  reject(new Error("Timeout waiting for worker"));
1146
1165
  }
1147
1166
  }, MESSAGE_TIMEOUT);
1148
1167
  const entry = {
1149
1168
  resolve: (v) => {
1150
- if (!noCircuitBreaker && !entry._skipCb) self._cbSuccess();
1169
+ if (!noCircuitBreaker && !entry._skipCb) self._cb.recordSuccess();
1151
1170
  resolve(v);
1152
1171
  },
1153
1172
  reject: (e) => {
1154
- if (!noCircuitBreaker && !entry._skipCb) self._cbFailure();
1173
+ if (!noCircuitBreaker && !entry._skipCb) self._cb.recordFailure();
1155
1174
  reject(e);
1156
1175
  },
1157
1176
  timeout
@@ -1162,11 +1181,15 @@ var init_cad = __esm({
1162
1181
  } catch (e) {
1163
1182
  clearTimeout(timeout);
1164
1183
  self._pendingRequests.delete(requestId);
1165
- if (!noCircuitBreaker) self._cbFailure();
1184
+ if (!noCircuitBreaker) self._cb.recordFailure();
1166
1185
  reject(new Error(`Failed to write to worker: ${e.message}`));
1167
1186
  }
1168
1187
  });
1169
1188
  }
1189
+ _isReadOnlyMessage(msg) {
1190
+ if (msg.code) return isReadCommand(msg.code);
1191
+ return false;
1192
+ }
1170
1193
  };
1171
1194
  _activeConnection = new CadConnection();
1172
1195
  _resolveInstanceConn = null;
@@ -1175,28 +1198,28 @@ var init_cad = __esm({
1175
1198
  return _resolveConn().connected;
1176
1199
  },
1177
1200
  set connected(v) {
1178
- _activeConnection.connected = v;
1201
+ _resolveConn().connected = v;
1179
1202
  },
1180
1203
  get version() {
1181
- return _activeConnection._version;
1204
+ return _resolveConn()._version;
1182
1205
  },
1183
1206
  set version(v) {
1184
- _activeConnection._version = v;
1207
+ _resolveConn()._version = v;
1185
1208
  },
1186
1209
  get product() {
1187
- return _activeConnection._product;
1210
+ return _resolveConn()._product;
1188
1211
  },
1189
1212
  set product(v) {
1190
- _activeConnection._product = v;
1213
+ _resolveConn()._product = v;
1191
1214
  },
1192
1215
  get _platform() {
1193
- return _activeConnection._platform;
1216
+ return _resolveConn()._platform;
1194
1217
  },
1195
1218
  set _platform(v) {
1196
- _activeConnection._platform = v;
1219
+ _resolveConn()._platform = v;
1197
1220
  },
1198
1221
  async connect(platform) {
1199
- return _activeConnection.connect(platform);
1222
+ return _resolveConn().connect(platform);
1200
1223
  },
1201
1224
  async sendCommand(code) {
1202
1225
  return _resolveConn().sendCommand(code);
@@ -1205,49 +1228,49 @@ var init_cad = __esm({
1205
1228
  return _resolveConn().sendCommandWithResult(code, encoding);
1206
1229
  },
1207
1230
  getVersion() {
1208
- return _activeConnection.getVersion();
1231
+ return _resolveConn().getVersion();
1209
1232
  },
1210
1233
  getPlatform() {
1211
- return _activeConnection.getPlatform();
1234
+ return _resolveConn().getPlatform();
1212
1235
  },
1213
1236
  getConnectionId() {
1214
- return _activeConnection.getConnectionId();
1237
+ return _resolveConn().getConnectionId();
1215
1238
  },
1216
1239
  isAvailable() {
1217
- return _activeConnection.isAvailable();
1240
+ return _resolveConn().isAvailable();
1218
1241
  },
1219
1242
  async isBusy() {
1220
- return _activeConnection.isBusy();
1243
+ return _resolveConn().isBusy();
1221
1244
  },
1222
1245
  async hasDoc() {
1223
- return _activeConnection.hasDoc();
1246
+ return _resolveConn().hasDoc();
1224
1247
  },
1225
1248
  async newDoc() {
1226
- return _activeConnection.newDoc();
1249
+ return _resolveConn().newDoc();
1227
1250
  },
1228
1251
  async bringToFront() {
1229
- return _activeConnection.bringToFront();
1252
+ return _resolveConn().bringToFront();
1230
1253
  },
1231
1254
  async getActiveDocInfo() {
1232
- return _activeConnection.getActiveDocInfo();
1255
+ return _resolveConn().getActiveDocInfo();
1233
1256
  },
1234
1257
  async getInfo() {
1235
- return _activeConnection.getInfo();
1258
+ return _resolveConn().getInfo();
1236
1259
  },
1237
1260
  async disconnect() {
1238
- return _activeConnection.disconnect();
1261
+ return _resolveConn().disconnect();
1239
1262
  },
1240
1263
  async ping() {
1241
- return _activeConnection.ping();
1264
+ return _resolveConn().ping();
1242
1265
  },
1243
1266
  _reset() {
1244
- return _activeConnection._reset();
1267
+ return _resolveConn()._reset();
1245
1268
  },
1246
1269
  _getQueueSize() {
1247
- return _activeConnection._getQueueSize();
1270
+ return _resolveConn()._getQueueSize();
1248
1271
  },
1249
1272
  _getRestartCount() {
1250
- return _activeConnection._getRestartCount();
1273
+ return _resolveConn()._getRestartCount();
1251
1274
  }
1252
1275
  };
1253
1276
  cad_default = CadConnection;
@@ -1258,7 +1281,7 @@ var init_cad = __esm({
1258
1281
  import path3 from "path";
1259
1282
  import { fileURLToPath as fileURLToPath2 } from "url";
1260
1283
  import { createRequire } from "module";
1261
- var CAD_PLATFORMS, FILE_EXTENSIONS, __dirname2, require2, version, SERVER_NAME, SERVER_VERSION, SERVER_CAPABILITIES, MOCK_PACKAGES;
1284
+ var CAD_PLATFORMS, FILE_EXTENSIONS, __dirname2, require2, version, SERVER_NAME, SERVER_VERSION, SERVER_CAPABILITIES, MOCK_PACKAGES, CAD_RESOURCE_URIS;
1262
1285
  var init_constants = __esm({
1263
1286
  "src/constants.js"() {
1264
1287
  CAD_PLATFORMS = ["AutoCAD", "ZWCAD", "GStarCAD", "BricsCAD"];
@@ -1281,6 +1304,17 @@ var init_constants = __esm({
1281
1304
  completions: {}
1282
1305
  };
1283
1306
  MOCK_PACKAGES = [];
1307
+ CAD_RESOURCE_URIS = [
1308
+ "atlisp://cad/info",
1309
+ "atlisp://dwg/name",
1310
+ "atlisp://dwg/path",
1311
+ "atlisp://dwg/entities",
1312
+ "atlisp://dwg/tbl",
1313
+ "atlisp://dwg/block-refs",
1314
+ "atlisp://dwg/text-content",
1315
+ "atlisp://cad/dwgs",
1316
+ "atlisp://packages"
1317
+ ];
1284
1318
  }
1285
1319
  });
1286
1320
 
@@ -1481,7 +1515,10 @@ var init_resource_defs = __esm({
1481
1515
  { uri: "atlisp://standards/drafting", name: "Drafting Standards", description: "CAD \u5236\u56FE\u89C4\u8303", mimeType: "application/json", subscribable: false },
1482
1516
  { uri: "atlisp://standards/coding", name: "Coding Standards", description: "@lisp \u7F16\u7801\u89C4\u8303", mimeType: "application/json", subscribable: false },
1483
1517
  { uri: "atlisp://dwg/context", name: "DWG Context", description: "\u5F53\u524D\u56FE\u7EB8\u4E0A\u4E0B\u6587\u6458\u8981\u3002\u8FD4\u56DE\u6587\u6863\u540D/\u8DEF\u5F84/\u5E03\u5C40/UCS/\u56FE\u5C42\u5217\u8868/\u5B9E\u4F53\u7EDF\u8BA1/\u7CFB\u7EDF\u53D8\u91CF\u7EC4\u5408\u4FE1\u606F\uFF0C\u5E2E\u52A9\u5FEB\u901F\u4E86\u89E3\u5F53\u524D CAD \u72B6\u6001", mimeType: "application/json", subscribable: true },
1484
- { uri: "atlisp://tools/relationships", name: "Tool Relationships", description: "\u5DE5\u5177\u4F9D\u8D56\u5173\u7CFB\u56FE\u3002\u63CF\u8FF0\u5DE5\u5177\u95F4\u7684\u8C03\u7528\u94FE\u5173\u7CFB\uFF08\u5982 select_entities \u2192 batch_delete\uFF09\uFF0C\u5E2E\u52A9\u7406\u89E3\u5DE5\u5177\u7EC4\u5408\u4F7F\u7528\u65B9\u5F0F", mimeType: "application/json", subscribable: false }
1518
+ { uri: "atlisp://tools/relationships", name: "Tool Relationships", description: "\u5DE5\u5177\u4F9D\u8D56\u5173\u7CFB\u56FE\u3002\u63CF\u8FF0\u5DE5\u5177\u95F4\u7684\u8C03\u7528\u94FE\u5173\u7CFB\uFF08\u5982 select_entities \u2192 batch_delete\uFF09\uFF0C\u5E2E\u52A9\u7406\u89E3\u5DE5\u5177\u7EC4\u5408\u4F7F\u7528\u65B9\u5F0F", mimeType: "application/json", subscribable: false },
1519
+ { uri: "atlisp://dwg/analysis", name: "Drawing Analysis", description: "\u5F53\u524D\u56FE\u7EB8\u7EFC\u5408\u5206\u6790\uFF1A\u5B9E\u4F53\u7C7B\u578B\u7EDF\u8BA1\u3001\u5757\u4F7F\u7528\u7EDF\u8BA1\u3001\u56FE\u5C42\u6458\u8981\u3001\u6587\u5B57\u7EDF\u8BA1\u3001\u7CFB\u7EDF\u53D8\u91CF\u5FEB\u7167\u3002\u652F\u6301 ?type=summary\uFF08\u9ED8\u8BA4\uFF09\u6216 ?type=detailed", mimeType: "application/json", subscribable: true },
1520
+ { uri: "atlisp://tools/categories", name: "Tool Categories", description: "\u5DE5\u5177\u5206\u7C7B\u6982\u89C8\u3002\u5217\u51FA\u6240\u6709\u5206\u7C7B\u53CA\u5176\u5305\u542B\u7684\u5DE5\u5177\u6570\u91CF", mimeType: "application/json", subscribable: false },
1521
+ { uri: "atlisp://dwg/standards/check", name: "Drawing Standards Check", description: "\u68C0\u67E5\u5F53\u524D\u56FE\u7EB8\u662F\u5426\u7B26\u5408\u52A0\u8F7D\u7684\u5236\u56FE\u89C4\u8303\uFF08\u56FE\u5C42\u547D\u540D\u3001\u989C\u8272\u3001\u7EBF\u578B\u7B49\u89C4\u5219\uFF09", mimeType: "application/json", subscribable: false }
1485
1522
  ];
1486
1523
  TBL_QUERIES = {
1487
1524
  layer: { name: "layer", lisp: `(progn
@@ -1528,49 +1565,70 @@ var init_resource_defs = __esm({
1528
1565
  });
1529
1566
 
1530
1567
  // src/handlers/resource-cache.js
1568
+ function _getCache() {
1569
+ const key = _instanceKey || _defaultKey;
1570
+ if (!_instanceCaches.has(key)) {
1571
+ _instanceCaches.set(key, /* @__PURE__ */ new Map());
1572
+ }
1573
+ return _instanceCaches.get(key);
1574
+ }
1575
+ function _ttl() {
1576
+ return config_default.resourceCacheTtl;
1577
+ }
1531
1578
  function ensureCleanup() {
1532
1579
  if (cleanupTimer) return;
1533
1580
  cleanupTimer = setInterval(() => {
1534
1581
  const now = Date.now();
1535
- for (const [key, entry] of resourceCache) {
1536
- if (now - entry.timestamp >= CACHE_TTL) {
1537
- resourceCache.delete(key);
1582
+ const ttl = _ttl();
1583
+ for (const cache of _instanceCaches.values()) {
1584
+ for (const [key, entry] of cache) {
1585
+ if (now - entry.timestamp >= ttl) {
1586
+ cache.delete(key);
1587
+ }
1538
1588
  }
1539
1589
  }
1540
1590
  }, CLEANUP_INTERVAL);
1541
1591
  cleanupTimer.unref();
1542
1592
  }
1593
+ function setInstanceKey(key) {
1594
+ _instanceKey = key || null;
1595
+ }
1543
1596
  function getCached(key) {
1544
- const entry = resourceCache.get(key);
1545
- if (entry && Date.now() - entry.timestamp < CACHE_TTL) {
1597
+ const cache = _getCache();
1598
+ const entry = cache.get(key);
1599
+ if (entry && Date.now() - entry.timestamp < _ttl()) {
1546
1600
  return entry.data;
1547
1601
  }
1548
- resourceCache.delete(key);
1602
+ cache.delete(key);
1549
1603
  return null;
1550
1604
  }
1551
1605
  function setCache(key, data) {
1552
- resourceCache.set(key, { data, timestamp: Date.now() });
1606
+ _getCache().set(key, { data, timestamp: Date.now() });
1553
1607
  }
1554
1608
  function clearCache(uri) {
1555
- const prefix = URI_CACHE_PREFIX[uri];
1556
- if (prefix) {
1557
- for (const key of resourceCache.keys()) {
1558
- if (key === prefix || key.startsWith(prefix + ":")) {
1559
- resourceCache.delete(key);
1609
+ const basePrefix = URI_CACHE_PREFIX[uri];
1610
+ if (basePrefix) {
1611
+ const cache = _getCache();
1612
+ for (const key of cache.keys()) {
1613
+ if (key === basePrefix || key.startsWith(basePrefix + ":")) {
1614
+ cache.delete(key);
1560
1615
  }
1561
1616
  }
1562
1617
  } else if (!uri) {
1563
- resourceCache.clear();
1618
+ const key = _instanceKey || _defaultKey;
1619
+ _instanceCaches.delete(key);
1620
+ _instanceCaches.set(key, /* @__PURE__ */ new Map());
1564
1621
  }
1565
1622
  }
1566
- var CACHE_TTL, resourceCache, CLEANUP_INTERVAL, cleanupTimer, URI_CACHE_PREFIX;
1623
+ var CLEANUP_INTERVAL, cleanupTimer, _instanceKey, _instanceCaches, _defaultKey, URI_CACHE_PREFIX;
1567
1624
  var init_resource_cache = __esm({
1568
1625
  "src/handlers/resource-cache.js"() {
1569
1626
  init_config();
1570
- CACHE_TTL = config_default.resourceCacheTtl;
1571
- resourceCache = /* @__PURE__ */ new Map();
1572
1627
  CLEANUP_INTERVAL = 6e4;
1573
1628
  cleanupTimer = null;
1629
+ _instanceKey = null;
1630
+ _instanceCaches = /* @__PURE__ */ new Map();
1631
+ _defaultKey = "__default__";
1574
1632
  ensureCleanup();
1575
1633
  URI_CACHE_PREFIX = {
1576
1634
  "atlisp://cad/info": "cad:info",
@@ -1591,6 +1649,23 @@ function createError(code, details = {}) {
1591
1649
  const def = ERROR_DEFS[code] || ERROR_DEFS[ERROR_CODES.INTERNAL_ERROR];
1592
1650
  return new MCPError(code, def.message, details);
1593
1651
  }
1652
+ function toMcpError(error2) {
1653
+ if (error2 instanceof MCPError) return error2;
1654
+ if (error2 instanceof Error) {
1655
+ return new MCPError(ERROR_CODES.INTERNAL_ERROR, error2.message, { stack: error2.stack });
1656
+ }
1657
+ return new MCPError(ERROR_CODES.INTERNAL_ERROR, String(error2));
1658
+ }
1659
+ function mcpErrorResponse(error2) {
1660
+ const mcpErr = toMcpError(error2);
1661
+ const json = mcpErr.toJSON();
1662
+ return {
1663
+ content: [{ type: "text", text: `[${json.code}] ${json.message}
1664
+ \u63D0\u793A: ${json.hint}
1665
+ \u5EFA\u8BAE\u5DE5\u5177: ${json.suggestedTools.join(", ") || "\u65E0"}` }],
1666
+ isError: true
1667
+ };
1668
+ }
1594
1669
  function mcpSuccessResponse(text) {
1595
1670
  return { content: [{ type: "text", text: String(text) }] };
1596
1671
  }
@@ -1604,9 +1679,14 @@ var init_errors = __esm({
1604
1679
  LISP_PAREN_MISMATCH: "LISP_PAREN_MISMATCH",
1605
1680
  TIMEOUT: "TIMEOUT",
1606
1681
  INVALID_PARAMS: "INVALID_PARAMS",
1682
+ INVALID_ARGS: "INVALID_ARGS",
1607
1683
  METHOD_NOT_FOUND: "METHOD_NOT_FOUND",
1684
+ HANDLER_NOT_FOUND: "HANDLER_NOT_FOUND",
1608
1685
  RESOURCE_NOT_FOUND: "RESOURCE_NOT_FOUND",
1609
1686
  PACKAGE_NOT_FOUND: "PACKAGE_NOT_FOUND",
1687
+ OPERATION_FAILED: "OPERATION_FAILED",
1688
+ RATE_LIMITED: "RATE_LIMITED",
1689
+ SESSION_NOT_FOUND: "SESSION_NOT_FOUND",
1610
1690
  INTERNAL_ERROR: "INTERNAL_ERROR"
1611
1691
  };
1612
1692
  ERROR_DEFS = {
@@ -1640,6 +1720,31 @@ var init_errors = __esm({
1640
1720
  hint: "\u8BF7\u68C0\u67E5\u5DE5\u5177\u53C2\u6570\u683C\u5F0F\u662F\u5426\u6B63\u786E\u3002\u4F7F\u7528 search_tools \u6216 list_tools_by_category \u67E5\u770B\u5DE5\u5177\u53C2\u6570\u8981\u6C42",
1641
1721
  tools: ["search_tools", "list_tools_by_category"]
1642
1722
  },
1723
+ [ERROR_CODES.INVALID_ARGS]: {
1724
+ message: "\u53C2\u6570\u6821\u9A8C\u5931\u8D25",
1725
+ hint: "\u5DE5\u5177\u6240\u9700\u53C2\u6570\u7F3A\u5931\u6216\u683C\u5F0F\u9519\u8BEF\u3002\u8BF7\u68C0\u67E5\u53C2\u6570\u7C7B\u578B\u548C\u5FC5\u586B\u5B57\u6BB5\uFF0C\u4F7F\u7528 search_tools \u67E5\u770B\u5DE5\u5177\u53C2\u6570\u8981\u6C42",
1726
+ tools: ["search_tools", "list_tools_by_category"]
1727
+ },
1728
+ [ERROR_CODES.HANDLER_NOT_FOUND]: {
1729
+ message: "\u5DE5\u5177\u672A\u627E\u5230",
1730
+ hint: "\u5DE5\u5177\u540D\u79F0\u4E0D\u5B58\u5728\u3002\u4F7F\u7528 search_tools \u641C\u7D22\u53EF\u7528\u5DE5\u5177\uFF0C\u6216 list_tools_by_category \u6D4F\u89C8\u5168\u90E8\u5206\u7C7B",
1731
+ tools: ["search_tools", "list_tools_by_category"]
1732
+ },
1733
+ [ERROR_CODES.OPERATION_FAILED]: {
1734
+ message: "\u64CD\u4F5C\u6267\u884C\u5931\u8D25",
1735
+ hint: "\u5DE5\u5177\u6267\u884C\u8FC7\u7A0B\u4E2D\u53D1\u751F\u9519\u8BEF\u3002\u53EF\u91CD\u8BD5\u64CD\u4F5C\uFF0C\u6216\u68C0\u67E5\u65E5\u5FD7\u83B7\u53D6\u8BE6\u7EC6\u4FE1\u606F",
1736
+ tools: ["get_system_status", "get_cad_info"]
1737
+ },
1738
+ [ERROR_CODES.RATE_LIMITED]: {
1739
+ message: "\u8C03\u7528\u9891\u7387\u8D85\u9650",
1740
+ hint: "\u5DE5\u5177\u8C03\u7528\u8FC7\u4E8E\u9891\u7E41\u3002\u8BF7\u7B49\u5F85\u4E00\u6BB5\u65F6\u95F4\u540E\u518D\u8BD5",
1741
+ tools: []
1742
+ },
1743
+ [ERROR_CODES.SESSION_NOT_FOUND]: {
1744
+ message: "\u4F1A\u8BDD\u672A\u627E\u5230",
1745
+ hint: "\u6307\u5B9A ID \u7684\u4F1A\u8BDD\u4E0D\u5B58\u5728\u3002\u8BF7\u68C0\u67E5 sessionId \u53C2\u6570\u662F\u5426\u6B63\u786E",
1746
+ tools: ["lisp_repl_list"]
1747
+ },
1643
1748
  [ERROR_CODES.METHOD_NOT_FOUND]: {
1644
1749
  message: "\u65B9\u6CD5\u4E0D\u5B58\u5728",
1645
1750
  hint: "\u5DE5\u5177\u540D\u79F0\u8F93\u5165\u9519\u8BEF\u3002\u4F7F\u7528 search_tools \u641C\u7D22\u53EF\u7528\u5DE5\u5177\uFF0C\u6216 list_tools_by_category \u6D4F\u89C8\u5168\u90E8\u5206\u7C7B",
@@ -1784,8 +1889,8 @@ __export(renderer_exports, {
1784
1889
  renderPrompt: () => renderPrompt,
1785
1890
  renderTemplate: () => renderTemplate
1786
1891
  });
1787
- function resolvePath(obj, path20) {
1788
- const parts = path20.split(/[.\[\]]/g).filter(Boolean);
1892
+ function resolvePath(obj, path21) {
1893
+ const parts = path21.split(/[.\[\]]/g).filter(Boolean);
1789
1894
  let val = obj;
1790
1895
  for (const p of parts) {
1791
1896
  if (val == null) return void 0;
@@ -1795,8 +1900,8 @@ function resolvePath(obj, path20) {
1795
1900
  }
1796
1901
  function renderTemplate(template, args) {
1797
1902
  return template.replace(PARAM_RE, (match, rawExpr, expr) => {
1798
- const path20 = rawExpr || expr;
1799
- const val = resolvePath(args, path20);
1903
+ const path21 = rawExpr || expr;
1904
+ const val = resolvePath(args, path21);
1800
1905
  if (val == null) return "";
1801
1906
  const strVal = String(val);
1802
1907
  if (rawExpr) return strVal;
@@ -2228,8 +2333,8 @@ var init_sse_session = __esm({
2228
2333
  sendMessage(data) {
2229
2334
  return this.sendEvent(SSE_EVENTS.MESSAGE, data);
2230
2335
  }
2231
- sendEndpoint(path20) {
2232
- return this.sendEvent(SSE_EVENTS.ENDPOINT, path20);
2336
+ sendEndpoint(path21) {
2337
+ return this.sendEvent(SSE_EVENTS.ENDPOINT, path21);
2233
2338
  }
2234
2339
  sendError(code, message, id = null) {
2235
2340
  return this.sendEvent(SSE_EVENTS.ERROR, { code, message }, id);
@@ -2951,7 +3056,8 @@ var init_info_tools = __esm({
2951
3056
  }
2952
3057
  },
2953
3058
  { name: "search_tools", description: "\u6309\u5173\u952E\u8BCD\u641C\u7D22\u53EF\u7528\u5DE5\u5177", inputSchema: { type: "object", properties: { query: { type: "string", description: "\u641C\u7D22\u5173\u952E\u8BCD(\u5339\u914D\u540D\u79F0\u548C\u63CF\u8FF0)" } }, required: ["query"] } },
2954
- { name: "list_tools_by_category", description: "\u6309\u5206\u7C7B\u5217\u51FA\u5DE5\u5177", inputSchema: { type: "object", properties: { category: { type: "string", description: "\u5206\u7C7B\u540D\u79F0(\u53EF\u9009,\u4E0D\u4F20\u5219\u5217\u51FA\u6240\u6709\u5206\u7C7B)" } } } }
3059
+ { name: "list_tools_by_category", description: "\u6309\u5206\u7C7B\u5217\u51FA\u5DE5\u5177", inputSchema: { type: "object", properties: { category: { type: "string", description: "\u5206\u7C7B\u540D\u79F0(\u53EF\u9009,\u4E0D\u4F20\u5219\u5217\u51FA\u6240\u6709\u5206\u7C7B)" } } } },
3060
+ { name: "get_tool_info", description: "\u83B7\u53D6\u5DE5\u5177\u7684\u8BE6\u7EC6\u4FE1\u606F\uFF08\u53C2\u6570\u3001\u5206\u7C7B\u3001\u53EA\u8BFB/\u7834\u574F\u6027\u6807\u8BB0\u7B49\uFF09", inputSchema: { type: "object", properties: { name: { type: "string", description: "\u5DE5\u5177\u540D\u79F0" } }, required: ["name"] } }
2955
3061
  ];
2956
3062
  }
2957
3063
  });
@@ -5492,12 +5598,12 @@ var init_pool_tools = __esm({
5492
5598
  inputSchema: {
5493
5599
  type: "object",
5494
5600
  properties: {
5495
- key: { type: "string", description: "\u5B9E\u4F8B\u6807\u8BC6\u952E\u540D\uFF08\u5982 autocad, zwcad, mycad\uFF09" },
5601
+ key: { type: "string", description: "\u5B9E\u4F8B\u6807\u8BC6\u952E\u540D\uFF08\u5982 autocad, zwcad, mycad\uFF1B\u4E0D\u4F20\u65F6\u7531 pid \u81EA\u52A8\u6D3E\u751F\uFF09" },
5496
5602
  platform: { type: "string", description: "CAD \u5E73\u53F0: AutoCAD, ZWCAD, GStarCAD, BricsCAD\uFF08\u53EF\u9009\uFF0C\u81EA\u52A8\u68C0\u6D4B\uFF09" },
5603
+ pid: { type: "number", description: "\u76EE\u6807\u8FDB\u7A0B PID\uFF08\u53EF\u9009\uFF0C\u901A\u8FC7 ROT \u67E5\u627E\u540E\u8FDE\u63A5\uFF0C\u53EF\u4E0E pid \u81EA\u52A8\u6D3E\u751F key\uFF09" },
5497
5604
  label: { type: "string", description: "\u5B9E\u4F8B\u663E\u793A\u6807\u7B7E\uFF08\u53EF\u9009\uFF09" },
5498
5605
  makeActive: { type: "boolean", description: "\u8FDE\u63A5\u540E\u8BBE\u4E3A\u6D3B\u52A8\u5B9E\u4F8B\uFF08\u9ED8\u8BA4 true\uFF09" }
5499
- },
5500
- required: ["key"]
5606
+ }
5501
5607
  }
5502
5608
  },
5503
5609
  {
@@ -6374,7 +6480,149 @@ var init_lint_tools = __esm({
6374
6480
  }
6375
6481
  });
6376
6482
 
6483
+ // src/tools/debug-tools.js
6484
+ var tools41;
6485
+ var init_debug_tools = __esm({
6486
+ "src/tools/debug-tools.js"() {
6487
+ tools41 = [
6488
+ {
6489
+ name: "debug_start",
6490
+ description: "\u542F\u52A8\u4EA4\u4E92\u5F0F\u8C03\u8BD5\u4F1A\u8BDD\u3002\u5C06\u4EE3\u7801\u6309\u5757\u62C6\u5206\u4E3A\u65AD\u70B9\u7EA7\u6267\u884C\uFF0C\u52A0\u8F7D\u8C03\u8BD5\u8F85\u52A9\u51FD\u6570\u5230 CAD\uFF0C\u8DF3\u8FC7\u975E\u65AD\u70B9\u524D\u7F6E\u5757",
6491
+ inputSchema: {
6492
+ type: "object",
6493
+ properties: {
6494
+ code: { type: "string", description: "\u539F\u59CB AutoLISP \u4EE3\u7801" },
6495
+ chunks: {
6496
+ type: "array",
6497
+ description: "\u7531\u6269\u5C55\u7AEF\u62C6\u5206\u597D\u7684\u4EE3\u7801\u5757\u5217\u8868",
6498
+ items: {
6499
+ type: "object",
6500
+ properties: {
6501
+ id: { type: "integer", description: "\u5757\u5E8F\u53F7 (1-based)" },
6502
+ line: { type: "integer", description: "\u6E90\u6587\u4EF6\u884C\u53F7" },
6503
+ code: { type: "string", description: "\u5757\u7684 AutoLISP \u4EE3\u7801" },
6504
+ vars: { type: "array", items: { type: "string" }, description: "\u8BE5\u5757\u9700\u6355\u83B7\u7684\u53D8\u91CF\u540D" },
6505
+ isBreakpoint: { type: "boolean", description: "\u8BE5\u884C\u6709\u65AD\u70B9" },
6506
+ isDefunStart: { type: "boolean", description: "\u662F defun \u5B9A\u4E49\u5F00\u5934" }
6507
+ },
6508
+ required: ["id", "line", "code", "vars", "isBreakpoint"]
6509
+ }
6510
+ },
6511
+ encoding: { type: "string", description: "\u7F16\u7801\uFF08\u9ED8\u8BA4 auto\uFF09" }
6512
+ },
6513
+ required: ["code", "chunks"]
6514
+ }
6515
+ },
6516
+ {
6517
+ name: "debug_step",
6518
+ description: "\u6267\u884C\u4E00\u6B65\uFF08step\uFF09\u6216\u7EE7\u7EED\u5230\u4E0B\u4E00\u65AD\u70B9\uFF08continue\uFF09",
6519
+ inputSchema: {
6520
+ type: "object",
6521
+ properties: {
6522
+ sessionId: { type: "string", description: "\u8C03\u8BD5\u4F1A\u8BDD ID" },
6523
+ action: { type: "string", enum: ["step", "continue"], description: "step=\u6267\u884C\u4E00\u4E2A\u5757, continue=\u5230\u4E0B\u4E00\u65AD\u70B9/\u7ED3\u675F" }
6524
+ },
6525
+ required: ["sessionId", "action"]
6526
+ }
6527
+ },
6528
+ {
6529
+ name: "debug_evaluate",
6530
+ description: "\u8C03\u8BD5\u6682\u505C\u65F6\u6C42\u503C AutoLISP \u8868\u8FBE\u5F0F",
6531
+ inputSchema: {
6532
+ type: "object",
6533
+ properties: {
6534
+ sessionId: { type: "string", description: "\u8C03\u8BD5\u4F1A\u8BDD ID" },
6535
+ expression: { type: "string", description: "\u4EFB\u610F AutoLISP \u8868\u8FBE\u5F0F" }
6536
+ },
6537
+ required: ["sessionId", "expression"]
6538
+ }
6539
+ },
6540
+ {
6541
+ name: "debug_get_state",
6542
+ description: "\u67E5\u8BE2\u8C03\u8BD5\u4F1A\u8BDD\u5F53\u524D\u72B6\u6001",
6543
+ inputSchema: {
6544
+ type: "object",
6545
+ properties: {
6546
+ sessionId: { type: "string", description: "\u8C03\u8BD5\u4F1A\u8BDD ID" }
6547
+ },
6548
+ required: ["sessionId"]
6549
+ }
6550
+ },
6551
+ {
6552
+ name: "debug_stop",
6553
+ description: "\u505C\u6B62\u8C03\u8BD5\u4F1A\u8BDD\uFF0C\u6E05\u7406 CAD \u5168\u5C40\u53D8\u91CF\u548C\u8F85\u52A9\u51FD\u6570",
6554
+ inputSchema: {
6555
+ type: "object",
6556
+ properties: {
6557
+ sessionId: { type: "string", description: "\u8C03\u8BD5\u4F1A\u8BDD ID" }
6558
+ },
6559
+ required: ["sessionId"]
6560
+ }
6561
+ }
6562
+ ];
6563
+ }
6564
+ });
6565
+
6566
+ // src/tools/concurrent-tools.js
6567
+ var tools42;
6568
+ var init_concurrent_tools = __esm({
6569
+ "src/tools/concurrent-tools.js"() {
6570
+ tools42 = [
6571
+ {
6572
+ name: "batch_concurrent_tasks",
6573
+ description: "\u5E76\u53D1\u6267\u884C\u591A\u5B9E\u4F8B\u5DE5\u5177 \u2014 \u5C06\u591A\u4E2A\u5DE5\u5177\u8C03\u7528\u540C\u65F6\u8DEF\u7531\u5230\u4E0D\u540C CAD \u5B9E\u4F8B\u5E76\u884C\u6267\u884C\u3002\u5185\u90E8\u4F7F\u7528 Promise.allSettled\uFF0C\u4E00\u4E2A\u5931\u8D25\u4E0D\u5F71\u54CD\u5176\u4ED6\u3002",
6574
+ inputSchema: {
6575
+ type: "object",
6576
+ properties: {
6577
+ tasks: {
6578
+ type: "array",
6579
+ items: {
6580
+ type: "object",
6581
+ properties: {
6582
+ tool: { type: "string", description: "\u5DE5\u5177\u540D\u79F0" },
6583
+ args: { type: "object", description: "\u5DE5\u5177\u53C2\u6570\uFF08\u4E0D\u542B instance\uFF09" },
6584
+ instance: { type: "string", description: "\u76EE\u6807\u5B9E\u4F8B key" }
6585
+ },
6586
+ required: ["tool", "instance"]
6587
+ },
6588
+ description: "\u8981\u5E76\u53D1\u6267\u884C\u7684\u4EFB\u52A1\u5217\u8868"
6589
+ }
6590
+ },
6591
+ required: ["tasks"]
6592
+ }
6593
+ }
6594
+ ];
6595
+ }
6596
+ });
6597
+
6377
6598
  // src/tools/index.js
6599
+ function getToolCategory(name) {
6600
+ const n = name;
6601
+ if (/^(draw_|create_3d_|create_box|create_cone|create_cylinder|create_sphere|create_torus|create_wedge|extrude_|revolve_|boolean_|slice_solid|thicken_|offset_surface|create_loft_|create_region)/.test(n)) return "\u7ED8\u56FE\u4E0E3D";
6602
+ if (/^(move_|copy_|rotate_|scale_|mirror_|offset_|array_|batch_)/.test(n)) return "\u4FEE\u6539";
6603
+ if (/^(trim_|extend_|fillet|chamfer|break_|join_|stretch)/.test(n)) return "\u7F16\u8F91";
6604
+ if (/^(create_layer|delete_layer|set_layer|layer_)/.test(n)) return "\u56FE\u5C42";
6605
+ if (/^(create_block|insert_block|explode_block|get_block|set_block|batch_get_block|batch_set_block)/.test(n)) return "\u5757";
6606
+ if (/^(create_dim|list_dim|set_dim|create_hatch|set_hatch)/.test(n)) return "\u6807\u6CE8\u4E0E\u586B\u5145";
6607
+ if (/^(create_ucs|set_ucs|delete_ucs|list_ucs|get_current_ucs|create_layout|delete_layout|rename_layout|set_layout|list_layouts|get_current_layout)/.test(n)) return "\u5750\u6807\u7CFB\u4E0E\u5E03\u5C40";
6608
+ if (/^(create_viewport|set_viewport|lock_viewport|unlock_viewport|maximize_viewport)/.test(n)) return "\u89C6\u53E3";
6609
+ if (/^find_/.test(n)) return "\u7A7A\u95F4\u67E5\u8BE2";
6610
+ if (/^(select_|get_entity|set_entity|delete_entity|get_bounding_box)/.test(n)) return "\u9009\u62E9\u4E0E\u5C5E\u6027";
6611
+ if (/^(open_dwg|save_dwg|export_|import_|close_dwg|publish_)/.test(n)) return "\u6587\u4EF6";
6612
+ if (/^(plot_|save_pdf|batch_plot|publish_layouts)/.test(n)) return "\u6253\u5370";
6613
+ if (/^(get_xdata|set_xdata|delete_xdata|list_xdata|copxy_xdata|search_by_xdata|export_xdata|import_xdata)/.test(n)) return "\u6269\u5C55\u6570\u636E";
6614
+ if (/^(create_sheet|open_sheet|list_sheet|add_sheet|delete_sheet|publish_sheet|sheet_|reorder_sheet|create_subset|list_subset|set_sheet)/.test(n)) return "\u56FE\u7EB8\u96C6";
6615
+ if (/^(attach_|detach_|reload_|list_attached|get_pdf|create_dgn|create_dwf|import_dxf|import_dgn|export_dgn|create_hyper|remove_hyper|xref|bind_xref|clip_xref|path_xref)/.test(n)) return "\u53C2\u7167\u4E0E\u5E95\u56FE";
6616
+ if (/^(create_text|create_mleader|create_linetype|list_text|list_linetype|list_mleader|load_linetype|create_dimension_style|set_dimstyle|list_dimension_styles|set_mleader_style)/.test(n)) return "\u6837\u5F0F";
6617
+ if (/^(purge_|drawing_audit|drawing_recover|overkill)/.test(n)) return "\u6E05\u7406";
6618
+ if (/^(find_text|replace_text|set_text_|justify_|convert_text|explode_text)/.test(n)) return "\u6587\u5B57";
6619
+ if (/^(eval_lisp|connect_cad|get_cad|get_platform|get_system|at_command|new_document|bring_to_front|install_atlisp|init_atlisp|get_system_status)/.test(n)) return "\u7CFB\u7EDF";
6620
+ if (/^(list_packages|search_packages|install_package|get_function_usage|list_symbols|import_funlib)/.test(n)) return "\u5305\u4E0E\u51FD\u6570";
6621
+ if (/^(watch_|lisp_repl|webhook_|sandbox_|audit_|cad_pool_|external_|list_data|export_to|import_external)/.test(n)) return "\u9AD8\u7EA7";
6622
+ if (/^(list_prompts|get_prompt|search_tools|list_tools_by_category|get_tool_info)/.test(n)) return "\u5143\u5DE5\u5177";
6623
+ if (/^(agent_)/.test(n)) return "Agent\u4E0A\u4E0B\u6587";
6624
+ return "\u5176\u4ED6";
6625
+ }
6378
6626
  function addAnnotations(tool) {
6379
6627
  const isDeprecated = DEPRECATED_TOOLS.has(tool.name);
6380
6628
  const isPoolTool = tool.name.startsWith("cad_pool_") || tool.name === "session_pin_instance" || tool.name === "session_unpin_instance";
@@ -6394,11 +6642,12 @@ function addAnnotations(tool) {
6394
6642
  destructiveHint: tool.name.startsWith("delete_") || tool.name.startsWith("purge_") || tool.name.startsWith("boolean_") || EXTRA_DESTRUCTIVE.has(tool.name),
6395
6643
  idempotentHint: READ_ONLY_TOOLS.has(tool.name) || IDEMPOTENT_WRITE_TOOLS.has(tool.name),
6396
6644
  openWorldHint: OPEN_WORLD_TOOLS.has(tool.name),
6645
+ category: getToolCategory(tool.name),
6397
6646
  ...isDeprecated ? { deprecatedReason: "This tool has been superseded. Check documentation for alternatives." } : {}
6398
6647
  }
6399
6648
  };
6400
6649
  }
6401
- var READ_ONLY_TOOLS, DEPRECATED_TOOLS, EXTRA_DESTRUCTIVE, IDEMPOTENT_WRITE_TOOLS, OPEN_WORLD_TOOLS, tools41, toolsByName;
6650
+ var READ_ONLY_TOOLS, DEPRECATED_TOOLS, EXTRA_DESTRUCTIVE, IDEMPOTENT_WRITE_TOOLS, OPEN_WORLD_TOOLS, tools43, toolsByName;
6402
6651
  var init_tools = __esm({
6403
6652
  "src/tools/index.js"() {
6404
6653
  init_cad_tools();
@@ -6441,6 +6690,8 @@ var init_tools = __esm({
6441
6690
  init_context_tools();
6442
6691
  init_batch_transaction_tools();
6443
6692
  init_lint_tools();
6693
+ init_debug_tools();
6694
+ init_concurrent_tools();
6444
6695
  READ_ONLY_TOOLS = /* @__PURE__ */ new Set([
6445
6696
  "get_cad_info",
6446
6697
  "get_platform_info",
@@ -6523,7 +6774,8 @@ var init_tools = __esm({
6523
6774
  "analyze_lisp_code",
6524
6775
  "format_lisp_code",
6525
6776
  "list_lint_rules",
6526
- "lint_project"
6777
+ "lint_project",
6778
+ "debug_get_state"
6527
6779
  ]);
6528
6780
  DEPRECATED_TOOLS = /* @__PURE__ */ new Set([]);
6529
6781
  EXTRA_DESTRUCTIVE = /* @__PURE__ */ new Set([
@@ -6579,7 +6831,7 @@ var init_tools = __esm({
6579
6831
  "import_page_setup",
6580
6832
  "load_linetype"
6581
6833
  ]);
6582
- tools41 = [
6834
+ tools43 = [
6583
6835
  ...tools,
6584
6836
  ...tools2,
6585
6837
  ...tools3,
@@ -6619,9 +6871,11 @@ var init_tools = __esm({
6619
6871
  ...tools38,
6620
6872
  ...tools39,
6621
6873
  ...tools37,
6622
- ...tools40
6874
+ ...tools40,
6875
+ ...tools41,
6876
+ ...tools42
6623
6877
  ].map(addAnnotations);
6624
- toolsByName = new Map(tools41.map((t) => [t.name, t]));
6878
+ toolsByName = new Map(tools43.map((t) => [t.name, t]));
6625
6879
  }
6626
6880
  });
6627
6881
 
@@ -6674,7 +6928,7 @@ function parseLispList(str) {
6674
6928
  });
6675
6929
  }
6676
6930
  function createMcpServer(handlers = {}) {
6677
- const { tools: tools42 = [], handleToolCall: handleToolCall2 = () => ({ content: [] }) } = handlers;
6931
+ const { tools: tools44 = [], handleToolCall: handleToolCall2 = () => ({ content: [] }) } = handlers;
6678
6932
  const server = new Server(
6679
6933
  { name: SERVER_NAME, version: SERVER_VERSION },
6680
6934
  {
@@ -6697,8 +6951,8 @@ function createMcpServer(handlers = {}) {
6697
6951
  const params = request.params || {};
6698
6952
  const cursor = decodeCursor(params.cursor);
6699
6953
  const PAGE_SIZE = 100;
6700
- const page = tools42.slice(cursor, cursor + PAGE_SIZE);
6701
- const nextCursor = cursor + PAGE_SIZE < tools42.length ? encodeCursor(cursor + PAGE_SIZE) : void 0;
6954
+ const page = tools44.slice(cursor, cursor + PAGE_SIZE);
6955
+ const nextCursor = cursor + PAGE_SIZE < tools44.length ? encodeCursor(cursor + PAGE_SIZE) : void 0;
6702
6956
  return { tools: page, nextCursor };
6703
6957
  });
6704
6958
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
@@ -7047,11 +7301,11 @@ function createMcpServer(handlers = {}) {
7047
7301
  if (p.toLowerCase().startsWith(prefix)) values.push(p);
7048
7302
  }
7049
7303
  } else if (ref?.type === "tool") {
7050
- for (const t of tools42) {
7304
+ for (const t of tools44) {
7051
7305
  if (t.name.toLowerCase().startsWith(prefix)) values.push(t.name);
7052
7306
  }
7053
7307
  } else if (ref?.type === "ref/parameter") {
7054
- const toolDef = tools42.find((t) => t.name === ref.name);
7308
+ const toolDef = tools44.find((t) => t.name === ref.name);
7055
7309
  if (toolDef?.inputSchema?.properties) {
7056
7310
  const paramName = argument.name;
7057
7311
  const paramDef = toolDef.inputSchema.properties[paramName];
@@ -8565,82 +8819,435 @@ var init_constraint_handlers = __esm({
8565
8819
  }
8566
8820
  });
8567
8821
 
8568
- // src/handlers/dim-hatch-handlers.js
8569
- var dim_hatch_handlers_exports = {};
8570
- __export(dim_hatch_handlers_exports, {
8571
- batchSetLayer: () => batchSetLayer,
8572
- createDimension: () => createDimension,
8573
- createHatch: () => createHatch,
8574
- createNamedView: () => createNamedView,
8575
- exportDwf: () => exportDwf,
8576
- exportDxf: () => exportDxf,
8577
- getBlockAttributes: () => getBlockAttributes,
8578
- getDimensionValue: () => getDimensionValue,
8579
- listNamedViews: () => listNamedViews,
8580
- restoreNamedView: () => restoreNamedView,
8581
- setBlockAttribute: () => setBlockAttribute,
8582
- setHatchProperties: () => setHatchProperties,
8583
- zoomToExtents: () => zoomToExtents,
8584
- zoomToWindow: () => zoomToWindow
8585
- });
8586
- function toSafePoint(pt) {
8587
- const parts = (Array.isArray(pt) ? pt : String(pt).split(/[,\s]+/)).map(Number);
8588
- return parts.filter((n) => !isNaN(n)).join(" ");
8589
- }
8590
- async function createDimension(type, points, style = {}) {
8822
+ // src/debug-engine.js
8823
+ import { randomUUID as randomUUID4 } from "crypto";
8824
+ async function ensureCad() {
8591
8825
  if (!cad.connected) {
8826
+ log("Debug engine: CAD not connected, attempting auto-connect...");
8592
8827
  await cad.connect();
8593
8828
  }
8594
- if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
8595
- let dimCode = "";
8596
- const validTypes = ["ALIGNED", "LINEAR", "ORDINATE", "RADIAL", "DIAMETER", "ANGULAR"];
8597
- const dimType = (type || "ALIGNED").toUpperCase();
8598
- if (!validTypes.includes(dimType)) {
8599
- return { content: [{ type: "text", text: `\u4E0D\u652F\u6301\u7684\u6807\u6CE8\u7C7B\u578B: ${type}` }], isError: true };
8829
+ if (!cad.connected) {
8830
+ throw new Error("CAD \u672A\u8FDE\u63A5");
8600
8831
  }
8601
- const defPt = toSafePoint(points.defPt || "0,0");
8602
- const pt1 = toSafePoint(points.pt1 || points.pt || "0,0");
8603
- const pt2 = toSafePoint(points.pt2 || "100,0");
8604
- const leaderLen = parseFloat(style.leaderLength) || 10;
8605
- switch (dimType) {
8606
- case "ALIGNED":
8607
- dimCode = `(command "._DIMALIGNED" (list ${pt1}) (list ${pt2}) (list ${defPt}))`;
8608
- break;
8609
- case "LINEAR":
8610
- dimCode = `(command "._DIMLINEAR" (list ${pt1}) (list ${pt2}) (list ${defPt}))`;
8611
- break;
8612
- case "ORDINATE":
8613
- dimCode = `(command "._DIMORDINATE" (list ${pt1}) (list ${defPt}))`;
8614
- break;
8615
- case "RADIAL":
8616
- dimCode = `(command "._DIMRADIUS" (list ${defPt}) "${leaderLen}")`;
8617
- break;
8618
- case "DIAMETER":
8619
- dimCode = `(command "._DIMDIAMETER" (list ${defPt}) "${leaderLen}")`;
8620
- break;
8621
- case "ANGULAR":
8622
- dimCode = `(command "._DIMANGULAR" (list ${pt1}) (list ${pt2}) (list ${defPt}))`;
8623
- break;
8832
+ }
8833
+ async function createSession(code, chunks, encoding) {
8834
+ if (sessions.size >= MAX_SESSIONS2) {
8835
+ let oldest = null;
8836
+ for (const [id, s] of sessions) {
8837
+ if (!oldest || s.createdAt < oldest.createdAt) oldest = { id, s };
8838
+ }
8839
+ if (oldest) {
8840
+ sessions.delete(oldest.id);
8841
+ log(`Debug session ${oldest.id} evicted (max sessions)`);
8842
+ }
8843
+ }
8844
+ if (!chunks || chunks.length === 0) {
8845
+ throw new Error("Chunks array is empty");
8846
+ }
8847
+ if (chunks.length > MAX_CHUNKS) {
8848
+ throw new Error(`Too many chunks: ${chunks.length} (max ${MAX_CHUNKS})`);
8849
+ }
8850
+ for (const chunk of chunks) {
8851
+ if (typeof chunk.id !== "number" || typeof chunk.code !== "string") {
8852
+ throw new Error("Each chunk must have id (number) and code (string)");
8853
+ }
8624
8854
  }
8855
+ const sessionId = randomUUID4();
8856
+ const session = new DebugSession(sessionId, code, chunks, encoding);
8857
+ sessions.set(sessionId, session);
8858
+ return sessionId;
8859
+ }
8860
+ function getSession(id) {
8861
+ return sessions.get(id) || null;
8862
+ }
8863
+ async function closeSession(sessionId) {
8864
+ const session = sessions.get(sessionId);
8865
+ if (!session) return false;
8625
8866
  try {
8626
- await cad.sendCommand(dimCode + "\n");
8627
- return { content: [{ type: "text", text: `\u5DF2\u521B\u5EFA ${dimType} \u6807\u6CE8` }] };
8867
+ if (cad.connected) {
8868
+ await cad.sendCommand(CLEANUP_CODE + "\n");
8869
+ }
8628
8870
  } catch (e) {
8629
- return { content: [{ type: "text", text: `\u9519\u8BEF: ${e.message}` }], isError: true };
8871
+ log(`Debug cleanup warning: ${e.message}`);
8630
8872
  }
8873
+ sessions.delete(sessionId);
8874
+ return true;
8631
8875
  }
8632
- async function getDimensionValue(handle) {
8633
- if (!cad.connected) {
8634
- await cad.connect();
8876
+ async function loadHelpers(session) {
8877
+ await ensureCad();
8878
+ try {
8879
+ await cad.sendCommand(HELPER_DEFUNS + "\n");
8880
+ } catch (e) {
8881
+ throw new Error(`Failed to load debug helpers: ${e.message}`);
8635
8882
  }
8636
- if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
8637
- const code = `(progn
8638
- (vl-load-com)
8639
- (if (setq ent (handent "${escapeLispString(handle)}"))
8640
- (progn
8641
- (setq ed (entget ent) typ (cdr (assoc 0 ed)))
8642
- (if (wcmatch typ "DIMENSION")
8643
- (list
8883
+ }
8884
+ async function fastForward(session) {
8885
+ await ensureCad();
8886
+ session.state = "running";
8887
+ while (session.currentIndex < session.totalChunks) {
8888
+ const chunk = session.chunks[session.currentIndex];
8889
+ if (chunk.isBreakpoint) break;
8890
+ try {
8891
+ await cad.sendCommandWithResult(chunk.code, session.encoding);
8892
+ } catch (e) {
8893
+ session.state = "error";
8894
+ session.error = `Fast-forward error at chunk ${chunk.id}: ${e.message}`;
8895
+ throw new Error(session.error);
8896
+ }
8897
+ session.currentIndex++;
8898
+ }
8899
+ if (session.currentIndex >= session.totalChunks) {
8900
+ session.state = "done";
8901
+ return {
8902
+ done: true,
8903
+ finalResult: session.finalResult
8904
+ };
8905
+ }
8906
+ session.state = "paused";
8907
+ return {
8908
+ done: false,
8909
+ pausedAt: {
8910
+ id: session.chunks[session.currentIndex].id,
8911
+ line: session.chunks[session.currentIndex].line
8912
+ }
8913
+ };
8914
+ }
8915
+ async function executeStep(session) {
8916
+ await ensureCad();
8917
+ if (session.state !== "paused" && session.state !== "ready") {
8918
+ throw new Error(`Invalid state: ${session.state} (expected paused or ready)`);
8919
+ }
8920
+ if (session.currentIndex >= session.totalChunks) {
8921
+ session.state = "done";
8922
+ return { done: true, finalResult: session.finalResult };
8923
+ }
8924
+ const chunk = session.chunks[session.currentIndex];
8925
+ const vars = chunk.vars || [];
8926
+ session.state = "running";
8927
+ let rawResult;
8928
+ try {
8929
+ const varCapture = vars.length > 0 ? `(mapcar (function (lambda (v) (list (vl-prin1-to-string v) (if (boundp v) (vl-prin1-to-string (eval v)) nil)))) (list ${vars.map((v) => `'${v}`).join(" ")})` : "nil";
8930
+ const lispCode = `(progn
8931
+ (setq @::dbg/result (progn ${chunk.code}))
8932
+ (list
8933
+ (vl-prin1-to-string @::dbg/result)
8934
+ ${varCapture}))`;
8935
+ rawResult = await cad.sendCommandWithResult(lispCode, session.encoding);
8936
+ } catch (e) {
8937
+ session.state = "error";
8938
+ session.error = `Step error at chunk ${chunk.id}: ${e.message}`;
8939
+ throw new Error(session.error);
8940
+ }
8941
+ session.currentIndex++;
8942
+ session.finalResult = rawResult;
8943
+ let parsedResult = rawResult;
8944
+ let variables = [];
8945
+ try {
8946
+ const parsed = parseLispRecursive(rawResult);
8947
+ if (Array.isArray(parsed) && parsed.length >= 2) {
8948
+ parsedResult = parsed[0] !== null ? String(parsed[0]) : "nil";
8949
+ if (Array.isArray(parsed[1])) {
8950
+ variables = parsed[1].map((item) => {
8951
+ if (Array.isArray(item) && item.length >= 2) {
8952
+ return { name: String(item[0] || ""), value: item[1] !== null ? String(item[1]) : "nil" };
8953
+ }
8954
+ return null;
8955
+ }).filter(Boolean);
8956
+ }
8957
+ }
8958
+ } catch {
8959
+ parsedResult = rawResult || "nil";
8960
+ }
8961
+ const checkpoint = {
8962
+ id: chunk.id,
8963
+ line: chunk.line,
8964
+ variables,
8965
+ result: parsedResult
8966
+ };
8967
+ session.lastCheckpoint = checkpoint;
8968
+ if (session.currentIndex >= session.totalChunks) {
8969
+ session.state = "done";
8970
+ return {
8971
+ done: true,
8972
+ checkpoint,
8973
+ finalResult: parsedResult
8974
+ };
8975
+ }
8976
+ session.state = "paused";
8977
+ return { done: false, checkpoint };
8978
+ }
8979
+ async function continueExecution(session) {
8980
+ await ensureCad();
8981
+ if (session.state !== "paused" && session.state !== "ready") {
8982
+ throw new Error(`Invalid state: ${session.state} (expected paused or ready)`);
8983
+ }
8984
+ while (session.currentIndex < session.totalChunks) {
8985
+ const chunk = session.chunks[session.currentIndex];
8986
+ if (chunk.isBreakpoint) {
8987
+ return await executeStep(session);
8988
+ }
8989
+ try {
8990
+ await cad.sendCommandWithResult(chunk.code, session.encoding);
8991
+ } catch (e) {
8992
+ session.state = "error";
8993
+ session.error = `Continue error at chunk ${chunk.id}: ${e.message}`;
8994
+ throw new Error(session.error);
8995
+ }
8996
+ session.currentIndex++;
8997
+ session.finalResult = null;
8998
+ }
8999
+ session.state = "done";
9000
+ return { done: true, finalResult: session.finalResult };
9001
+ }
9002
+ async function evaluateExpression(session, expression) {
9003
+ await ensureCad();
9004
+ if (session.state !== "paused") {
9005
+ throw new Error(`Invalid state: ${session.state} (expected paused)`);
9006
+ }
9007
+ try {
9008
+ const result = await cad.sendCommandWithResult(`(vl-prin1-to-string ${expression})`, session.encoding);
9009
+ return { value: result || "nil" };
9010
+ } catch (e) {
9011
+ return { error: `CAD error: ${e.message}` };
9012
+ }
9013
+ }
9014
+ var sessions, MAX_SESSIONS2, MAX_CHUNKS, HELPER_DEFUNS, CLEANUP_CODE, DebugSession;
9015
+ var init_debug_engine = __esm({
9016
+ "src/debug-engine.js"() {
9017
+ init_cad();
9018
+ init_logger();
9019
+ init_handler_utils();
9020
+ sessions = /* @__PURE__ */ new Map();
9021
+ MAX_SESSIONS2 = 10;
9022
+ MAX_CHUNKS = 500;
9023
+ HELPER_DEFUNS = `
9024
+ (defun @::dbg/exec-chunk (chunk-code var-names)
9025
+ (setq @::dbg/result (eval (read chunk-code)))
9026
+ (if var-names
9027
+ (setq @::dbg/snapshot
9028
+ (mapcar
9029
+ (function (lambda (v)
9030
+ (list (vl-prin1-to-string v)
9031
+ (if (boundp v) (vl-prin1-to-string (eval v)) nil))))
9032
+ var-names))
9033
+ (setq @::dbg/snapshot nil))
9034
+ (list @::dbg/result @::dbg/snapshot))
9035
+ `;
9036
+ CLEANUP_CODE = `
9037
+ (if (not (vl-catch-all-error-p (vl-catch-all-apply (function (lambda ()
9038
+ (mapcar (function (lambda (s) (set s nil)))
9039
+ (quote (@::dbg/result @::dbg/snapshot @::dbg/state @::dbg/chunks @::dbg/idx @::dbg/encoding))))))))
9040
+ (princ "ok"))
9041
+ `;
9042
+ DebugSession = class {
9043
+ constructor(sessionId, code, chunks, encoding) {
9044
+ this.sessionId = sessionId;
9045
+ this.code = code;
9046
+ this.chunks = chunks;
9047
+ this.totalChunks = chunks.length;
9048
+ this.currentIndex = 0;
9049
+ this.encoding = encoding || null;
9050
+ this.state = "ready";
9051
+ this.lastCheckpoint = null;
9052
+ this.finalResult = null;
9053
+ this.createdAt = Date.now();
9054
+ this.error = null;
9055
+ }
9056
+ };
9057
+ }
9058
+ });
9059
+
9060
+ // src/handlers/debug-handlers.js
9061
+ var debug_handlers_exports = {};
9062
+ __export(debug_handlers_exports, {
9063
+ debugEvaluate: () => debugEvaluate,
9064
+ debugGetState: () => debugGetState,
9065
+ debugStart: () => debugStart,
9066
+ debugStep: () => debugStep,
9067
+ debugStop: () => debugStop
9068
+ });
9069
+ async function debugGetState(args) {
9070
+ try {
9071
+ const { sessionId } = args || {};
9072
+ if (!sessionId) return mcpError("Missing required argument: sessionId");
9073
+ const session = getSession(sessionId);
9074
+ if (!session) return mcpError(`Debug session not found: ${sessionId}`);
9075
+ const checkpoint = session.lastCheckpoint;
9076
+ return mcpSuccess(JSON.stringify({
9077
+ state: session.state,
9078
+ currentIndex: session.currentIndex,
9079
+ totalChunks: session.totalChunks,
9080
+ currentCheckpoint: checkpoint || void 0,
9081
+ error: session.error
9082
+ }));
9083
+ } catch (e) {
9084
+ return mcpError(e.message);
9085
+ }
9086
+ }
9087
+ async function debugStop(args) {
9088
+ try {
9089
+ const { sessionId } = args || {};
9090
+ if (!sessionId) return mcpError("Missing required argument: sessionId");
9091
+ const session = getSession(sessionId);
9092
+ if (!session) {
9093
+ return mcpError(`Debug session not found: ${sessionId}`);
9094
+ }
9095
+ const ok = await closeSession(sessionId);
9096
+ return mcpSuccess(JSON.stringify({ success: ok }));
9097
+ } catch (e) {
9098
+ return mcpError(e.message);
9099
+ }
9100
+ }
9101
+ var debugStart, debugStep, debugEvaluate;
9102
+ var init_debug_handlers = __esm({
9103
+ "src/handlers/debug-handlers.js"() {
9104
+ init_handler_utils();
9105
+ init_debug_engine();
9106
+ debugStart = withCadConnection(async (args) => {
9107
+ try {
9108
+ const { code, chunks, encoding } = args || {};
9109
+ if (!code) return mcpError("Missing required argument: code");
9110
+ if (!chunks || !Array.isArray(chunks)) return mcpError("Missing required argument: chunks (must be an array)");
9111
+ const sessionId = await createSession(code, chunks, encoding);
9112
+ const session = getSession(sessionId);
9113
+ if (!session) return mcpError("Failed to create debug session");
9114
+ await loadHelpers(session);
9115
+ const ffResult = await fastForward(session);
9116
+ if (session.state === "error") {
9117
+ await closeSession(sessionId);
9118
+ return mcpError(session.error);
9119
+ }
9120
+ return mcpSuccess(JSON.stringify({
9121
+ sessionId,
9122
+ totalChunks: session.totalChunks,
9123
+ ...ffResult
9124
+ }));
9125
+ } catch (e) {
9126
+ return mcpError(e.message);
9127
+ }
9128
+ });
9129
+ debugStep = withCadConnection(async (args) => {
9130
+ try {
9131
+ const { sessionId, action } = args || {};
9132
+ if (!sessionId) return mcpError("Missing required argument: sessionId");
9133
+ if (!action) return mcpError("Missing required argument: action");
9134
+ const session = getSession(sessionId);
9135
+ if (!session) return mcpError(`Debug session not found: ${sessionId}`);
9136
+ if (session.state !== "paused" && session.state !== "ready") {
9137
+ return mcpError(`Invalid state: ${session.state} (expected paused or ready)`);
9138
+ }
9139
+ let result;
9140
+ if (action === "step") {
9141
+ result = await executeStep(session);
9142
+ } else if (action === "continue") {
9143
+ result = await continueExecution(session);
9144
+ } else {
9145
+ return mcpError(`Invalid action: ${action} (expected step or continue)`);
9146
+ }
9147
+ if (session.state === "error") {
9148
+ return mcpError(session.error);
9149
+ }
9150
+ return mcpSuccess(JSON.stringify(result));
9151
+ } catch (e) {
9152
+ return mcpError(e.message);
9153
+ }
9154
+ });
9155
+ debugEvaluate = withCadConnection(async (args) => {
9156
+ try {
9157
+ const { sessionId, expression } = args || {};
9158
+ if (!sessionId) return mcpError("Missing required argument: sessionId");
9159
+ if (!expression) return mcpError("Missing required argument: expression");
9160
+ const session = getSession(sessionId);
9161
+ if (!session) return mcpError(`Debug session not found: ${sessionId}`);
9162
+ if (session.state !== "paused") {
9163
+ return mcpError(`Invalid state: ${session.state} (expected paused)`);
9164
+ }
9165
+ const result = await evaluateExpression(session, expression);
9166
+ if (result.error) return mcpError(result.error);
9167
+ return mcpSuccess(JSON.stringify({ value: result.value }));
9168
+ } catch (e) {
9169
+ return mcpError(e.message);
9170
+ }
9171
+ });
9172
+ }
9173
+ });
9174
+
9175
+ // src/handlers/dim-hatch-handlers.js
9176
+ var dim_hatch_handlers_exports = {};
9177
+ __export(dim_hatch_handlers_exports, {
9178
+ batchSetLayer: () => batchSetLayer,
9179
+ createDimension: () => createDimension,
9180
+ createHatch: () => createHatch,
9181
+ createNamedView: () => createNamedView,
9182
+ exportDwf: () => exportDwf,
9183
+ exportDxf: () => exportDxf,
9184
+ getBlockAttributes: () => getBlockAttributes,
9185
+ getDimensionValue: () => getDimensionValue,
9186
+ listNamedViews: () => listNamedViews,
9187
+ restoreNamedView: () => restoreNamedView,
9188
+ setBlockAttribute: () => setBlockAttribute,
9189
+ setHatchProperties: () => setHatchProperties,
9190
+ zoomToExtents: () => zoomToExtents,
9191
+ zoomToWindow: () => zoomToWindow
9192
+ });
9193
+ function toSafePoint(pt) {
9194
+ const parts = (Array.isArray(pt) ? pt : String(pt).split(/[,\s]+/)).map(Number);
9195
+ return parts.filter((n) => !isNaN(n)).join(" ");
9196
+ }
9197
+ async function createDimension(type, points, style = {}) {
9198
+ if (!cad.connected) {
9199
+ await cad.connect();
9200
+ }
9201
+ if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
9202
+ let dimCode = "";
9203
+ const validTypes = ["ALIGNED", "LINEAR", "ORDINATE", "RADIAL", "DIAMETER", "ANGULAR"];
9204
+ const dimType = (type || "ALIGNED").toUpperCase();
9205
+ if (!validTypes.includes(dimType)) {
9206
+ return { content: [{ type: "text", text: `\u4E0D\u652F\u6301\u7684\u6807\u6CE8\u7C7B\u578B: ${type}` }], isError: true };
9207
+ }
9208
+ const defPt = toSafePoint(points.defPt || "0,0");
9209
+ const pt1 = toSafePoint(points.pt1 || points.pt || "0,0");
9210
+ const pt2 = toSafePoint(points.pt2 || "100,0");
9211
+ const leaderLen = parseFloat(style.leaderLength) || 10;
9212
+ switch (dimType) {
9213
+ case "ALIGNED":
9214
+ dimCode = `(command "._DIMALIGNED" (list ${pt1}) (list ${pt2}) (list ${defPt}))`;
9215
+ break;
9216
+ case "LINEAR":
9217
+ dimCode = `(command "._DIMLINEAR" (list ${pt1}) (list ${pt2}) (list ${defPt}))`;
9218
+ break;
9219
+ case "ORDINATE":
9220
+ dimCode = `(command "._DIMORDINATE" (list ${pt1}) (list ${defPt}))`;
9221
+ break;
9222
+ case "RADIAL":
9223
+ dimCode = `(command "._DIMRADIUS" (list ${defPt}) "${leaderLen}")`;
9224
+ break;
9225
+ case "DIAMETER":
9226
+ dimCode = `(command "._DIMDIAMETER" (list ${defPt}) "${leaderLen}")`;
9227
+ break;
9228
+ case "ANGULAR":
9229
+ dimCode = `(command "._DIMANGULAR" (list ${pt1}) (list ${pt2}) (list ${defPt}))`;
9230
+ break;
9231
+ }
9232
+ try {
9233
+ await cad.sendCommand(dimCode + "\n");
9234
+ return { content: [{ type: "text", text: `\u5DF2\u521B\u5EFA ${dimType} \u6807\u6CE8` }] };
9235
+ } catch (e) {
9236
+ return { content: [{ type: "text", text: `\u9519\u8BEF: ${e.message}` }], isError: true };
9237
+ }
9238
+ }
9239
+ async function getDimensionValue(handle) {
9240
+ if (!cad.connected) {
9241
+ await cad.connect();
9242
+ }
9243
+ if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
9244
+ const code = `(progn
9245
+ (vl-load-com)
9246
+ (if (setq ent (handent "${escapeLispString(handle)}"))
9247
+ (progn
9248
+ (setq ed (entget ent) typ (cdr (assoc 0 ed)))
9249
+ (if (wcmatch typ "DIMENSION")
9250
+ (list
8644
9251
  "type" typ
8645
9252
  "handle" (cdr (assoc 5 ed))
8646
9253
  "layer" (cdr (assoc 8 ed))
@@ -10231,6 +10838,87 @@ var init_lint_handlers = __esm({
10231
10838
  }
10232
10839
  });
10233
10840
 
10841
+ // src/handlers/meta-handlers.js
10842
+ var meta_handlers_exports = {};
10843
+ __export(meta_handlers_exports, {
10844
+ get_prompt_handler: () => get_prompt_handler,
10845
+ get_tool_info: () => get_tool_info,
10846
+ list_prompts_handler: () => list_prompts_handler,
10847
+ list_tools_by_category: () => list_tools_by_category,
10848
+ search_tools: () => search_tools
10849
+ });
10850
+ async function search_tools(a) {
10851
+ const q = (a.query || "").toLowerCase();
10852
+ const results = tools43.filter((t) => t.name.toLowerCase().includes(q) || (t.description || "").toLowerCase().includes(q));
10853
+ const text = results.length ? `\u627E\u5230 ${results.length} \u4E2A\u5DE5\u5177:
10854
+ ${results.map((t) => `${t.name}: ${t.description}`).join("\n")}` : `\u672A\u627E\u5230\u5305\u542B "${a.query}" \u7684\u5DE5\u5177`;
10855
+ return { content: [{ type: "text", text }] };
10856
+ }
10857
+ async function list_tools_by_category(a) {
10858
+ const categories = {};
10859
+ for (const t of tools43) {
10860
+ const cat = t.annotations?.category || "\u5176\u4ED6";
10861
+ if (!categories[cat]) categories[cat] = [];
10862
+ categories[cat].push(t.name);
10863
+ }
10864
+ if (a.category) {
10865
+ const names = categories[a.category];
10866
+ return { content: [{ type: "text", text: names ? `${a.category} (${names.length}):
10867
+ ${names.join("\n")}` : `\u672A\u627E\u5230\u5206\u7C7B: ${a.category}` }] };
10868
+ }
10869
+ const lines = [];
10870
+ for (const [cat, names] of Object.entries(categories)) {
10871
+ lines.push(`## ${cat} (${names.length})`);
10872
+ }
10873
+ lines.push(`
10874
+ \u5171 ${Object.keys(categories).length} \u4E2A\u5206\u7C7B`);
10875
+ return { content: [{ type: "text", text: lines.join("\n") }] };
10876
+ }
10877
+ async function get_tool_info(a) {
10878
+ if (!a.name) return { content: [{ type: "text", text: "\u9700\u8981 name \u53C2\u6570\uFF08\u5DE5\u5177\u540D\u79F0\uFF09" }], isError: true };
10879
+ const tool = tools43.find((t) => t.name === a.name);
10880
+ if (!tool) return { content: [{ type: "text", text: `\u672A\u627E\u5230\u5DE5\u5177: ${a.name}` }], isError: true };
10881
+ const ann = tool.annotations || {};
10882
+ const required = (tool.inputSchema?.required || []).join(", ") || "\u65E0";
10883
+ const props = tool.inputSchema?.properties || {};
10884
+ const params = Object.entries(props).map(([k, v]) => {
10885
+ const req = required.includes(k) ? "\u5FC5\u586B" : "\u53EF\u9009";
10886
+ const desc = v.description || "";
10887
+ return ` ${k} (${req}): ${desc}`;
10888
+ }).join("\n");
10889
+ const lines = [
10890
+ `## ${tool.name}`,
10891
+ `\u63CF\u8FF0: ${tool.description || "\u65E0"}`,
10892
+ `\u5206\u7C7B: ${ann.category || "\u5176\u4ED6"}`,
10893
+ `\u53EA\u8BFB: ${ann.readOnlyHint ? "\u662F" : "\u5426"}`,
10894
+ `\u7834\u574F\u6027: ${ann.destructiveHint ? "\u662F" : "\u5426"}`,
10895
+ `\u5E42\u7B49: ${ann.idempotentHint ? "\u662F" : "\u5426"}`,
10896
+ `\u5F00\u653E\u4E16\u754C: ${ann.openWorldHint ? "\u662F" : "\u5426"}`,
10897
+ ``,
10898
+ `\u53C2\u6570:`,
10899
+ params || " \u65E0\u53C2\u6570"
10900
+ ];
10901
+ if (ann.deprecatedReason) {
10902
+ lines.push(`
10903
+ \u5DF2\u5F03\u7528: ${ann.deprecatedReason}`);
10904
+ }
10905
+ return { content: [{ type: "text", text: lines.join("\n") }] };
10906
+ }
10907
+ async function list_prompts_handler() {
10908
+ const prompts = await listPrompts2();
10909
+ return { content: [{ type: "text", text: JSON.stringify(prompts) }] };
10910
+ }
10911
+ async function get_prompt_handler(a) {
10912
+ const prompt = await getPrompt2(a.name, a.args || {});
10913
+ return { content: prompt.messages.map((m) => ({ type: "text", text: m.content.text || m.content })) };
10914
+ }
10915
+ var init_meta_handlers = __esm({
10916
+ "src/handlers/meta-handlers.js"() {
10917
+ init_tools();
10918
+ init_prompt_handlers();
10919
+ }
10920
+ });
10921
+
10234
10922
  // src/handlers/package-handlers.js
10235
10923
  var package_handlers_exports = {};
10236
10924
  __export(package_handlers_exports, {
@@ -10240,7 +10928,7 @@ __export(package_handlers_exports, {
10240
10928
  });
10241
10929
  async function fetchPackages() {
10242
10930
  const now = Date.now();
10243
- if (cachedPackages && cachedAt2 && now - cachedAt2 < CACHE_TTL2) {
10931
+ if (cachedPackages && cachedAt2 && now - cachedAt2 < CACHE_TTL) {
10244
10932
  return cachedPackages;
10245
10933
  }
10246
10934
  try {
@@ -10317,7 +11005,7 @@ async function installPackage(packageName) {
10317
11005
  }
10318
11006
  return { content: [{ type: "text", text: `\u5305 "${packageName}" \u5B89\u88C5\u547D\u4EE4\u5DF2\u53D1\u9001\uFF01` }] };
10319
11007
  }
10320
- var PACKAGES_LIST_URL, cachedPackages, cachedAt2, CACHE_TTL2;
11008
+ var PACKAGES_LIST_URL, cachedPackages, cachedAt2, CACHE_TTL;
10321
11009
  var init_package_handlers = __esm({
10322
11010
  "src/handlers/package-handlers.js"() {
10323
11011
  init_logger();
@@ -10326,7 +11014,7 @@ var init_package_handlers = __esm({
10326
11014
  PACKAGES_LIST_URL = process.env.PACKAGES_LIST_URL || "http://s3.atlisp.cn/packages-list?fmt=json";
10327
11015
  cachedPackages = null;
10328
11016
  cachedAt2 = null;
10329
- CACHE_TTL2 = 10 * 60 * 1e3;
11017
+ CACHE_TTL = 10 * 60 * 1e3;
10330
11018
  }
10331
11019
  });
10332
11020
 
@@ -12150,6 +12838,192 @@ var init_sheetset_handlers = __esm({
12150
12838
  }
12151
12839
  });
12152
12840
 
12841
+ // src/handlers/skill-handlers.js
12842
+ var skill_handlers_exports = {};
12843
+ __export(skill_handlers_exports, {
12844
+ skill_install: () => skill_install,
12845
+ skill_list_categories: () => skill_list_categories,
12846
+ skill_list_installed: () => skill_list_installed,
12847
+ skill_search: () => skill_search,
12848
+ skill_uninstall: () => skill_uninstall
12849
+ });
12850
+ import fs8 from "fs";
12851
+ import path12 from "path";
12852
+ import os7 from "os";
12853
+ function readCache() {
12854
+ try {
12855
+ if (fs8.existsSync(CACHE_PATH)) return JSON.parse(fs8.readFileSync(CACHE_PATH, "utf-8"));
12856
+ } catch {
12857
+ }
12858
+ return null;
12859
+ }
12860
+ function writeCache(data) {
12861
+ try {
12862
+ fs8.writeFileSync(CACHE_PATH, JSON.stringify(data, null, 2), "utf-8");
12863
+ } catch {
12864
+ }
12865
+ }
12866
+ async function fetchIndex() {
12867
+ let index = readCache();
12868
+ if (index && Date.now() - index.fetchedAt < 36e5) return index;
12869
+ try {
12870
+ const resp = await fetch(REGISTRY_URL, { headers: { "User-Agent": "atlisp-mcp" } });
12871
+ if (resp.ok) {
12872
+ index = await resp.json();
12873
+ index.fetchedAt = Date.now();
12874
+ writeCache(index);
12875
+ }
12876
+ } catch {
12877
+ }
12878
+ return index;
12879
+ }
12880
+ async function skill_search(a) {
12881
+ const index = await fetchIndex();
12882
+ if (!index?.skills) return { content: [{ type: "text", text: "\u65E0\u6CD5\u83B7\u53D6 skill \u6CE8\u518C\u8868\u7D22\u5F15" }], isError: true };
12883
+ const q = (a.query || "").toLowerCase();
12884
+ const results = index.skills.filter(
12885
+ (s) => s.name.toLowerCase().includes(q) || (s.description || "").toLowerCase().includes(q) || (s.tags || []).some((t) => t.toLowerCase().includes(q))
12886
+ );
12887
+ if (!results.length) return { content: [{ type: "text", text: `\u672A\u627E\u5230\u5339\u914D "${a.query}" \u7684 skill` }] };
12888
+ const lines = [`\u627E\u5230 ${results.length} \u4E2A skill:
12889
+ `];
12890
+ for (const s of results) {
12891
+ lines.push(`**${s.name}**`);
12892
+ if (s.description) lines.push(` \u63CF\u8FF0: ${s.description}`);
12893
+ if (s.tags?.length) lines.push(` \u6807\u7B7E: ${s.tags.join(", ")}`);
12894
+ if (s.version) lines.push(` \u7248\u672C: ${s.version}`);
12895
+ lines.push("");
12896
+ }
12897
+ return { content: [{ type: "text", text: lines.join("\n") }] };
12898
+ }
12899
+ async function skill_install(a) {
12900
+ if (!a.name) return { content: [{ type: "text", text: "\u9700\u8981 skill \u540D\u79F0" }], isError: true };
12901
+ const index = await fetchIndex();
12902
+ if (!index?.skills) return { content: [{ type: "text", text: "\u65E0\u6CD5\u83B7\u53D6 skill \u6CE8\u518C\u8868\u7D22\u5F15" }], isError: true };
12903
+ const skill = index.skills.find((s) => s.name === a.name);
12904
+ if (!skill) return { content: [{ type: "text", text: `\u672A\u627E\u5230 skill: ${a.name}` }], isError: true };
12905
+ const downloadUrl = skill.download_url || skill.url;
12906
+ if (!downloadUrl) return { content: [{ type: "text", text: `Skill "${a.name}" \u6CA1\u6709\u4E0B\u8F7D\u5730\u5740` }], isError: true };
12907
+ let content;
12908
+ try {
12909
+ const resp = await fetch(downloadUrl, { headers: { "User-Agent": "atlisp-mcp" } });
12910
+ if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
12911
+ content = await resp.text();
12912
+ } catch (e) {
12913
+ return { content: [{ type: "text", text: `\u4E0B\u8F7D\u5931\u8D25: ${e.message}` }], isError: true };
12914
+ }
12915
+ try {
12916
+ fs8.mkdirSync(SKILLS_DIR, { recursive: true });
12917
+ } catch {
12918
+ }
12919
+ const safeName = a.name.replace(/[^a-zA-Z0-9\u4e00-\u9fff_-]/g, "-").substring(0, 40);
12920
+ const skillFile = path12.join(SKILLS_DIR, `skill-${safeName}.md`);
12921
+ if (fs8.existsSync(skillFile)) {
12922
+ return { content: [{ type: "text", text: `Skill "${a.name}" \u5DF2\u5B89\u88C5 (${skillFile})` }], isError: true };
12923
+ }
12924
+ let finalContent = content;
12925
+ if (!content.match(/^---\n/)) {
12926
+ const tags = (skill.tags || []).join(", ");
12927
+ finalContent = `---
12928
+ name: ${a.name}
12929
+ description: ${skill.description || ""}
12930
+ tags: ${tags}
12931
+ ---
12932
+
12933
+ ${content}`;
12934
+ }
12935
+ fs8.writeFileSync(skillFile, finalContent, "utf-8");
12936
+ return { content: [{ type: "text", text: `${skill.name} \u5B89\u88C5\u6210\u529F
12937
+ \u6587\u4EF6: ${skillFile}${skill.version ? `
12938
+ \u7248\u672C: ${skill.version}` : ""}` }] };
12939
+ }
12940
+ async function skill_list_installed() {
12941
+ if (!fs8.existsSync(SKILLS_DIR)) return { content: [{ type: "text", text: "\u6682\u65E0\u5DF2\u5B89\u88C5\u7684 skill" }] };
12942
+ const results = [];
12943
+ try {
12944
+ const entries = fs8.readdirSync(SKILLS_DIR, { withFileTypes: true });
12945
+ for (const entry of entries) {
12946
+ if (entry.isFile() && entry.name.endsWith(".md")) {
12947
+ const fullPath = path12.join(SKILLS_DIR, entry.name);
12948
+ try {
12949
+ const raw = fs8.readFileSync(fullPath, "utf-8");
12950
+ const match = raw.match(/^---\n([\s\S]*?)\n---/);
12951
+ if (match) {
12952
+ const fm = {};
12953
+ for (const line of match[1].split("\n")) {
12954
+ const sep = line.indexOf(":");
12955
+ if (sep > 0) fm[line.slice(0, sep).trim()] = line.slice(sep + 1).trim();
12956
+ }
12957
+ results.push({ name: fm.name || entry.name.replace(".md", ""), description: fm.description || "", tags: fm.tags || "" });
12958
+ } else {
12959
+ results.push({ name: entry.name.replace(".md", ""), description: "", tags: "" });
12960
+ }
12961
+ } catch {
12962
+ }
12963
+ }
12964
+ }
12965
+ } catch {
12966
+ }
12967
+ if (!results.length) return { content: [{ type: "text", text: "\u6682\u65E0\u5DF2\u5B89\u88C5\u7684 skill" }] };
12968
+ const lines = [`\u5DF2\u5B89\u88C5 ${results.length} \u4E2A skill:
12969
+ `];
12970
+ for (const s of results) {
12971
+ lines.push(`**${s.name}**${s.description ? ` \u2014 ${s.description}` : ""}`);
12972
+ if (s.tags) lines.push(` \u6807\u7B7E: ${s.tags}`);
12973
+ }
12974
+ return { content: [{ type: "text", text: lines.join("\n") }] };
12975
+ }
12976
+ async function skill_uninstall(a) {
12977
+ if (!a.name) return { content: [{ type: "text", text: "\u9700\u8981 skill \u540D\u79F0" }], isError: true };
12978
+ if (!fs8.existsSync(SKILLS_DIR)) return { content: [{ type: "text", text: `\u672A\u5B89\u88C5 skill: ${a.name}` }], isError: true };
12979
+ let found = null;
12980
+ try {
12981
+ const entries = fs8.readdirSync(SKILLS_DIR, { withFileTypes: true });
12982
+ for (const entry of entries) {
12983
+ if (entry.isFile() && entry.name.endsWith(".md")) {
12984
+ const fullPath = path12.join(SKILLS_DIR, entry.name);
12985
+ try {
12986
+ const raw = fs8.readFileSync(fullPath, "utf-8");
12987
+ const match = raw.match(/^---\n([\s\S]*?)\n---/);
12988
+ if (match) {
12989
+ for (const line of match[1].split("\n")) {
12990
+ if (line.startsWith("name:") && line.slice(5).trim() === a.name) {
12991
+ found = fullPath;
12992
+ break;
12993
+ }
12994
+ }
12995
+ } else if (entry.name.replace(".md", "") === a.name) {
12996
+ found = fullPath;
12997
+ }
12998
+ } catch {
12999
+ }
13000
+ if (found) break;
13001
+ }
13002
+ }
13003
+ } catch {
13004
+ }
13005
+ if (!found) return { content: [{ type: "text", text: `\u672A\u5B89\u88C5 skill: ${a.name}` }], isError: true };
13006
+ fs8.unlinkSync(found);
13007
+ return { content: [{ type: "text", text: `${a.name} \u5DF2\u5378\u8F7D` }] };
13008
+ }
13009
+ async function skill_list_categories() {
13010
+ const index = await fetchIndex();
13011
+ if (!index?.skills) return { content: [{ type: "text", text: "\u65E0\u6CD5\u83B7\u53D6 skill \u6CE8\u518C\u8868\u7D22\u5F15" }], isError: true };
13012
+ const cats = /* @__PURE__ */ new Set();
13013
+ for (const s of index.skills) for (const t of s.tags || []) cats.add(t);
13014
+ const sorted = Array.from(cats).sort();
13015
+ return { content: [{ type: "text", text: sorted.length ? `\u6CE8\u518C\u8868\u5206\u7C7B (${sorted.length}):
13016
+ ${sorted.join("\n")}` : "\u6682\u65E0\u5206\u7C7B" }] };
13017
+ }
13018
+ var SKILLS_DIR, REGISTRY_URL, CACHE_PATH;
13019
+ var init_skill_handlers = __esm({
13020
+ "src/handlers/skill-handlers.js"() {
13021
+ SKILLS_DIR = path12.join(os7.homedir(), ".atlisp", "skills");
13022
+ REGISTRY_URL = process.env.SKILL_REGISTRY_URL || "https://raw.githubusercontent.com/atlisp/skill-registry/main/index.json";
13023
+ CACHE_PATH = path12.join(os7.homedir(), ".atlisp", "skill-registry-cache.json");
13024
+ }
13025
+ });
13026
+
12153
13027
  // src/handlers/spatial-handlers.js
12154
13028
  var spatial_handlers_exports = {};
12155
13029
  __export(spatial_handlers_exports, {
@@ -13521,13 +14395,13 @@ function lispPoint(pt) {
13521
14395
  }
13522
14396
  async function attachXref(args) {
13523
14397
  try {
13524
- const path20 = esc3(args.path);
14398
+ const path21 = esc3(args.path);
13525
14399
  const point = lispPoint(args.point);
13526
14400
  const scale = args.scale || 1;
13527
14401
  const rotation = args.rotation || 0;
13528
14402
  const overlay = args.overlay ? "_O" : "_A";
13529
14403
  const result = await cad.sendCommandWithResult(
13530
- `(command "_.XATTACH" "${path20}" "${overlay}" "${point}" "${scale}" "${rotation}") (princ)`
14404
+ `(command "_.XATTACH" "${path21}" "${overlay}" "${point}" "${scale}" "${rotation}") (princ)`
13531
14405
  );
13532
14406
  return mcpSuccess(`\u5916\u90E8\u53C2\u7167\u5DF2\u9644\u7740: ${args.path}`);
13533
14407
  } catch (e) {
@@ -13845,6 +14719,7 @@ var init_ = __esm({
13845
14719
  "./handlers/block-handlers.js": () => Promise.resolve().then(() => (init_block_handlers(), block_handlers_exports)),
13846
14720
  "./handlers/cad-handlers.js": () => Promise.resolve().then(() => (init_cad_handlers(), cad_handlers_exports)),
13847
14721
  "./handlers/constraint-handlers.js": () => Promise.resolve().then(() => (init_constraint_handlers(), constraint_handlers_exports)),
14722
+ "./handlers/debug-handlers.js": () => Promise.resolve().then(() => (init_debug_handlers(), debug_handlers_exports)),
13848
14723
  "./handlers/dim-hatch-handlers.js": () => Promise.resolve().then(() => (init_dim_hatch_handlers(), dim_hatch_handlers_exports)),
13849
14724
  "./handlers/draw-handlers.js": () => Promise.resolve().then(() => (init_draw_handlers(), draw_handlers_exports)),
13850
14725
  "./handlers/edit-handlers.js": () => Promise.resolve().then(() => (init_edit_handlers(), edit_handlers_exports)),
@@ -13854,6 +14729,7 @@ var init_ = __esm({
13854
14729
  "./handlers/function-handlers.js": () => Promise.resolve().then(() => (init_function_handlers(), function_handlers_exports)),
13855
14730
  "./handlers/layer-handlers.js": () => Promise.resolve().then(() => (init_layer_handlers(), layer_handlers_exports)),
13856
14731
  "./handlers/lint-handlers.js": () => Promise.resolve().then(() => (init_lint_handlers(), lint_handlers_exports)),
14732
+ "./handlers/meta-handlers.js": () => Promise.resolve().then(() => (init_meta_handlers(), meta_handlers_exports)),
13857
14733
  "./handlers/package-handlers.js": () => Promise.resolve().then(() => (init_package_handlers(), package_handlers_exports)),
13858
14734
  "./handlers/parametric-handlers.js": () => Promise.resolve().then(() => (init_parametric_handlers(), parametric_handlers_exports)),
13859
14735
  "./handlers/pdf-annotation-handlers.js": () => Promise.resolve().then(() => (init_pdf_annotation_handlers(), pdf_annotation_handlers_exports)),
@@ -13862,6 +14738,7 @@ var init_ = __esm({
13862
14738
  "./handlers/query-print-handlers.js": () => Promise.resolve().then(() => (init_query_print_handlers(), query_print_handlers_exports)),
13863
14739
  "./handlers/resource-handlers.js": () => Promise.resolve().then(() => (init_resource_handlers(), resource_handlers_exports)),
13864
14740
  "./handlers/sheetset-handlers.js": () => Promise.resolve().then(() => (init_sheetset_handlers(), sheetset_handlers_exports)),
14741
+ "./handlers/skill-handlers.js": () => Promise.resolve().then(() => (init_skill_handlers(), skill_handlers_exports)),
13865
14742
  "./handlers/spatial-handlers.js": () => Promise.resolve().then(() => (init_spatial_handlers(), spatial_handlers_exports)),
13866
14743
  "./handlers/style-handlers.js": () => Promise.resolve().then(() => (init_style_handlers(), style_handlers_exports)),
13867
14744
  "./handlers/text-handlers.js": () => Promise.resolve().then(() => (init_text_handlers(), text_handlers_exports)),
@@ -13874,8 +14751,8 @@ var init_ = __esm({
13874
14751
  });
13875
14752
 
13876
14753
  // src/handler-loader.js
13877
- import fs8 from "fs";
13878
- import path12 from "path";
14754
+ import fs9 from "fs";
14755
+ import path13 from "path";
13879
14756
  import { fileURLToPath as fileURLToPath5 } from "url";
13880
14757
  function toolNameToFuncName(toolName) {
13881
14758
  if (FUNCTION_OVERRIDES[toolName]) return FUNCTION_OVERRIDES[toolName];
@@ -13925,10 +14802,10 @@ async function getHandlerModule(name) {
13925
14802
  async function initHandlerModules() {
13926
14803
  if (_handlerModulesInit) return;
13927
14804
  _handlerModulesInit = true;
13928
- const handlerDir = path12.join(__dirname5, "handlers");
14805
+ const handlerDir = path13.join(__dirname5, "handlers");
13929
14806
  let names;
13930
14807
  try {
13931
- names = await fs8.promises.readdir(handlerDir);
14808
+ names = await fs9.promises.readdir(handlerDir);
13932
14809
  } catch {
13933
14810
  log("Handler directory not found, using static manifest");
13934
14811
  return;
@@ -13939,6 +14816,7 @@ async function initHandlerModules() {
13939
14816
  handlerModules[name] = () => globImport_handlers_handlers_js(`./handlers/${name}-handlers.js`);
13940
14817
  }
13941
14818
  }
14819
+ watchHandlers();
13942
14820
  }
13943
14821
  async function getHandler(toolName) {
13944
14822
  const resolved = await resolveHandler(toolName);
@@ -13947,12 +14825,40 @@ async function getHandler(toolName) {
13947
14825
  if (!mod || typeof mod[resolved.funcName] !== "function") return null;
13948
14826
  return mod[resolved.funcName];
13949
14827
  }
13950
- var __dirname5, _handlerModulesInit, _handlerNameMapInit, handlerModules, moduleCache, resolvedCache, FUNCTION_OVERRIDES;
13951
- var init_handler_loader = __esm({
14828
+ function watchHandlers() {
14829
+ if (_watcherInitialized) return;
14830
+ _watcherInitialized = true;
14831
+ const handlerDir = path13.join(__dirname5, "handlers");
14832
+ let debounceTimer = null;
14833
+ try {
14834
+ if (!fs9.existsSync(handlerDir)) return;
14835
+ fs9.watch(handlerDir, (eventType, filename) => {
14836
+ if (!filename || !filename.endsWith("-handlers.js")) return;
14837
+ if (debounceTimer) clearTimeout(debounceTimer);
14838
+ debounceTimer = setTimeout(async () => {
14839
+ const name = filename.replace("-handlers.js", "");
14840
+ moduleCache.delete(name);
14841
+ resolvedCache.clear();
14842
+ _handlerNameMapInit = false;
14843
+ _handlerModulesInit = false;
14844
+ try {
14845
+ await initHandlerModules();
14846
+ log(`Handler hot-reloaded: ${filename}`);
14847
+ } catch (e) {
14848
+ log(`Handler reload failed for ${filename}: ${e.message}`);
14849
+ }
14850
+ }, 300);
14851
+ });
14852
+ } catch (e) {
14853
+ log(`Handler watch error: ${e.message}`);
14854
+ }
14855
+ }
14856
+ var __dirname5, _handlerModulesInit, _handlerNameMapInit, handlerModules, moduleCache, resolvedCache, FUNCTION_OVERRIDES, _watcherInitialized;
14857
+ var init_handler_loader = __esm({
13952
14858
  "src/handler-loader.js"() {
13953
14859
  init_logger();
13954
14860
  init_();
13955
- __dirname5 = path12.dirname(fileURLToPath5(import.meta.url));
14861
+ __dirname5 = path13.dirname(fileURLToPath5(import.meta.url));
13956
14862
  _handlerModulesInit = false;
13957
14863
  _handlerNameMapInit = false;
13958
14864
  handlerModules = {};
@@ -13964,16 +14870,18 @@ var init_handler_loader = __esm({
13964
14870
  get_dimension: "getDimensionValue",
13965
14871
  eval_lisp_with_result: "evalLisp"
13966
14872
  };
14873
+ _watcherInitialized = false;
13967
14874
  }
13968
14875
  });
13969
14876
 
13970
14877
  // src/streaming.js
13971
- async function streamEntitiesByType({ type = null, layer = null, chunk = STREAM_CHUNK_SIZE, offset = 0 }) {
13972
- const hasFilter = type || layer;
13973
- const filters = [];
13974
- if (type) filters.push(`(cons 0 "${type}")`);
13975
- if (layer) filters.push(`(cons 8 "${layer}")`);
13976
- const ssgetExpr = filters.length > 0 ? `(ssget "_X" (list ${filters.join(" ")}))` : `(ssget "_X")`;
14878
+ function escapeLispStr(str) {
14879
+ if (typeof str !== "string") return "";
14880
+ return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
14881
+ }
14882
+ async function streamEntities({ filterExpr, extractFields, itemMapper, offset, limit, hasFilter, warnThreshold, overLimitMsg, resultProp }) {
14883
+ const ssgetExpr = filterExpr ? `(ssget "_X" ${filterExpr})` : `(ssget "_X")`;
14884
+ const extractCode = extractFields.length === 1 ? `(list ${extractFields.join(" ")})` : `(list ${extractFields.join(" ")})`;
13977
14885
  const lisp = `(progn
13978
14886
  (setq ss ${ssgetExpr})
13979
14887
  (setq total (if ss (sslength ss) 0))
@@ -13985,10 +14893,10 @@ async function streamEntitiesByType({ type = null, layer = null, chunk = STREAM_
13985
14893
  (setq result (list total))
13986
14894
  (setq i ${offset})
13987
14895
  (setq count 0)
13988
- (while (and (< i total) (< count ${chunk}))
14896
+ (while (and (< i total) (< count ${limit}))
13989
14897
  (setq e (ssname ss i))
13990
14898
  (setq elist (entget e))
13991
- (setq result (cons (list (cdr (assoc 0 elist)) (cdr (assoc 5 elist)) (cdr (assoc 8 elist))) result))
14899
+ (setq result (cons ${extractCode} result))
13992
14900
  (setq i (1+ i))
13993
14901
  (setq count (1+ count)))
13994
14902
  (reverse result)))))`;
@@ -14002,108 +14910,68 @@ async function streamEntitiesByType({ type = null, layer = null, chunk = STREAM_
14002
14910
  offset: 0,
14003
14911
  count: 0,
14004
14912
  hasMore: false,
14005
- entities: [],
14006
- error: `\u5B9E\u4F53\u6570\u91CF (${parsed[1]}) \u8D85\u8FC7\u6700\u5927\u9650\u5236 (${MAX_ENTITY_LIMIT})\uFF0C\u8BF7\u4F7F\u7528 type/layer \u8FC7\u6EE4`
14913
+ [resultProp]: [],
14914
+ error: `\u5B9E\u4F53\u6570\u91CF (${parsed[1]}) \u8D85\u8FC7\u6700\u5927\u9650\u5236 (${MAX_ENTITY_LIMIT})\uFF0C${overLimitMsg}`
14007
14915
  };
14008
14916
  }
14009
- const entities = parsed.slice(1);
14010
- const result = {
14011
- total,
14012
- offset,
14013
- count: entities.length,
14014
- hasMore: offset + entities.length < total,
14015
- entities
14016
- };
14017
- if (!hasFilter && total > UNFILTERED_WARN_THRESHOLD) {
14018
- result.warning = `\u672A\u63D0\u4F9B type/layer \u8FC7\u6EE4\uFF0C\u5168\u56FE\u626B\u63CF\u4E86 ${total} \u4E2A\u5B9E\u4F53\u3002\u5EFA\u8BAE\u6DFB\u52A0 type \u6216 layer \u53C2\u6570\u4EE5\u5927\u5E45\u63D0\u5347\u6027\u80FD\u548C\u964D\u4F4E\u5185\u5B58\u5360\u7528\u3002`;
14917
+ const items = parsed.slice(1).map(itemMapper);
14918
+ const result = { total, offset, count: items.length, hasMore: offset + items.length < total, [resultProp]: items };
14919
+ if (!hasFilter && total > warnThreshold) {
14920
+ result.warning = `\u672A\u63D0\u4F9B\u8FC7\u6EE4\u6761\u4EF6\uFF0C\u5168\u56FE\u626B\u63CF\u4E86 ${total} \u4E2A\u5B9E\u4F53\u3002\u5EFA\u8BAE\u6DFB\u52A0\u8FC7\u6EE4\u53C2\u6570\u4EE5\u63D0\u5347\u6027\u80FD\u3002`;
14019
14921
  }
14020
14922
  return result;
14021
14923
  } catch {
14022
- return { total: 0, offset: 0, count: 0, hasMore: false, entities: [] };
14924
+ return { total: 0, offset: 0, count: 0, hasMore: false, [resultProp]: [] };
14023
14925
  }
14024
14926
  }
14927
+ async function streamEntitiesByType({ type = null, layer = null, chunk = STREAM_CHUNK_SIZE, offset = 0 }) {
14928
+ const filters = [];
14929
+ if (type) filters.push(`(cons 0 "${escapeLispStr(type)}")`);
14930
+ if (layer) filters.push(`(cons 8 "${escapeLispStr(layer)}")`);
14931
+ const filterExpr = filters.length > 0 ? `(list ${filters.join(" ")})` : null;
14932
+ return streamEntities({
14933
+ filterExpr,
14934
+ extractFields: ["(list (cdr (assoc 0 elist)) (cdr (assoc 5 elist)) (cdr (assoc 8 elist)))"],
14935
+ itemMapper: (item) => ({ type: item[0], handle: item[1], layer: item[2] }),
14936
+ offset,
14937
+ limit: chunk,
14938
+ hasFilter: !!(type || layer),
14939
+ warnThreshold: UNFILTERED_WARN_THRESHOLD,
14940
+ overLimitMsg: "\u8BF7\u4F7F\u7528 type/layer \u8FC7\u6EE4",
14941
+ resultProp: "entities"
14942
+ });
14943
+ }
14025
14944
  async function streamTextContent({ layer = null, bbox = null, offset = 0, limit = 100 }) {
14026
14945
  const textFilters = [`(cons 0 "TEXT")`, `(cons 0 "MTEXT")`];
14027
- if (layer) textFilters.push(`(cons 8 "${layer}")`);
14028
- const lisp = `(progn
14029
- (setq ss (ssget "_X" (list '(-4 . "<OR") ${textFilters.join(" ")} '(-4 . "OR>"))))
14030
- (setq total (if ss (sslength ss) 0))
14031
- (if (> total ${MAX_ENTITY_LIMIT})
14032
- (list -1 total)
14033
- (if (>= ${offset} total)
14034
- (list total)
14035
- (progn
14036
- (setq result (list total))
14037
- (setq i ${offset})
14038
- (setq count 0)
14039
- (while (and (< i total) (< count ${limit}))
14040
- (setq e (ssname ss i))
14041
- (setq elist (entget e))
14042
- (setq txt (cdr (assoc 1 elist)))
14043
- (setq result (cons (list (cdr (assoc 5 elist)) txt (cdr (assoc 10 elist))) result))
14044
- (setq i (1+ i))
14045
- (setq count (1+ count)))
14046
- (reverse result)))))`;
14047
- const raw = await cad.sendCommandWithResult(lisp);
14048
- try {
14049
- const parsed = JSON.parse(raw);
14050
- const total = parsed[0] || 0;
14051
- if (total === -1) {
14052
- return {
14053
- total: parsed[1] || 0,
14054
- offset: 0,
14055
- count: 0,
14056
- hasMore: false,
14057
- texts: [],
14058
- error: `\u6587\u672C\u5B9E\u4F53\u6570\u91CF (${parsed[1]}) \u8D85\u8FC7\u6700\u5927\u9650\u5236 (${MAX_ENTITY_LIMIT})\uFF0C\u8BF7\u4F7F\u7528 layer \u8FC7\u6EE4`
14059
- };
14060
- }
14061
- const texts = parsed.slice(1).map((t) => ({ handle: t[0], text: t[1], point: t[2] }));
14062
- return { total, offset, count: texts.length, hasMore: offset + texts.length < total, texts };
14063
- } catch {
14064
- return { total: 0, offset: 0, count: 0, hasMore: false, texts: [] };
14065
- }
14946
+ if (layer) textFilters.push(`(cons 8 "${escapeLispStr(layer)}")`);
14947
+ const filterExpr = `(list '(-4 . "<OR") ${textFilters.join(" ")} '(-4 . "OR>"))`;
14948
+ return streamEntities({
14949
+ filterExpr,
14950
+ extractFields: ["(list (cdr (assoc 5 elist)) (cdr (assoc 1 elist)) (cdr (assoc 10 elist)))"],
14951
+ itemMapper: (item) => ({ handle: item[0], text: item[1], point: item[2] }),
14952
+ offset,
14953
+ limit,
14954
+ hasFilter: !!layer,
14955
+ warnThreshold: UNFILTERED_WARN_THRESHOLD,
14956
+ overLimitMsg: "\u8BF7\u4F7F\u7528 layer \u8FC7\u6EE4",
14957
+ resultProp: "texts"
14958
+ });
14066
14959
  }
14067
14960
  async function streamBlockRefs({ blockName = null, offset = 0, limit = 100 }) {
14068
14961
  const filters = [`(cons 0 "INSERT")`];
14069
- if (blockName) filters.push(`(cons 2 "${blockName}")`);
14070
- const lisp = `(progn
14071
- (setq ss (ssget "_X" (list ${filters.join(" ")})))
14072
- (setq total (if ss (sslength ss) 0))
14073
- (if (> total ${MAX_ENTITY_LIMIT})
14074
- (list -1 total)
14075
- (if (>= ${offset} total)
14076
- (list total)
14077
- (progn
14078
- (setq result (list total))
14079
- (setq i ${offset})
14080
- (setq count 0)
14081
- (while (and (< i total) (< count ${limit}))
14082
- (setq e (ssname ss i))
14083
- (setq elist (entget e))
14084
- (setq result (cons (list (cdr (assoc 2 elist)) (cdr (assoc 5 elist)) (cdr (assoc 10 elist))) result))
14085
- (setq i (1+ i))
14086
- (setq count (1+ count)))
14087
- (reverse result)))))`;
14088
- const raw = await cad.sendCommandWithResult(lisp);
14089
- try {
14090
- const parsed = JSON.parse(raw);
14091
- const total = parsed[0] || 0;
14092
- if (total === -1) {
14093
- return {
14094
- total: parsed[1] || 0,
14095
- offset: 0,
14096
- count: 0,
14097
- hasMore: false,
14098
- refs: [],
14099
- error: `\u5757\u53C2\u7167\u6570\u91CF (${parsed[1]}) \u8D85\u8FC7\u6700\u5927\u9650\u5236 (${MAX_ENTITY_LIMIT})\uFF0C\u8BF7\u4F7F\u7528 blockName \u8FC7\u6EE4`
14100
- };
14101
- }
14102
- const refs = parsed.slice(1).map((r) => ({ blockName: r[0], handle: r[1], insertionPoint: r[2] }));
14103
- return { total, offset, count: refs.length, hasMore: offset + refs.length < total, refs };
14104
- } catch {
14105
- return { total: 0, offset: 0, count: 0, hasMore: false, refs: [] };
14106
- }
14962
+ if (blockName) filters.push(`(cons 2 "${escapeLispStr(blockName)}")`);
14963
+ const filterExpr = filters.length > 0 ? `(list ${filters.join(" ")})` : null;
14964
+ return streamEntities({
14965
+ filterExpr,
14966
+ extractFields: ["(list (cdr (assoc 2 elist)) (cdr (assoc 5 elist)) (cdr (assoc 10 elist)))"],
14967
+ itemMapper: (item) => ({ blockName: item[0], handle: item[1], insertionPoint: item[2] }),
14968
+ offset,
14969
+ limit,
14970
+ hasFilter: !!blockName,
14971
+ warnThreshold: UNFILTERED_WARN_THRESHOLD,
14972
+ overLimitMsg: "\u8BF7\u4F7F\u7528 blockName \u8FC7\u6EE4",
14973
+ resultProp: "refs"
14974
+ });
14107
14975
  }
14108
14976
  var STREAM_CHUNK_SIZE, MAX_ENTITY_LIMIT, UNFILTERED_WARN_THRESHOLD;
14109
14977
  var init_streaming = __esm({
@@ -14115,6 +14983,74 @@ var init_streaming = __esm({
14115
14983
  }
14116
14984
  });
14117
14985
 
14986
+ // src/cad-rot-helper.js
14987
+ import edge from "edge-js";
14988
+ import path14 from "path";
14989
+ import { fileURLToPath as fileURLToPath6 } from "url";
14990
+ function getRotHelper() {
14991
+ if (_rotHelper) return _rotHelper;
14992
+ const dllPath = path14.join(projectRoot, "dist", "cad-rothelper.dll");
14993
+ try {
14994
+ _rotHelper = edge.func({
14995
+ assemblyFile: dllPath,
14996
+ typeName: "CadRotHelper.RotEnumerator",
14997
+ methodName: "EnumerateAll"
14998
+ });
14999
+ return _rotHelper;
15000
+ } catch (e) {
15001
+ error(`ROT \u5E2E\u52A9\u5668\u52A0\u8F7D\u5931\u8D25: ${e.message}`);
15002
+ return null;
15003
+ }
15004
+ }
15005
+ async function discoverCadInstances() {
15006
+ const helper = getRotHelper();
15007
+ if (!helper) {
15008
+ log("ROT \u5E2E\u52A9\u5668\u4E0D\u53EF\u7528\uFF0C\u8DF3\u8FC7 CAD \u5B9E\u4F8B\u53D1\u73B0");
15009
+ return [];
15010
+ }
15011
+ return new Promise((resolve) => {
15012
+ helper(null, (err, result) => {
15013
+ if (err) {
15014
+ error(`ROT \u679A\u4E3E\u5931\u8D25: ${err.message}`);
15015
+ resolve([]);
15016
+ return;
15017
+ }
15018
+ if (!Array.isArray(result)) {
15019
+ resolve([]);
15020
+ return;
15021
+ }
15022
+ const seen = /* @__PURE__ */ new Set();
15023
+ const instances = [];
15024
+ for (const entry of result) {
15025
+ if (!entry.appName) continue;
15026
+ const pidKey = `${entry.appName}-${entry.pid}`;
15027
+ if (seen.has(pidKey)) continue;
15028
+ seen.add(pidKey);
15029
+ const key = `${entry.appName.toLowerCase()}-${entry.pid}`;
15030
+ instances.push({
15031
+ key,
15032
+ platform: entry.appName,
15033
+ pid: entry.pid,
15034
+ hwnd: entry.hwnd,
15035
+ version: entry.version,
15036
+ moniker: entry.name
15037
+ });
15038
+ }
15039
+ log(`ROT \u53D1\u73B0: ${instances.length} \u4E2A CAD \u5B9E\u4F8B (${instances.map((i) => `${i.platform}#${i.pid}`).join(", ")})`);
15040
+ resolve(instances);
15041
+ });
15042
+ });
15043
+ }
15044
+ var __filename, projectRoot, _rotHelper;
15045
+ var init_cad_rot_helper = __esm({
15046
+ "src/cad-rot-helper.js"() {
15047
+ init_logger();
15048
+ __filename = fileURLToPath6(import.meta.url);
15049
+ projectRoot = path14.dirname(path14.dirname(__filename));
15050
+ _rotHelper = null;
15051
+ }
15052
+ });
15053
+
14118
15054
  // src/cad-pool.js
14119
15055
  var POOL_STRATEGIES, CadConnectionPool, pool;
14120
15056
  var init_cad_pool = __esm({
@@ -14122,6 +15058,8 @@ var init_cad_pool = __esm({
14122
15058
  init_cad();
14123
15059
  init_logger();
14124
15060
  init_config();
15061
+ init_cad_rot_helper();
15062
+ init_resource_cache();
14125
15063
  POOL_STRATEGIES = {
14126
15064
  ROUND_ROBIN: "round-robin",
14127
15065
  LEAST_BUSY: "least-busy",
@@ -14130,9 +15068,8 @@ var init_cad_pool = __esm({
14130
15068
  CadConnectionPool = class {
14131
15069
  _instances = /* @__PURE__ */ new Map();
14132
15070
  _maxPoolSize = 3;
14133
- _activeKey = "default";
15071
+ _activeKey = null;
14134
15072
  _strategy = POOL_STRATEGIES.MANUAL;
14135
- _rrIndex = 0;
14136
15073
  _reconnectTimers = /* @__PURE__ */ new Map();
14137
15074
  _reconnectCounts = /* @__PURE__ */ new Map();
14138
15075
  _maxReconnectAttempts = 10;
@@ -14142,30 +15079,8 @@ var init_cad_pool = __esm({
14142
15079
  this._maxPoolSize = config_default.maxPoolSize || 3;
14143
15080
  }
14144
15081
  getConnection(key) {
14145
- this._ensureDefault();
14146
15082
  return this._instances.get(key)?.connection || null;
14147
15083
  }
14148
- /**
14149
- * Ensure the default entry exists. Created lazily to avoid
14150
- * calling getActiveConnection() at module import time (which
14151
- * breaks tests that mock cad.js).
14152
- */
14153
- _ensureDefault() {
14154
- if (!this._instances.has("default")) {
14155
- const defaultConn = getActiveConnection();
14156
- this._instances.set("default", {
14157
- key: "default",
14158
- connection: defaultConn,
14159
- platform: null,
14160
- version: null,
14161
- connected: false,
14162
- lastPing: Date.now(),
14163
- createdAt: Date.now(),
14164
- label: "\u9ED8\u8BA4 CAD",
14165
- autoReconnect: true
14166
- });
14167
- }
14168
- }
14169
15084
  /**
14170
15085
  * Connect to a CAD instance and add it to the pool.
14171
15086
  * @param {string} key - Instance identifier
@@ -14173,14 +15088,30 @@ var init_cad_pool = __esm({
14173
15088
  * @param {object} [options] - Additional options
14174
15089
  * @param {string} [options.label] - Human-readable label
14175
15090
  * @param {boolean} [options.autoReconnect=true] - Auto-reconnect on failure
15091
+ * @param {number} [options.pid] - Target process PID (find via ROT)
14176
15092
  * @param {boolean} [options.makeActive=true] - Set as active after connecting
14177
15093
  */
14178
15094
  async connect(key = "default", platform = null, options = {}) {
14179
- this._ensureDefault();
14180
- const { label, autoReconnect = true, makeActive = true } = options || {};
15095
+ const { label, autoReconnect = true, makeActive = true, pid } = options || {};
14181
15096
  if (this._instances.size >= this._maxPoolSize && !this._instances.has(key)) {
14182
15097
  throw new Error(`\u8FDE\u63A5\u6C60\u5DF2\u6EE1 (${this._maxPoolSize})\uFF0C\u65E0\u6CD5\u6DFB\u52A0\u65B0\u5B9E\u4F8B "${key}"`);
14183
15098
  }
15099
+ if (pid != null) {
15100
+ const instances = await discoverCadInstances();
15101
+ const match = instances.find((i) => i.pid === pid);
15102
+ if (!match) {
15103
+ error(`\u8FDE\u63A5\u6C60: \u672A\u627E\u5230 PID ${pid} \u7684 CAD \u5B9E\u4F8B`);
15104
+ return false;
15105
+ }
15106
+ platform = platform || match.platform;
15107
+ if (key === "default" || key === pid.toString()) {
15108
+ key = match.key;
15109
+ }
15110
+ if (match.moniker) {
15111
+ if (!options) options = {};
15112
+ options._moniker = match.moniker;
15113
+ }
15114
+ }
14184
15115
  let entry = this._instances.get(key);
14185
15116
  if (entry && entry.connected) {
14186
15117
  return true;
@@ -14202,7 +15133,9 @@ var init_cad_pool = __esm({
14202
15133
  }
14203
15134
  const conn = entry.connection;
14204
15135
  try {
14205
- const ok = await conn.connect(platform);
15136
+ const moniker = options._moniker || entry.moniker;
15137
+ const connectOpts = moniker ? { moniker, key } : {};
15138
+ const ok = await conn.connect(platform, connectOpts);
14206
15139
  if (ok) {
14207
15140
  entry.connected = true;
14208
15141
  entry.platform = conn.getPlatform();
@@ -14226,17 +15159,6 @@ var init_cad_pool = __esm({
14226
15159
  * Disconnect and remove an instance from the pool.
14227
15160
  */
14228
15161
  async disconnect(key) {
14229
- this._ensureDefault();
14230
- if (key === "default") {
14231
- const entry2 = this._instances.get("default");
14232
- if (entry2) {
14233
- await entry2.connection.disconnect();
14234
- entry2.connected = false;
14235
- entry2.platform = null;
14236
- entry2.version = null;
14237
- }
14238
- return true;
14239
- }
14240
15162
  const entry = this._instances.get(key);
14241
15163
  if (!entry) return false;
14242
15164
  this._clearReconnectTimer(key);
@@ -14247,7 +15169,12 @@ var init_cad_pool = __esm({
14247
15169
  }
14248
15170
  this._instances.delete(key);
14249
15171
  if (this._activeKey === key) {
14250
- this.setActive("default");
15172
+ const remaining = Array.from(this._instances.keys());
15173
+ if (remaining.length > 0) {
15174
+ this.setActive(remaining[0]);
15175
+ } else {
15176
+ this._activeKey = null;
15177
+ }
14251
15178
  }
14252
15179
  log(`\u8FDE\u63A5\u6C60: \u5B9E\u4F8B "${key}" \u5DF2\u65AD\u5F00\u5E76\u79FB\u9664`);
14253
15180
  return true;
@@ -14256,7 +15183,6 @@ var init_cad_pool = __esm({
14256
15183
  * Set the active instance. All cad.* calls will be routed here.
14257
15184
  */
14258
15185
  setActive(key) {
14259
- this._ensureDefault();
14260
15186
  const entry = this._instances.get(key);
14261
15187
  if (!entry) {
14262
15188
  error(`\u8FDE\u63A5\u6C60: \u5B9E\u4F8B "${key}" \u4E0D\u5B58\u5728`);
@@ -14264,47 +15190,88 @@ var init_cad_pool = __esm({
14264
15190
  }
14265
15191
  this._activeKey = key;
14266
15192
  setActiveConnection(entry.connection);
15193
+ setInstanceKey(key);
14267
15194
  cad.connected = entry.connected;
14268
15195
  log(`\u8FDE\u63A5\u6C60: \u5DF2\u5207\u6362\u5230\u5B9E\u4F8B "${key}"`);
15196
+ if (this._onInstanceSwitch) {
15197
+ try {
15198
+ this._onInstanceSwitch(key, entry);
15199
+ } catch (e) {
15200
+ error(`\u5207\u6362\u56DE\u8C03\u7528\u9519\u8BEF: ${e.message}`);
15201
+ }
15202
+ }
14269
15203
  return true;
14270
15204
  }
15205
+ /**
15206
+ * Register a callback invoked after every instance switch.
15207
+ * @param {function} fn - (key, entry) => void
15208
+ */
15209
+ onInstanceSwitch(fn) {
15210
+ this._onInstanceSwitch = fn;
15211
+ }
14271
15212
  /**
14272
15213
  * Discover and add all running CAD instances.
14273
- * Tries each platform to find running CAD processes.
15214
+ * Uses ROT enumeration to find all instances (including multiple of the same platform).
15215
+ * Falls back to GetActiveObject per platform when ROT is unavailable.
14274
15216
  */
14275
15217
  async discover() {
14276
- this._ensureDefault();
14277
- const platforms = ["AutoCAD", "ZWCAD", "GStarCAD", "BricsCAD"];
14278
- let foundCount = 0;
14279
- for (const platform of platforms) {
14280
- const key = platform.toLowerCase();
15218
+ const resultEntries = [];
15219
+ const rotInstances = await discoverCadInstances();
15220
+ const rotHadResults = rotInstances.length > 0;
15221
+ for (const inst of rotInstances) {
15222
+ const key = inst.key;
14281
15223
  if (this._instances.has(key) && this._instances.get(key).connected) {
14282
- foundCount++;
15224
+ resultEntries.push(this._instances.get(key));
14283
15225
  continue;
14284
15226
  }
14285
- try {
14286
- const tempConn = createCadConnection();
14287
- const ok = await tempConn.connect(platform);
14288
- if (ok) {
14289
- const label = `${platform} ${tempConn.getVersion() || ""}`.trim();
14290
- this._instances.set(key, {
14291
- key,
14292
- connection: tempConn,
14293
- platform: tempConn.getPlatform(),
14294
- version: tempConn.getVersion(),
14295
- connected: true,
14296
- lastPing: Date.now(),
14297
- createdAt: Date.now(),
14298
- label,
14299
- autoReconnect: true
14300
- });
14301
- foundCount++;
14302
- log(`\u8FDE\u63A5\u6C60: \u53D1\u73B0 ${label}`);
15227
+ this._instances.set(key, {
15228
+ key,
15229
+ connection: null,
15230
+ platform: inst.platform,
15231
+ pid: inst.pid,
15232
+ version: inst.version,
15233
+ moniker: inst.moniker,
15234
+ connected: false,
15235
+ lastPing: Date.now(),
15236
+ createdAt: Date.now(),
15237
+ label: `${inst.platform} #${inst.pid} ${inst.version || ""}`.trim(),
15238
+ autoReconnect: false
15239
+ });
15240
+ log(`\u8FDE\u63A5\u6C60: \u53D1\u73B0 ${inst.platform} \u5B9E\u4F8B PID=${inst.pid}`);
15241
+ resultEntries.push(this._instances.get(key));
15242
+ }
15243
+ if (!rotHadResults) {
15244
+ const connectWithTimeout = (conn, platform, ms = 4e3) => Promise.race([
15245
+ conn.connect(platform),
15246
+ new Promise((_, reject) => setTimeout(() => reject(new Error("timeout")), ms))
15247
+ ]);
15248
+ for (const platform of ["AutoCAD", "ZWCAD", "GStarCAD", "BricsCAD"]) {
15249
+ const key = platform.toLowerCase();
15250
+ if (this._instances.has(key) && this._instances.get(key).connected) continue;
15251
+ try {
15252
+ const tempConn = createCadConnection();
15253
+ const ok = await connectWithTimeout(tempConn, platform);
15254
+ if (ok) {
15255
+ const label = `${platform} ${tempConn.getVersion() || ""}`.trim();
15256
+ this._instances.set(key, {
15257
+ key,
15258
+ connection: tempConn,
15259
+ platform: tempConn.getPlatform(),
15260
+ version: tempConn.getVersion(),
15261
+ connected: true,
15262
+ lastPing: Date.now(),
15263
+ createdAt: Date.now(),
15264
+ label,
15265
+ autoReconnect: true
15266
+ });
15267
+ log(`\u8FDE\u63A5\u6C60: \u53D1\u73B0 ${label}`);
15268
+ resultEntries.push(this._instances.get(key));
15269
+ }
15270
+ } catch (e) {
14303
15271
  }
14304
- } catch (e) {
14305
15272
  }
14306
15273
  }
14307
- return Array.from(this._instances.values()).map((e) => ({
15274
+ return resultEntries.map((e) => ({
14308
15275
  key: e.key,
14309
15276
  connected: e.connected,
14310
15277
  platform: e.platform,
@@ -14317,7 +15284,6 @@ var init_cad_pool = __esm({
14317
15284
  * Health check for all instances.
14318
15285
  */
14319
15286
  async healthCheck() {
14320
- this._ensureDefault();
14321
15287
  const results = [];
14322
15288
  for (const [key, entry] of this._instances) {
14323
15289
  try {
@@ -14363,14 +15329,21 @@ var init_cad_pool = __esm({
14363
15329
  * Get the active instance.
14364
15330
  */
14365
15331
  getActive() {
14366
- this._ensureDefault();
14367
15332
  return this._instances.get(this._activeKey) || null;
14368
15333
  }
14369
15334
  /**
14370
15335
  * Get stats for all instances.
14371
15336
  */
14372
15337
  getStats() {
14373
- this._ensureDefault();
15338
+ if (this._instances.size === 0) {
15339
+ return {
15340
+ total: 0,
15341
+ maxPoolSize: this._maxPoolSize,
15342
+ active: null,
15343
+ strategy: this._strategy,
15344
+ instances: []
15345
+ };
15346
+ }
14374
15347
  return {
14375
15348
  total: this._instances.size,
14376
15349
  maxPoolSize: this._maxPoolSize,
@@ -14382,7 +15355,7 @@ var init_cad_pool = __esm({
14382
15355
  platform: i.platform,
14383
15356
  version: i.version,
14384
15357
  label: i.label,
14385
- queueSize: i.connection._getQueueSize ? i.connection._getQueueSize() : 0,
15358
+ queueSize: i.connection?._getQueueSize ? i.connection._getQueueSize() : 0,
14386
15359
  lastPingAgo: Date.now() - (i.lastPing || 0),
14387
15360
  uptime: i.connected ? Math.round((Date.now() - i.createdAt) / 1e3) : 0
14388
15361
  }))
@@ -14392,7 +15365,7 @@ var init_cad_pool = __esm({
14392
15365
  * List all instances.
14393
15366
  */
14394
15367
  listInstances() {
14395
- this._ensureDefault();
15368
+ if (this._instances.size === 0) return [];
14396
15369
  return Array.from(this._instances.values()).map((i) => ({
14397
15370
  key: i.key,
14398
15371
  connected: i.connected,
@@ -14404,7 +15377,7 @@ var init_cad_pool = __esm({
14404
15377
  }));
14405
15378
  }
14406
15379
  /**
14407
- * Disconnect all instances.
15380
+ * Disconnect all instances in parallel.
14408
15381
  */
14409
15382
  async disconnectAll() {
14410
15383
  this.stopHealthCheck();
@@ -14413,27 +15386,16 @@ var init_cad_pool = __esm({
14413
15386
  }
14414
15387
  this._reconnectTimers.clear();
14415
15388
  this._reconnectCounts.clear();
14416
- for (const [key, entry] of this._instances) {
15389
+ const entries = Array.from(this._instances.entries());
15390
+ this._instances.clear();
15391
+ this._activeKey = null;
15392
+ await Promise.allSettled(entries.map(async ([key, entry]) => {
14417
15393
  try {
14418
15394
  await entry.connection.disconnect();
14419
15395
  } catch (e) {
14420
15396
  error(`\u8FDE\u63A5\u6C60: \u65AD\u5F00 "${key}" \u51FA\u9519: ${e.message}`);
14421
15397
  }
14422
- }
14423
- this._instances.clear();
14424
- this._activeKey = "default";
14425
- const defaultConn = getActiveConnection();
14426
- this._instances.set("default", {
14427
- key: "default",
14428
- connection: defaultConn,
14429
- platform: null,
14430
- version: null,
14431
- connected: false,
14432
- lastPing: Date.now(),
14433
- createdAt: Date.now(),
14434
- label: "\u9ED8\u8BA4 CAD",
14435
- autoReconnect: true
14436
- });
15398
+ }));
14437
15399
  log("\u8FDE\u63A5\u6C60: \u6240\u6709\u5B9E\u4F8B\u5DF2\u65AD\u5F00");
14438
15400
  }
14439
15401
  // --- Internal ---
@@ -14493,7 +15455,7 @@ var init_cad_pool = __esm({
14493
15455
  });
14494
15456
 
14495
15457
  // src/trigger-engine.js
14496
- function escapeLispStr(v) {
15458
+ function escapeLispStr2(v) {
14497
15459
  if (typeof v !== "string") return v;
14498
15460
  return v.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
14499
15461
  }
@@ -14501,10 +15463,10 @@ function buildFilterExpr(filter) {
14501
15463
  if (!filter || typeof filter !== "object") return "nil";
14502
15464
  const parts = [];
14503
15465
  if (filter.type) {
14504
- parts.push(`(cons 0 "${escapeLispStr(filter.type)}")`);
15466
+ parts.push(`(cons 0 "${escapeLispStr2(filter.type)}")`);
14505
15467
  }
14506
15468
  if (filter.layer) {
14507
- parts.push(`(cons 8 "${escapeLispStr(filter.layer)}")`);
15469
+ parts.push(`(cons 8 "${escapeLispStr2(filter.layer)}")`);
14508
15470
  }
14509
15471
  if (parts.length === 0) return "nil";
14510
15472
  return `(list ${parts.join(" ")})`;
@@ -14553,7 +15515,7 @@ async function evaluateWatch(watch) {
14553
15515
  return { matched, data: { count } };
14554
15516
  }
14555
15517
  case "layer_exists": {
14556
- const lName = escapeLispStr(watch.layerName || "");
15518
+ const lName = escapeLispStr2(watch.layerName || "");
14557
15519
  result = await cad.sendCommandWithResult(
14558
15520
  `(progn (if (tblsearch "LAYER" "${lName}") (list 1) (list 0)))`
14559
15521
  );
@@ -14561,7 +15523,7 @@ async function evaluateWatch(watch) {
14561
15523
  return { matched: exists, data: { exists, layerName: watch.layerName } };
14562
15524
  }
14563
15525
  case "doc_name": {
14564
- const pattern = escapeLispStr(watch.pattern || "*");
15526
+ const pattern = escapeLispStr2(watch.pattern || "*");
14565
15527
  result = await cad.sendCommandWithResult(
14566
15528
  `(progn (setq _dn (getvar "dwgname")) (setq _dm (if (wcmatch _dn "${pattern}") 1 0)) (list _dm _dn))`
14567
15529
  );
@@ -14571,14 +15533,14 @@ async function evaluateWatch(watch) {
14571
15533
  return { matched, data: { matched: Boolean(matched), name } };
14572
15534
  }
14573
15535
  case "doc_path": {
14574
- const pattern = escapeLispStr(watch.pattern || "*");
15536
+ const pattern = escapeLispStr2(watch.pattern || "*");
14575
15537
  result = await cad.sendCommandWithResult(
14576
15538
  `(progn (setq _dp (getvar "dwgprefix")) (setq _dm (if (wcmatch _dp "${pattern}") 1 0)) (list _dm _dp))`
14577
15539
  );
14578
15540
  const arr = parseLispList2(result) || [];
14579
15541
  const matched = Number(arr[0]) !== 0;
14580
- const path20 = String(arr[1] || "");
14581
- return { matched, data: { matched: Boolean(matched), path: path20 } };
15542
+ const path21 = String(arr[1] || "");
15543
+ return { matched, data: { matched: Boolean(matched), path: path21 } };
14582
15544
  }
14583
15545
  case "entity_selected": {
14584
15546
  result = await cad.sendCommandWithResult(
@@ -14748,53 +15710,6 @@ function stripStringsAndComments(code) {
14748
15710
  const withoutStrings = code.replace(/"([^"\\]*(?:\\.[^"\\]*)*)"/g, "");
14749
15711
  return withoutStrings.replace(/;.*$/gm, "");
14750
15712
  }
14751
- var THREAT_BLOCK, THREAT_WARN, THREAT_ALLOW, CVT, SECURITY_RULES, _cachedLevel;
14752
- var init_lisp_security = __esm({
14753
- "src/lisp-security.js"() {
14754
- init_logger();
14755
- init_config();
14756
- THREAT_BLOCK = "block";
14757
- THREAT_WARN = "warn";
14758
- THREAT_ALLOW = "allow";
14759
- CVT = {
14760
- strict: THREAT_BLOCK,
14761
- standard: THREAT_WARN,
14762
- relaxed: THREAT_ALLOW
14763
- };
14764
- SECURITY_RULES = [
14765
- { re: /\bstartapp\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 startapp \u51FD\u6570" },
14766
- { re: /\bvl-bt\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 vl-bt \u51FD\u6570" },
14767
- { re: /\bvlax-import-type-library\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u5BFC\u5165\u7C7B\u578B\u5E93" },
14768
- { re: /\bdos_command_string\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 dos_command_string" },
14769
- { re: /\beval\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 eval \u51FD\u6570" },
14770
- { re: /\bcommand\s*["\s]*[_(]?\s*["']?\.?\s*_?(?:shell|quit|exit|close|open|new|recover|audit|purge|wblock|export)/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528\u5371\u9669 CAD \u547D\u4EE4" },
14771
- { re: /\bload\s*[(\s"]/i, baseSeverity: THREAT_BLOCK, msg: "load \u51FD\u6570\u53EF\u80FD\u52A0\u8F7D\u5916\u90E8\u4EE3\u7801" },
14772
- { re: /\b(?:read-line|read-char|write-line|write-char)\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u6587\u4EF6 I/O \u64CD\u4F5C\u53EF\u80FD\u5F71\u54CD\u7CFB\u7EDF" },
14773
- { re: /\bvl-file-copy\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u6587\u4EF6\u590D\u5236" },
14774
- { re: /\bvl-file-delete\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u6587\u4EF6\u5220\u9664" },
14775
- { re: /\bvl-file-rename\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u6587\u4EF6\u91CD\u547D\u540D" },
14776
- { re: /\bvl-registry-write[\s\)]/i, baseSeverity: THREAT_BLOCK, msg: "vl-registry-write \u4F1A\u4FEE\u6539\u6CE8\u518C\u8868" },
14777
- { re: /\bvl-exit-with-value\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 vl-exit-with-value" },
14778
- { re: /\bvl-exit-with-error\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 vl-exit-with-error" },
14779
- { re: /\bvlax-add-cmd\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u6CE8\u518C\u65B0\u547D\u4EE4" },
14780
- { re: /\bvla-Delete\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u76F4\u63A5\u5220\u9664 vla \u5BF9\u8C61" },
14781
- { re: /\bvla-Save\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u76F4\u63A5\u4FDD\u5B58\u6587\u6863" },
14782
- { re: /\bdirectory\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 directory \u51FD\u6570" },
14783
- { re: /\bvla-deletefile\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u5220\u9664\u6587\u4EF6" },
14784
- { re: /\bvla-copyfile\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u590D\u5236\u6587\u4EF6" },
14785
- { re: /\bvla-movefile\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u79FB\u52A8\u6587\u4EF6" }
14786
- ];
14787
- _cachedLevel = null;
14788
- try {
14789
- onConfigChange(() => {
14790
- _cachedLevel = null;
14791
- });
14792
- } catch (_) {
14793
- }
14794
- }
14795
- });
14796
-
14797
- // src/lisp-sandbox.js
14798
15713
  function getSessionAllowlist(sessionId) {
14799
15714
  if (!sessionAllowlist.has(sessionId)) {
14800
15715
  sessionAllowlist.set(sessionId, /* @__PURE__ */ new Set());
@@ -14838,66 +15753,103 @@ function validateLispCode(code, sessionId = null) {
14838
15753
  summary: blocked ? `\u5B89\u5168\u9650\u5236: ${results.filter((r) => r.blocked).map((r) => r.message).join("; ")}` : "\u901A\u8FC7"
14839
15754
  };
14840
15755
  }
14841
- var RULES2, sessionAllowlist;
14842
- var init_lisp_sandbox = __esm({
14843
- "src/lisp-sandbox.js"() {
15756
+ var THREAT_BLOCK, THREAT_WARN, THREAT_ALLOW, CVT, SECURITY_RULES, _cachedLevel, RULES2, sessionAllowlist;
15757
+ var init_lisp_security = __esm({
15758
+ "src/lisp-security.js"() {
14844
15759
  init_logger();
14845
15760
  init_config();
14846
- init_lisp_security();
15761
+ THREAT_BLOCK = "block";
15762
+ THREAT_WARN = "warn";
15763
+ THREAT_ALLOW = "allow";
15764
+ CVT = {
15765
+ strict: THREAT_BLOCK,
15766
+ standard: THREAT_WARN,
15767
+ relaxed: THREAT_ALLOW
15768
+ };
15769
+ SECURITY_RULES = [
15770
+ { re: /\bstartapp\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 startapp \u51FD\u6570" },
15771
+ { re: /\bvl-bt\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 vl-bt \u51FD\u6570" },
15772
+ { re: /\bvlax-import-type-library\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u5BFC\u5165\u7C7B\u578B\u5E93" },
15773
+ { re: /\bdos_command_string\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 dos_command_string" },
15774
+ { re: /\beval\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 eval \u51FD\u6570" },
15775
+ { re: /\bcommand\s*["\s]*[_(]?\s*["']?\.?\s*_?(?:shell|quit|exit|close|open|new|recover|audit|purge|wblock|export)/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528\u5371\u9669 CAD \u547D\u4EE4" },
15776
+ { re: /\bload\s*[(\s"]/i, baseSeverity: THREAT_BLOCK, msg: "load \u51FD\u6570\u53EF\u80FD\u52A0\u8F7D\u5916\u90E8\u4EE3\u7801" },
15777
+ { re: /\b(?:read-line|read-char|write-line|write-char)\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u6587\u4EF6 I/O \u64CD\u4F5C\u53EF\u80FD\u5F71\u54CD\u7CFB\u7EDF" },
15778
+ { re: /\bvl-file-copy\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u6587\u4EF6\u590D\u5236" },
15779
+ { re: /\bvl-file-delete\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u6587\u4EF6\u5220\u9664" },
15780
+ { re: /\bvl-file-rename\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u6587\u4EF6\u91CD\u547D\u540D" },
15781
+ { re: /\bvl-registry-write[\s\)]/i, baseSeverity: THREAT_BLOCK, msg: "vl-registry-write \u4F1A\u4FEE\u6539\u6CE8\u518C\u8868" },
15782
+ { re: /\bvl-exit-with-value\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 vl-exit-with-value" },
15783
+ { re: /\bvl-exit-with-error\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 vl-exit-with-error" },
15784
+ { re: /\bvlax-add-cmd\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u6CE8\u518C\u65B0\u547D\u4EE4" },
15785
+ { re: /\bvla-Delete\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u76F4\u63A5\u5220\u9664 vla \u5BF9\u8C61" },
15786
+ { re: /\bvla-Save\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u76F4\u63A5\u4FDD\u5B58\u6587\u6863" },
15787
+ { re: /\bdirectory\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 directory \u51FD\u6570" },
15788
+ { re: /\bvla-deletefile\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u5220\u9664\u6587\u4EF6" },
15789
+ { re: /\bvla-copyfile\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u590D\u5236\u6587\u4EF6" },
15790
+ { re: /\bvla-movefile\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u79FB\u52A8\u6587\u4EF6" }
15791
+ ];
15792
+ _cachedLevel = null;
15793
+ try {
15794
+ onConfigChange(() => {
15795
+ _cachedLevel = null;
15796
+ });
15797
+ } catch (_) {
15798
+ }
14847
15799
  RULES2 = SECURITY_RULES;
14848
15800
  sessionAllowlist = /* @__PURE__ */ new Map();
14849
15801
  }
14850
15802
  });
14851
15803
 
14852
15804
  // src/repl-engine.js
14853
- import { randomUUID as randomUUID4 } from "crypto";
15805
+ import { randomUUID as randomUUID5 } from "crypto";
14854
15806
  function ensureCleanup2() {
14855
15807
  if (cleanupTimer2) return;
14856
15808
  cleanupTimer2 = setInterval(() => {
14857
15809
  const now = Date.now();
14858
- for (const [id, session] of sessions) {
15810
+ for (const [id, session] of sessions2) {
14859
15811
  if (now - session.createdAt > SESSION_TTL) {
14860
- sessions.delete(id);
15812
+ sessions2.delete(id);
14861
15813
  log(`REPL session ${id} expired (TTL)`);
14862
15814
  }
14863
15815
  }
14864
15816
  }, CLEANUP_INTERVAL3);
14865
15817
  cleanupTimer2.unref();
14866
15818
  }
14867
- function createSession() {
14868
- if (sessions.size >= MAX_SESSIONS2) {
15819
+ function createSession2() {
15820
+ if (sessions2.size >= MAX_SESSIONS3) {
14869
15821
  let oldest = null;
14870
- for (const [id, s] of sessions) {
15822
+ for (const [id, s] of sessions2) {
14871
15823
  if (!oldest || s.lastActivity < oldest.lastActivity) oldest = { id, s };
14872
15824
  }
14873
15825
  if (oldest) {
14874
- sessions.delete(oldest.id);
15826
+ sessions2.delete(oldest.id);
14875
15827
  log(`REPL session ${oldest.id} evicted (max sessions reached)`);
14876
15828
  }
14877
15829
  }
14878
15830
  ensureCleanup2();
14879
- const sessionId = randomUUID4();
15831
+ const sessionId = randomUUID5();
14880
15832
  const session = new ReplSession(sessionId);
14881
- sessions.set(sessionId, session);
15833
+ sessions2.set(sessionId, session);
14882
15834
  return sessionId;
14883
15835
  }
14884
15836
  async function executeInSession(sessionId, code) {
14885
- let session = sessions.get(sessionId);
15837
+ let session = sessions2.get(sessionId);
14886
15838
  if (!session) {
14887
- sessionId = createSession();
14888
- session = sessions.get(sessionId);
15839
+ sessionId = createSession2();
15840
+ session = sessions2.get(sessionId);
14889
15841
  }
14890
15842
  const result = await session.execute(code);
14891
15843
  return { sessionId, result, history: session.history };
14892
15844
  }
14893
15845
  function getHistory(sessionId) {
14894
- const session = sessions.get(sessionId);
15846
+ const session = sessions2.get(sessionId);
14895
15847
  if (!session) return null;
14896
15848
  return session.history;
14897
15849
  }
14898
- function listSessions() {
15850
+ function listSessions2() {
14899
15851
  const result = [];
14900
- for (const [id, session] of sessions) {
15852
+ for (const [id, session] of sessions2) {
14901
15853
  result.push({
14902
15854
  sessionId: id,
14903
15855
  historyCount: session.history.length,
@@ -14907,20 +15859,20 @@ function listSessions() {
14907
15859
  }
14908
15860
  return result;
14909
15861
  }
14910
- function closeSession(id) {
14911
- const session = sessions.get(id);
15862
+ function closeSession2(id) {
15863
+ const session = sessions2.get(id);
14912
15864
  if (!session) return false;
14913
- sessions.delete(id);
15865
+ sessions2.delete(id);
14914
15866
  return true;
14915
15867
  }
14916
- var sessions, MAX_SESSIONS2, SESSION_TTL, CLEANUP_INTERVAL3, cleanupTimer2, SETQ_RE, ReplSession;
15868
+ var sessions2, MAX_SESSIONS3, SESSION_TTL, CLEANUP_INTERVAL3, cleanupTimer2, SETQ_RE, ReplSession;
14917
15869
  var init_repl_engine = __esm({
14918
15870
  "src/repl-engine.js"() {
14919
15871
  init_cad();
14920
15872
  init_logger();
14921
15873
  init_lint_analyzer();
14922
- sessions = /* @__PURE__ */ new Map();
14923
- MAX_SESSIONS2 = 50;
15874
+ sessions2 = /* @__PURE__ */ new Map();
15875
+ MAX_SESSIONS3 = 50;
14924
15876
  SESSION_TTL = 30 * 60 * 1e3;
14925
15877
  CLEANUP_INTERVAL3 = 5 * 60 * 1e3;
14926
15878
  cleanupTimer2 = null;
@@ -14964,13 +15916,13 @@ ${analysis.errors.map((e) => ` L${e.line}: [${e.rule}] ${e.message}`).join("\n"
14964
15916
  });
14965
15917
 
14966
15918
  // src/webhook-engine.js
14967
- import fs9 from "fs";
14968
- import path13 from "path";
14969
- import os7 from "os";
15919
+ import fs10 from "fs";
15920
+ import path15 from "path";
15921
+ import os8 from "os";
14970
15922
  function loadWebhooks() {
14971
15923
  try {
14972
- if (fs9.existsSync(WEBHOOKS_FILE)) {
14973
- webhooks = JSON.parse(fs9.readFileSync(WEBHOOKS_FILE, "utf-8"));
15924
+ if (fs10.existsSync(WEBHOOKS_FILE)) {
15925
+ webhooks = JSON.parse(fs10.readFileSync(WEBHOOKS_FILE, "utf-8"));
14974
15926
  }
14975
15927
  } catch (e) {
14976
15928
  error(`Webhook load error: ${e.message}`);
@@ -14978,8 +15930,8 @@ function loadWebhooks() {
14978
15930
  }
14979
15931
  function saveWebhooks() {
14980
15932
  try {
14981
- ensureDir(path13.dirname(WEBHOOKS_FILE));
14982
- fs9.writeFileSync(WEBHOOKS_FILE, JSON.stringify(webhooks, null, 2), "utf-8");
15933
+ ensureDir(path15.dirname(WEBHOOKS_FILE));
15934
+ fs10.writeFileSync(WEBHOOKS_FILE, JSON.stringify(webhooks, null, 2), "utf-8");
14983
15935
  } catch (e) {
14984
15936
  error(`Webhook save error: ${e.message}`);
14985
15937
  }
@@ -15036,10 +15988,10 @@ function listWebhooks() {
15036
15988
  }
15037
15989
  function getWebhookLog(id) {
15038
15990
  const sanitized = String(id).replace(/[^a-zA-Z0-9_-]/g, "_");
15039
- const logPath = path13.join(os7.homedir(), ".atlisp", `webhook-${sanitized}.log`);
15991
+ const logPath = path15.join(os8.homedir(), ".atlisp", `webhook-${sanitized}.log`);
15040
15992
  try {
15041
- if (!fs9.existsSync(logPath)) return [];
15042
- return fs9.readFileSync(logPath, "utf-8").split("\n").filter(Boolean).map((l) => JSON.parse(l));
15993
+ if (!fs10.existsSync(logPath)) return [];
15994
+ return fs10.readFileSync(logPath, "utf-8").split("\n").filter(Boolean).map((l) => JSON.parse(l));
15043
15995
  } catch (e) {
15044
15996
  error(`Webhook log read error [${sanitized}]: ${e.message}`);
15045
15997
  return [];
@@ -15050,30 +16002,30 @@ var init_webhook_engine = __esm({
15050
16002
  "src/webhook-engine.js"() {
15051
16003
  init_logger();
15052
16004
  init_handler_utils();
15053
- WEBHOOKS_FILE = path13.join(os7.homedir(), ".atlisp", "webhooks.json");
16005
+ WEBHOOKS_FILE = path15.join(os8.homedir(), ".atlisp", "webhooks.json");
15054
16006
  webhooks = [];
15055
16007
  loadWebhooks();
15056
16008
  }
15057
16009
  });
15058
16010
 
15059
16011
  // src/audit-log.js
15060
- import fs10 from "fs";
15061
- import path14 from "path";
15062
- import os8 from "os";
16012
+ import fs11 from "fs";
16013
+ import path16 from "path";
16014
+ import os9 from "os";
15063
16015
  import zlib from "zlib";
15064
16016
  function rotateIfNeeded() {
15065
16017
  try {
15066
- if (fs10.existsSync(AUDIT_FILE) && fs10.statSync(AUDIT_FILE).size > MAX_LOG_SIZE2) {
16018
+ if (fs11.existsSync(AUDIT_FILE) && fs11.statSync(AUDIT_FILE).size > MAX_LOG_SIZE2) {
15067
16019
  const rotated = AUDIT_FILE.replace(".log", `.${Date.now()}.log`);
15068
- fs10.renameSync(AUDIT_FILE, rotated);
16020
+ fs11.renameSync(AUDIT_FILE, rotated);
15069
16021
  try {
15070
- const compressed = zlib.gzipSync(fs10.readFileSync(rotated));
15071
- fs10.writeFileSync(rotated + ".gz", compressed);
15072
- fs10.unlinkSync(rotated);
16022
+ const compressed = zlib.gzipSync(fs11.readFileSync(rotated));
16023
+ fs11.writeFileSync(rotated + ".gz", compressed);
16024
+ fs11.unlinkSync(rotated);
15073
16025
  } catch (e) {
15074
16026
  error(`Audit log compression failed, keeping raw file: ${e.message}`);
15075
16027
  try {
15076
- fs10.unlinkSync(rotated + ".gz");
16028
+ fs11.unlinkSync(rotated + ".gz");
15077
16029
  } catch {
15078
16030
  }
15079
16031
  }
@@ -15097,15 +16049,15 @@ function writeAuditEntry(entry) {
15097
16049
  user: entry.user || "",
15098
16050
  role: entry.role || ""
15099
16051
  }) + "\n";
15100
- fs10.appendFileSync(AUDIT_FILE, line, "utf-8");
16052
+ fs11.appendFileSync(AUDIT_FILE, line, "utf-8");
15101
16053
  } catch (e) {
15102
16054
  error(`Audit log write error: ${e.message}`);
15103
16055
  }
15104
16056
  }
15105
16057
  function queryAuditLog(filters = {}) {
15106
16058
  try {
15107
- if (!fs10.existsSync(AUDIT_FILE)) return { total: 0, offset: 0, count: 0, entries: [] };
15108
- const content = fs10.readFileSync(AUDIT_FILE, "utf-8");
16059
+ if (!fs11.existsSync(AUDIT_FILE)) return { total: 0, offset: 0, count: 0, entries: [] };
16060
+ const content = fs11.readFileSync(AUDIT_FILE, "utf-8");
15109
16061
  const lines = content.trim().split("\n").filter(Boolean);
15110
16062
  let entries = lines.map((l) => {
15111
16063
  try {
@@ -15137,8 +16089,8 @@ function queryAuditLog(filters = {}) {
15137
16089
  }
15138
16090
  function getAuditStats() {
15139
16091
  try {
15140
- if (!fs10.existsSync(AUDIT_FILE)) return { totalEntries: 0, tools: {}, errors: 0 };
15141
- const content = fs10.readFileSync(AUDIT_FILE, "utf-8");
16092
+ if (!fs11.existsSync(AUDIT_FILE)) return { totalEntries: 0, tools: {}, errors: 0 };
16093
+ const content = fs11.readFileSync(AUDIT_FILE, "utf-8");
15142
16094
  const lines = content.trim().split("\n").filter(Boolean);
15143
16095
  const toolCounts = {};
15144
16096
  let errors = 0;
@@ -15154,7 +16106,7 @@ function getAuditStats() {
15154
16106
  totalEntries: lines.length,
15155
16107
  tools: toolCounts,
15156
16108
  errors,
15157
- fileSize: fs10.existsSync(AUDIT_FILE) ? fs10.statSync(AUDIT_FILE).size : 0,
16109
+ fileSize: fs11.existsSync(AUDIT_FILE) ? fs11.statSync(AUDIT_FILE).size : 0,
15158
16110
  filePath: AUDIT_FILE
15159
16111
  };
15160
16112
  } catch (e) {
@@ -15166,8 +16118,8 @@ var init_audit_log = __esm({
15166
16118
  "src/audit-log.js"() {
15167
16119
  init_logger();
15168
16120
  init_handler_utils();
15169
- AUDIT_DIR = path14.join(os8.homedir(), "@lisp", "logs");
15170
- AUDIT_FILE = path14.join(AUDIT_DIR, "audit.log");
16121
+ AUDIT_DIR = path16.join(os9.homedir(), "@lisp", "logs");
16122
+ AUDIT_FILE = path16.join(AUDIT_DIR, "audit.log");
15171
16123
  MAX_LOG_SIZE2 = 10 * 1024 * 1024;
15172
16124
  MAX_LOG_AGE = 90 * 24 * 60 * 60 * 1e3;
15173
16125
  }
@@ -15201,14 +16153,17 @@ var init_agent_context = __esm({
15201
16153
  });
15202
16154
 
15203
16155
  // src/tool-validators.js
15204
- function validateProps(args, props, required, path20 = "") {
16156
+ function validationError(msg) {
16157
+ return new MCPError(ERROR_CODES.INVALID_ARGS, msg);
16158
+ }
16159
+ function validateProps(args, props, required, path21 = "") {
15205
16160
  for (const [key, prop] of Object.entries(props)) {
15206
- const fullKey = path20 ? `${path20}.${key}` : key;
16161
+ const fullKey = path21 ? `${path21}.${key}` : key;
15207
16162
  const isOptional = !required.includes(key);
15208
16163
  const val = args[key];
15209
16164
  if (val === void 0 || val === null) {
15210
16165
  if (isOptional) continue;
15211
- throw new Error(`Missing required argument: ${fullKey}`);
16166
+ throw validationError(`Missing required argument: ${fullKey}`);
15212
16167
  }
15213
16168
  if (prop.type === "object" && prop.properties && typeof val === "object" && !Array.isArray(val)) {
15214
16169
  const subRequired = prop.required || [];
@@ -15223,46 +16178,46 @@ function validateProps(args, props, required, path20 = "") {
15223
16178
  const actualType = typeof val;
15224
16179
  const expectedType = prop.type || "string";
15225
16180
  if (expectedType === "string" && actualType !== "string" && actualType !== "number") {
15226
- throw new Error(`Invalid type for argument ${fullKey}: expected string, got ${actualType}`);
16181
+ throw validationError(`Invalid type for argument ${fullKey}: expected string, got ${actualType}`);
15227
16182
  }
15228
16183
  if ((expectedType === "object" || expectedType === "array") && (actualType !== "object" || val === null)) {
15229
- throw new Error(`Invalid type for argument ${fullKey}: expected ${expectedType}, got ${actualType}`);
16184
+ throw validationError(`Invalid type for argument ${fullKey}: expected ${expectedType}, got ${actualType}`);
15230
16185
  }
15231
16186
  if (expectedType === "array" && !Array.isArray(val)) {
15232
- throw new Error(`Invalid type for argument ${fullKey}: expected array, got ${actualType}`);
16187
+ throw validationError(`Invalid type for argument ${fullKey}: expected array, got ${actualType}`);
15233
16188
  }
15234
16189
  if (expectedType === "object" && Array.isArray(val)) {
15235
- throw new Error(`Invalid type for argument ${fullKey}: expected object, got array`);
16190
+ throw validationError(`Invalid type for argument ${fullKey}: expected object, got array`);
15236
16191
  }
15237
16192
  if ((expectedType === "number" || expectedType === "integer") && actualType !== "number") {
15238
16193
  if (expectedType === "integer" && !Number.isInteger(val)) {
15239
- throw new Error(`Invalid type for argument ${fullKey}: expected integer, got ${actualType}`);
16194
+ throw validationError(`Invalid type for argument ${fullKey}: expected integer, got ${actualType}`);
15240
16195
  }
15241
16196
  if (expectedType === "number") {
15242
- throw new Error(`Invalid type for argument ${fullKey}: expected number, got ${actualType}`);
16197
+ throw validationError(`Invalid type for argument ${fullKey}: expected number, got ${actualType}`);
15243
16198
  }
15244
16199
  }
15245
16200
  if (expectedType === "boolean" && actualType !== "boolean") {
15246
- throw new Error(`Invalid type for argument ${fullKey}: expected boolean, got ${actualType}`);
16201
+ throw validationError(`Invalid type for argument ${fullKey}: expected boolean, got ${actualType}`);
15247
16202
  }
15248
16203
  if (prop.enum && !prop.enum.includes(val)) {
15249
- throw new Error(`Invalid value for argument ${fullKey}: must be one of ${prop.enum.join(", ")}`);
16204
+ throw validationError(`Invalid value for argument ${fullKey}: must be one of ${prop.enum.join(", ")}`);
15250
16205
  }
15251
16206
  if (expectedType === "string" && typeof prop.maxLength === "number" && String(val).length > prop.maxLength) {
15252
- throw new Error(`Argument ${fullKey} exceeds max length of ${prop.maxLength}`);
16207
+ throw validationError(`Argument ${fullKey} exceeds max length of ${prop.maxLength}`);
15253
16208
  }
15254
16209
  if ((expectedType === "number" || expectedType === "integer") && prop.minimum != null && val < prop.minimum) {
15255
- throw new Error(`Argument ${fullKey} must be >= ${prop.minimum}`);
16210
+ throw validationError(`Argument ${fullKey} must be >= ${prop.minimum}`);
15256
16211
  }
15257
16212
  if ((expectedType === "number" || expectedType === "integer") && prop.maximum != null && val > prop.maximum) {
15258
- throw new Error(`Argument ${fullKey} must be <= ${prop.maximum}`);
16213
+ throw validationError(`Argument ${fullKey} must be <= ${prop.maximum}`);
15259
16214
  }
15260
16215
  }
15261
16216
  }
15262
16217
  function validateToolArgs(name, args) {
15263
16218
  const toolDef = toolsByName.get(name);
15264
16219
  if (!toolDef) {
15265
- throw new Error(`Unknown tool: ${name}`);
16220
+ throw validationError(`Unknown tool: ${name}`);
15266
16221
  }
15267
16222
  const schema = toolDef.inputSchema;
15268
16223
  if (!schema || !schema.properties) return true;
@@ -15270,7 +16225,7 @@ function validateToolArgs(name, args) {
15270
16225
  const required = schema.required || [];
15271
16226
  for (const req of required) {
15272
16227
  if (!(req in (args || {}))) {
15273
- throw new Error(`Missing required argument: ${req}`);
16228
+ throw validationError(`Missing required argument: ${req}`);
15274
16229
  }
15275
16230
  }
15276
16231
  validateProps(args || {}, props, required);
@@ -15279,6 +16234,7 @@ function validateToolArgs(name, args) {
15279
16234
  var init_tool_validators = __esm({
15280
16235
  "src/tool-validators.js"() {
15281
16236
  init_tools();
16237
+ init_errors();
15282
16238
  }
15283
16239
  });
15284
16240
 
@@ -15389,7 +16345,7 @@ function registerTool(toolDef, handler) {
15389
16345
  }
15390
16346
  customTools.push(toolDef);
15391
16347
  customHandlers.set(toolDef.name, handler);
15392
- tools41.push(toolDef);
16348
+ tools43.push(toolDef);
15393
16349
  return true;
15394
16350
  }
15395
16351
  function callCustomTool(name, args) {
@@ -15409,73 +16365,92 @@ var init_tool_registry = __esm({
15409
16365
  });
15410
16366
 
15411
16367
  // src/tool-executor.js
16368
+ function resolveConn(instance) {
16369
+ if (instance) return pool.getConnection(instance) || resolveCadConnection();
16370
+ return cad;
16371
+ }
16372
+ async function writeAuditSuccess(name, args, options) {
16373
+ writeAuditEntry({
16374
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
16375
+ tool: name,
16376
+ args: args || {},
16377
+ sessionId: options.sessionId || "",
16378
+ result: "success"
16379
+ });
16380
+ }
16381
+ async function writeAuditError(name, args, options, error2) {
16382
+ writeAuditEntry({
16383
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
16384
+ tool: name,
16385
+ args: args || {},
16386
+ sessionId: options.sessionId || "",
16387
+ result: "error",
16388
+ error: error2.message
16389
+ });
16390
+ }
16391
+ async function beginUndo(instance) {
16392
+ const conn = resolveConn(instance);
16393
+ if (conn?.connected) {
16394
+ try {
16395
+ await conn.sendCommand('(command "_.UNDO" "_BEGIN")');
16396
+ return true;
16397
+ } catch (e) {
16398
+ error(`UNDO_BEGIN failed: ${e.message}`);
16399
+ }
16400
+ }
16401
+ return false;
16402
+ }
16403
+ async function endUndo(instance) {
16404
+ const conn = resolveConn(instance);
16405
+ try {
16406
+ await conn.sendCommand('(command "_.UNDO" "_END")');
16407
+ } catch (_) {
16408
+ }
16409
+ }
15412
16410
  async function handleToolCall(name, args, options = {}) {
15413
16411
  const handler = TOOL_HANDLERS[name];
15414
16412
  if (!handler) {
15415
16413
  const customResult = callCustomTool(name, args);
15416
16414
  if (customResult !== null) return customResult;
15417
- return { content: [{ type: "text", text: `\u672A\u77E5\u5DE5\u5177: ${name}` }], isError: true };
16415
+ return mcpErrorResponse(new MCPError(ERROR_CODES.HANDLER_NOT_FOUND, `\u672A\u77E5\u5DE5\u5177: ${name}`));
15418
16416
  }
15419
16417
  const isReadOnly = READ_ONLY_TOOLS.has(name);
15420
16418
  const notifyProgress = options.notifyProgress || null;
15421
16419
  const rateCheck = checkToolRateLimit(name, isReadOnly, options.sessionId);
15422
16420
  if (!rateCheck.allowed) {
15423
- return { content: [{ type: "text", text: `\u5DE5\u5177\u8C03\u7528\u9891\u7387\u8D85\u9650 (${isReadOnly ? "\u8BFB" : "\u5199"}\u64CD\u4F5C)\uFF0C\u8BF7 ${rateCheck.retryAfter} \u79D2\u540E\u91CD\u8BD5` }], isError: true };
16421
+ return mcpErrorResponse(new MCPError(ERROR_CODES.RATE_LIMITED, `\u5DE5\u5177\u8C03\u7528\u9891\u7387\u8D85\u9650\uFF0C\u8BF7 ${rateCheck.retryAfter} \u79D2\u540E\u91CD\u8BD5`));
15424
16422
  }
15425
16423
  return progressContext.run({ notifyProgress }, async () => {
15426
16424
  setNotifyProgress(notifyProgress);
16425
+ const instance = args?.instance || null;
15427
16426
  let undoBegun = false;
15428
16427
  try {
15429
16428
  validateToolArgs(name, args || {});
15430
- const instance = args?.instance || null;
15431
16429
  if (instance) {
15432
16430
  const ctx = mcpContext.getStore();
15433
16431
  ctx._toolInstance = instance;
15434
16432
  }
15435
16433
  if (!isReadOnly) {
15436
- const targetConn = instance ? pool.getConnection(instance) : cad;
15437
- if (targetConn?.connected) {
15438
- try {
15439
- await targetConn.sendCommand('(command "_.UNDO" "_BEGIN")');
15440
- undoBegun = true;
15441
- } catch (e) {
15442
- error(`UNDO_BEGIN failed: ${e.message}`);
15443
- }
15444
- }
16434
+ undoBegun = await beginUndo(instance);
15445
16435
  }
15446
16436
  const np = getNotifyProgress();
15447
16437
  if (np) np(1, 1, `\u6B63\u5728\u6267\u884C ${name}...`);
15448
16438
  const result = await handler(args || {});
15449
16439
  if (np) np(1, 1, `${name} \u6267\u884C\u5B8C\u6210`);
15450
- writeAuditEntry({
15451
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
15452
- tool: name,
15453
- args: args || {},
15454
- sessionId: options.sessionId || "",
15455
- result: result?.isError ? "error" : "success"
15456
- });
16440
+ await writeAuditSuccess(name, args, options);
15457
16441
  notifyResourceChanges(name);
15458
16442
  return result;
15459
16443
  } catch (e) {
15460
16444
  error(`Tool call error [${name}]: ${e.message}`, { tool: name, args });
15461
- writeAuditEntry({
15462
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
15463
- tool: name,
15464
- args: args || {},
15465
- sessionId: options.sessionId || "",
15466
- result: "error",
15467
- error: e.message
15468
- });
15469
- return { content: [{ type: "text", text: `\u5DE5\u5177\u6267\u884C\u9519\u8BEF: ${e.message}` }], isError: true };
16445
+ await writeAuditError(name, args, options, e);
16446
+ if (e.message.startsWith("Missing required") || e.message.startsWith("Invalid type")) {
16447
+ return mcpErrorResponse(new MCPError(ERROR_CODES.INVALID_ARGS, e.message));
16448
+ }
16449
+ return mcpErrorResponse(e);
15470
16450
  } finally {
15471
16451
  const ctx = mcpContext.getStore();
15472
16452
  if (ctx) ctx._toolInstance = null;
15473
- if (undoBegun) {
15474
- try {
15475
- await cad.sendCommand('(command "_.UNDO" "_END")');
15476
- } catch (_) {
15477
- }
15478
- }
16453
+ if (undoBegun) await endUndo(instance);
15479
16454
  }
15480
16455
  });
15481
16456
  }
@@ -15492,14 +16467,12 @@ var init_tool_executor = __esm({
15492
16467
  init_cad();
15493
16468
  init_cad_pool();
15494
16469
  init_audit_log();
16470
+ init_errors();
15495
16471
  init_tool_registry();
15496
16472
  }
15497
16473
  });
15498
16474
 
15499
16475
  // src/mcp-tools.js
15500
- import fs11 from "fs";
15501
- import path15 from "path";
15502
- import os9 from "os";
15503
16476
  import { formatCode as formatCode2 } from "@atlisp/lint/dist/formatter.js";
15504
16477
  import { lintProject as lintProject2 } from "@atlisp/lint/dist/project.js";
15505
16478
  import { RULES as RULES3 } from "@atlisp/lint/dist/rules.js";
@@ -15516,7 +16489,6 @@ var TOOL_HANDLERS;
15516
16489
  var init_mcp_tools = __esm({
15517
16490
  "src/mcp-tools.js"() {
15518
16491
  init_handler_loader();
15519
- init_prompt_handlers();
15520
16492
  init_streaming();
15521
16493
  init_constants();
15522
16494
  init_cad();
@@ -15524,13 +16496,24 @@ var init_mcp_tools = __esm({
15524
16496
  init_tools();
15525
16497
  init_mcp_server_state();
15526
16498
  init_trigger_engine();
15527
- init_lisp_sandbox();
16499
+ init_lisp_security();
15528
16500
  init_lint_analyzer();
15529
16501
  init_repl_engine();
15530
16502
  init_webhook_engine();
15531
16503
  init_audit_log();
15532
16504
  init_agent_context();
16505
+ init_resource_handlers();
16506
+ init_subscription_manager();
16507
+ init_skill_handlers();
16508
+ init_meta_handlers();
15533
16509
  init_tool_executor();
16510
+ pool.onInstanceSwitch(() => {
16511
+ for (const uri of CAD_RESOURCE_URIS) {
16512
+ if (isSubscribed(uri)) {
16513
+ notify2(uri, null);
16514
+ }
16515
+ }
16516
+ });
15534
16517
  TOOL_HANDLERS = {
15535
16518
  // === CAD Connection ===
15536
16519
  connect_cad: lazy("connect_cad", (a) => a?.platform),
@@ -15567,14 +16550,8 @@ ${CAD_PLATFORMS.join("\n")}
15567
16550
  return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
15568
16551
  },
15569
16552
  // === Prompts ===
15570
- list_prompts: async () => {
15571
- const prompts = await listPrompts2();
15572
- return { content: [{ type: "text", text: JSON.stringify(prompts) }] };
15573
- },
15574
- get_prompt: async (a) => {
15575
- const prompt = await getPrompt2(a.name, a.args || {});
15576
- return { content: prompt.messages.map((m) => ({ type: "text", text: m.content.text || m.content })) };
15577
- },
16553
+ list_prompts: list_prompts_handler,
16554
+ get_prompt: get_prompt_handler,
15578
16555
  // === Entity Operations ===
15579
16556
  get_entity: lazy("get_entity", (a) => a?.handle),
15580
16557
  set_entity: lazy("set_entity", (a) => [a?.handle, a?.property, a?.value]),
@@ -15861,6 +16838,35 @@ ${CAD_PLATFORMS.join("\n")}
15861
16838
  draw_ray: lazy("draw_ray", (a) => [a.point, a.direction]),
15862
16839
  draw_leader: lazy("draw_leader", (a) => [a.points, a.annotation]),
15863
16840
  draw_table: lazy("draw_table", (a) => [a.insertionPoint, a.rows, a.columns, a.rowHeight, a.colWidth]),
16841
+ // === Batch Concurrent ===
16842
+ batch_concurrent_tasks: async (a) => {
16843
+ if (!a.tasks || !Array.isArray(a.tasks) || a.tasks.length === 0) {
16844
+ return { content: [{ type: "text", text: "\u9700\u8981 tasks \u6570\u7EC4" }], isError: true };
16845
+ }
16846
+ if (a.tasks.length > 50) {
16847
+ return { content: [{ type: "text", text: "\u6700\u591A\u652F\u6301 50 \u4E2A\u5E76\u53D1\u4EFB\u52A1" }], isError: true };
16848
+ }
16849
+ const results = await Promise.allSettled(a.tasks.map(async (task, idx) => {
16850
+ const handler = TOOL_HANDLERS[task.tool];
16851
+ if (!handler) return { index: idx, tool: task.tool, instance: task.instance, ok: false, error: `\u672A\u77E5\u5DE5\u5177: ${task.tool}` };
16852
+ const ctx = mcpContext.getStore();
16853
+ const prevInstance = ctx?._toolInstance || null;
16854
+ if (ctx && task.instance) ctx._toolInstance = task.instance;
16855
+ try {
16856
+ const result = await handler(task.args || {});
16857
+ return { index: idx, tool: task.tool, instance: task.instance, ok: !result.isError, data: result.content?.[0]?.text || result };
16858
+ } catch (e) {
16859
+ return { index: idx, tool: task.tool, instance: task.instance, ok: false, error: e.message };
16860
+ } finally {
16861
+ if (ctx) ctx._toolInstance = prevInstance;
16862
+ }
16863
+ }));
16864
+ const output = results.map((r, idx) => {
16865
+ if (r.status === "fulfilled") return r.value;
16866
+ return { index: idx, tool: a.tasks[idx].tool, instance: a.tasks[idx].instance, ok: false, error: r.reason?.message || "Unknown error" };
16867
+ });
16868
+ return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }] };
16869
+ },
15864
16870
  // === Batch Transaction ===
15865
16871
  batch_transaction: lazy("batch_transaction"),
15866
16872
  // === Watch / Trigger ===
@@ -15907,10 +16913,10 @@ ${CAD_PLATFORMS.join("\n")}
15907
16913
  return { content: [{ type: "text", text: JSON.stringify(history) }] };
15908
16914
  },
15909
16915
  lisp_repl_list: () => {
15910
- return { content: [{ type: "text", text: JSON.stringify(listSessions()) }] };
16916
+ return { content: [{ type: "text", text: JSON.stringify(listSessions2()) }] };
15911
16917
  },
15912
16918
  lisp_repl_close: (a) => {
15913
- const ok = closeSession(a.sessionId);
16919
+ const ok = closeSession2(a.sessionId);
15914
16920
  return { content: [{ type: "text", text: JSON.stringify({ success: ok }) }] };
15915
16921
  },
15916
16922
  // === Streaming ===
@@ -15982,6 +16988,12 @@ ${CAD_PLATFORMS.join("\n")}
15982
16988
  return { content: [{ type: "text", text: `\u9879\u76EE\u5206\u6790\u5931\u8D25: ${e.message}` }], isError: true };
15983
16989
  }
15984
16990
  },
16991
+ // === Debug ===
16992
+ debug_start: lazy("debug_start", (a) => a),
16993
+ debug_step: lazy("debug_step", (a) => a),
16994
+ debug_evaluate: lazy("debug_evaluate", (a) => a),
16995
+ debug_get_state: lazy("debug_get_state", (a) => a),
16996
+ debug_stop: lazy("debug_stop", (a) => a),
15985
16997
  // === Sandbox (Security) ===
15986
16998
  sandbox_validate: (a) => {
15987
16999
  const result = validateLispCode(a.code, a.sessionId || null);
@@ -16011,6 +17023,14 @@ ${CAD_PLATFORMS.join("\n")}
16011
17023
  },
16012
17024
  cad_pool_switch: (a) => {
16013
17025
  const ok = pool.setActive(a.key);
17026
+ if (ok) {
17027
+ clearCache();
17028
+ for (const uri of CAD_RESOURCE_URIS) {
17029
+ if (isSubscribed(uri)) {
17030
+ notify2(uri, null);
17031
+ }
17032
+ }
17033
+ }
16014
17034
  return { content: [{ type: "text", text: ok ? `\u5DF2\u5207\u6362\u5230\u5B9E\u4F8B ${a.key}` : `\u5B9E\u4F8B ${a.key} \u4E0D\u5B58\u5728` }] };
16015
17035
  },
16016
17036
  cad_pool_health: async () => {
@@ -16022,8 +17042,9 @@ ${CAD_PLATFORMS.join("\n")}
16022
17042
  return { content: [{ type: "text", text: JSON.stringify(stats, null, 2) }] };
16023
17043
  },
16024
17044
  cad_pool_connect: async (a) => {
16025
- const ok = await pool.connect(a.key, a.platform, { label: a.label, makeActive: a.makeActive !== false });
16026
- return { content: [{ type: "text", text: ok ? `\u5B9E\u4F8B "${a.key}" \u5DF2\u8FDE\u63A5` : `\u5B9E\u4F8B "${a.key}" \u8FDE\u63A5\u5931\u8D25` }] };
17045
+ const ok = await pool.connect(a.key, a.platform, { label: a.label, pid: a.pid, makeActive: a.makeActive !== false });
17046
+ const label = a.key || (a.pid != null ? `PID ${a.pid}` : "default");
17047
+ return { content: [{ type: "text", text: ok ? `\u5B9E\u4F8B "${label}" \u5DF2\u8FDE\u63A5` : `\u5B9E\u4F8B "${label}" \u8FDE\u63A5\u5931\u8D25` }] };
16027
17048
  },
16028
17049
  cad_pool_disconnect: async (a) => {
16029
17050
  const ok = await pool.disconnect(a.key);
@@ -16073,261 +17094,15 @@ ${CAD_PLATFORMS.join("\n")}
16073
17094
  return { content: [{ type: "text", text: "\u4E0A\u4E0B\u6587\u5DF2\u6E05\u9664" }] };
16074
17095
  },
16075
17096
  // === Meta Tools ===
16076
- search_tools: async (a) => {
16077
- const q = (a.query || "").toLowerCase();
16078
- const results = tools41.filter((t) => t.name.toLowerCase().includes(q) || (t.description || "").toLowerCase().includes(q));
16079
- const text = results.length ? `\u627E\u5230 ${results.length} \u4E2A\u5DE5\u5177:
16080
- ${results.map((t) => `${t.name}: ${t.description}`).join("\n")}` : `\u672A\u627E\u5230\u5305\u542B "${a.query}" \u7684\u5DE5\u5177`;
16081
- return { content: [{ type: "text", text }] };
16082
- },
16083
- list_tools_by_category: async (a) => {
16084
- const categories = {};
16085
- for (const t of tools41) {
16086
- let cat = "\u5176\u4ED6";
16087
- const n = t.name;
16088
- if (/^(draw_|create_3d_|create_box|create_cone|create_cylinder|create_sphere|create_torus|create_wedge|extrude_|revolve_|boolean_|slice_solid|thicken_|offset_surface|create_loft_|create_region)/.test(n)) cat = "\u7ED8\u56FE\u4E0E3D";
16089
- else if (/^(move_|copy_|rotate_|scale_|mirror_|offset_|array_|batch_)/.test(n)) cat = "\u4FEE\u6539";
16090
- else if (/^(trim_|extend_|fillet|chamfer|break_|join_|stretch)/.test(n)) cat = "\u7F16\u8F91";
16091
- else if (/^(create_layer|delete_layer|set_layer|layer_)/.test(n)) cat = "\u56FE\u5C42";
16092
- else if (/^(create_block|insert_block|explode_block|get_block|set_block|batch_get_block|batch_set_block)/.test(n)) cat = "\u5757";
16093
- else if (/^(create_dim|list_dim|set_dim|create_hatch|set_hatch)/.test(n)) cat = "\u6807\u6CE8\u4E0E\u586B\u5145";
16094
- else if (/^(create_ucs|set_ucs|delete_ucs|list_ucs|get_current_ucs|create_layout|delete_layout|rename_layout|set_layout|list_layouts|get_current_layout)/.test(n)) cat = "\u5750\u6807\u7CFB\u4E0E\u5E03\u5C40";
16095
- else if (/^(create_viewport|set_viewport|lock_viewport|unlock_viewport|maximize_viewport)/.test(n)) cat = "\u89C6\u53E3";
16096
- else if (/^find_/.test(n)) cat = "\u7A7A\u95F4\u67E5\u8BE2";
16097
- else if (/^(select_|get_entity|set_entity|delete_entity|get_bounding_box)/.test(n)) cat = "\u9009\u62E9\u4E0E\u5C5E\u6027";
16098
- else if (/^(open_dwg|save_dwg|export_|import_|close_dwg|publish_)/.test(n)) cat = "\u6587\u4EF6";
16099
- else if (/^(plot_|save_pdf|batch_plot|publish_layouts)/.test(n)) cat = "\u6253\u5370";
16100
- else if (/^(get_xdata|set_xdata|delete_xdata|list_xdata|copxy_xdata|search_by_xdata|export_xdata|import_xdata)/.test(n)) cat = "\u6269\u5C55\u6570\u636E";
16101
- else if (/^(create_sheet|open_sheet|list_sheet|add_sheet|delete_sheet|publish_sheet|sheet_|reorder_sheet|create_subset|list_subset|set_sheet)/.test(n)) cat = "\u56FE\u7EB8\u96C6";
16102
- else if (/^(attach_|detach_|reload_|list_attached|get_pdf|create_dgn|create_dwf|import_dxf|import_dgn|export_dgn|create_hyper|remove_hyper|xref|bind_xref|clip_xref|path_xref)/.test(n)) cat = "\u53C2\u7167\u4E0E\u5E95\u56FE";
16103
- else if (/^(create_text|create_mleader|create_linetype|list_text|list_linetype|list_mleader|load_linetype|create_dimension_style|set_dimstyle|list_dimension_styles|set_mleader_style)/.test(n)) cat = "\u6837\u5F0F";
16104
- else if (/^(purge_|drawing_audit|drawing_recover|overkill)/.test(n)) cat = "\u6E05\u7406";
16105
- else if (/^(find_text|replace_text|set_text_|justify_|convert_text|explode_text)/.test(n)) cat = "\u6587\u5B57";
16106
- else if (/^(eval_lisp|connect_cad|get_cad|get_platform|get_system|at_command|new_document|bring_to_front|install_atlisp|init_atlisp|get_system_status)/.test(n)) cat = "\u7CFB\u7EDF";
16107
- else if (/^(list_packages|search_packages|install_package|get_function_usage|list_symbols|import_funlib)/.test(n)) cat = "\u5305\u4E0E\u51FD\u6570";
16108
- else if (/^(pipeline_|watch_|lisp_repl|webhook_|sandbox_|audit_|cad_pool_|external_|list_data|export_to|import_external)/.test(n)) cat = "\u9AD8\u7EA7";
16109
- else if (/^(list_prompts|get_prompt|search_tools|list_tools_by_category)/.test(n)) cat = "\u5143\u5DE5\u5177";
16110
- else if (/^(agent_)/.test(n)) cat = "Agent\u4E0A\u4E0B\u6587";
16111
- if (!categories[cat]) categories[cat] = [];
16112
- categories[cat].push(t.name);
16113
- }
16114
- if (a.category) {
16115
- const names = categories[a.category];
16116
- return { content: [{ type: "text", text: names ? `${a.category} (${names.length}):
16117
- ${names.join("\n")}` : `\u672A\u627E\u5230\u5206\u7C7B: ${a.category}` }] };
16118
- }
16119
- const lines = [];
16120
- for (const [cat, names] of Object.entries(categories)) {
16121
- lines.push(`## ${cat} (${names.length})`);
16122
- }
16123
- lines.push(`
16124
- \u5171 ${Object.keys(categories).length} \u4E2A\u5206\u7C7B`);
16125
- return { content: [{ type: "text", text: lines.join("\n") }] };
16126
- },
17097
+ search_tools,
17098
+ list_tools_by_category,
17099
+ get_tool_info,
16127
17100
  // === Skill Management ===
16128
- skill_search: async (a) => {
16129
- const registryUrl = process.env.SKILL_REGISTRY_URL || "https://raw.githubusercontent.com/atlisp/skill-registry/main/index.json";
16130
- const cachePath = path15.join(os9.homedir(), ".atlisp", "skill-registry-cache.json");
16131
- let cache = null;
16132
- try {
16133
- if (fs11.existsSync(cachePath)) cache = JSON.parse(fs11.readFileSync(cachePath, "utf-8"));
16134
- } catch {
16135
- }
16136
- let index = cache && Date.now() - cache.fetchedAt < 36e5 ? cache : null;
16137
- if (!index) {
16138
- try {
16139
- const resp = await fetch(registryUrl, { headers: { "User-Agent": "atlisp-mcp" } });
16140
- if (resp.ok) {
16141
- index = await resp.json();
16142
- index.fetchedAt = Date.now();
16143
- try {
16144
- fs11.writeFileSync(cachePath, JSON.stringify(index, null, 2), "utf-8");
16145
- } catch {
16146
- }
16147
- }
16148
- } catch {
16149
- }
16150
- }
16151
- if (!index || !index.skills) return { content: [{ type: "text", text: "\u65E0\u6CD5\u83B7\u53D6 skill \u6CE8\u518C\u8868\u7D22\u5F15" }], isError: true };
16152
- const q = (a.query || "").toLowerCase();
16153
- const results = index.skills.filter(
16154
- (s) => s.name.toLowerCase().includes(q) || (s.description || "").toLowerCase().includes(q) || (s.tags || []).some((t) => t.toLowerCase().includes(q))
16155
- );
16156
- if (!results.length) return { content: [{ type: "text", text: `\u672A\u627E\u5230\u5339\u914D "${a.query}" \u7684 skill` }] };
16157
- const lines = [`\u627E\u5230 ${results.length} \u4E2A skill:
16158
- `];
16159
- for (const s of results) {
16160
- lines.push(`**${s.name}**`);
16161
- if (s.description) lines.push(` \u63CF\u8FF0: ${s.description}`);
16162
- if (s.tags?.length) lines.push(` \u6807\u7B7E: ${s.tags.join(", ")}`);
16163
- if (s.version) lines.push(` \u7248\u672C: ${s.version}`);
16164
- lines.push("");
16165
- }
16166
- return { content: [{ type: "text", text: lines.join("\n") }] };
16167
- },
16168
- skill_install: async (a) => {
16169
- if (!a.name) return { content: [{ type: "text", text: "\u9700\u8981 skill \u540D\u79F0" }], isError: true };
16170
- const registryUrl = process.env.SKILL_REGISTRY_URL || "https://raw.githubusercontent.com/atlisp/skill-registry/main/index.json";
16171
- const cachePath = path15.join(os9.homedir(), ".atlisp", "skill-registry-cache.json");
16172
- let cache = null;
16173
- try {
16174
- if (fs11.existsSync(cachePath)) cache = JSON.parse(fs11.readFileSync(cachePath, "utf-8"));
16175
- } catch {
16176
- }
16177
- let index = cache;
16178
- if (!index || Date.now() - index.fetchedAt > 36e5) {
16179
- try {
16180
- const resp = await fetch(registryUrl, { headers: { "User-Agent": "atlisp-mcp" } });
16181
- if (resp.ok) {
16182
- index = await resp.json();
16183
- index.fetchedAt = Date.now();
16184
- try {
16185
- fs11.writeFileSync(cachePath, JSON.stringify(index, null, 2), "utf-8");
16186
- } catch {
16187
- }
16188
- }
16189
- } catch {
16190
- }
16191
- }
16192
- if (!index || !index.skills) return { content: [{ type: "text", text: "\u65E0\u6CD5\u83B7\u53D6 skill \u6CE8\u518C\u8868\u7D22\u5F15" }], isError: true };
16193
- const skill = index.skills.find((s) => s.name === a.name);
16194
- if (!skill) return { content: [{ type: "text", text: `\u672A\u627E\u5230 skill: ${a.name}` }], isError: true };
16195
- const downloadUrl = skill.download_url || skill.url;
16196
- if (!downloadUrl) return { content: [{ type: "text", text: `Skill "${a.name}" \u6CA1\u6709\u4E0B\u8F7D\u5730\u5740` }], isError: true };
16197
- let content;
16198
- try {
16199
- const resp = await fetch(downloadUrl, { headers: { "User-Agent": "atlisp-mcp" } });
16200
- if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
16201
- content = await resp.text();
16202
- } catch (e) {
16203
- return { content: [{ type: "text", text: `\u4E0B\u8F7D\u5931\u8D25: ${e.message}` }], isError: true };
16204
- }
16205
- const skillsDir = path15.join(os9.homedir(), ".atlisp", "skills");
16206
- try {
16207
- fs11.mkdirSync(skillsDir, { recursive: true });
16208
- } catch {
16209
- }
16210
- const safeName = a.name.replace(/[^a-zA-Z0-9\u4e00-\u9fff_-]/g, "-").substring(0, 40);
16211
- const skillFile = path15.join(skillsDir, `skill-${safeName}.md`);
16212
- if (fs11.existsSync(skillFile)) {
16213
- return { content: [{ type: "text", text: `Skill "${a.name}" \u5DF2\u5B89\u88C5 (${skillFile})` }], isError: true };
16214
- }
16215
- let finalContent = content;
16216
- if (!content.match(/^---\n/)) {
16217
- const tags = (skill.tags || []).join(", ");
16218
- finalContent = `---
16219
- name: ${a.name}
16220
- description: ${skill.description || ""}
16221
- tags: ${tags}
16222
- ---
16223
-
16224
- ${content}`;
16225
- }
16226
- fs11.writeFileSync(skillFile, finalContent, "utf-8");
16227
- return { content: [{ type: "text", text: `\u2705 ${skill.name} \u5B89\u88C5\u6210\u529F
16228
- \u6587\u4EF6: ${skillFile}${skill.version ? `
16229
- \u7248\u672C: ${skill.version}` : ""}` }] };
16230
- },
16231
- skill_list_installed: async () => {
16232
- const skillsDir = path15.join(os9.homedir(), ".atlisp", "skills");
16233
- if (!fs11.existsSync(skillsDir)) return { content: [{ type: "text", text: "\u6682\u65E0\u5DF2\u5B89\u88C5\u7684 skill" }] };
16234
- const results = [];
16235
- try {
16236
- const entries = fs11.readdirSync(skillsDir, { withFileTypes: true });
16237
- for (const entry of entries) {
16238
- if (entry.isFile() && entry.name.endsWith(".md")) {
16239
- const fullPath = path15.join(skillsDir, entry.name);
16240
- try {
16241
- const raw = fs11.readFileSync(fullPath, "utf-8");
16242
- const match = raw.match(/^---\n([\s\S]*?)\n---/);
16243
- if (match) {
16244
- const fm = {};
16245
- for (const line of match[1].split("\n")) {
16246
- const sep = line.indexOf(":");
16247
- if (sep > 0) fm[line.slice(0, sep).trim()] = line.slice(sep + 1).trim();
16248
- }
16249
- results.push({ name: fm.name || entry.name.replace(".md", ""), description: fm.description || "", tags: fm.tags || "" });
16250
- } else {
16251
- results.push({ name: entry.name.replace(".md", ""), description: "", tags: "" });
16252
- }
16253
- } catch {
16254
- }
16255
- }
16256
- }
16257
- } catch {
16258
- }
16259
- if (!results.length) return { content: [{ type: "text", text: "\u6682\u65E0\u5DF2\u5B89\u88C5\u7684 skill" }] };
16260
- const lines = [`\u5DF2\u5B89\u88C5 ${results.length} \u4E2A skill:
16261
- `];
16262
- for (const s of results) {
16263
- lines.push(`**${s.name}**${s.description ? ` \u2014 ${s.description}` : ""}`);
16264
- if (s.tags) lines.push(` \u6807\u7B7E: ${s.tags}`);
16265
- }
16266
- return { content: [{ type: "text", text: lines.join("\n") }] };
16267
- },
16268
- skill_uninstall: async (a) => {
16269
- if (!a.name) return { content: [{ type: "text", text: "\u9700\u8981 skill \u540D\u79F0" }], isError: true };
16270
- const skillsDir = path15.join(os9.homedir(), ".atlisp", "skills");
16271
- if (!fs11.existsSync(skillsDir)) return { content: [{ type: "text", text: `\u672A\u5B89\u88C5 skill: ${a.name}` }], isError: true };
16272
- let found = null;
16273
- try {
16274
- const entries = fs11.readdirSync(skillsDir, { withFileTypes: true });
16275
- for (const entry of entries) {
16276
- if (entry.isFile() && entry.name.endsWith(".md")) {
16277
- const fullPath = path15.join(skillsDir, entry.name);
16278
- try {
16279
- const raw = fs11.readFileSync(fullPath, "utf-8");
16280
- const match = raw.match(/^---\n([\s\S]*?)\n---/);
16281
- if (match) {
16282
- for (const line of match[1].split("\n")) {
16283
- if (line.startsWith("name:") && line.slice(5).trim() === a.name) {
16284
- found = fullPath;
16285
- break;
16286
- }
16287
- }
16288
- } else if (entry.name.replace(".md", "") === a.name) {
16289
- found = fullPath;
16290
- }
16291
- } catch {
16292
- }
16293
- if (found) break;
16294
- }
16295
- }
16296
- } catch {
16297
- }
16298
- if (!found) return { content: [{ type: "text", text: `\u672A\u5B89\u88C5 skill: ${a.name}` }], isError: true };
16299
- fs11.unlinkSync(found);
16300
- return { content: [{ type: "text", text: `\u2705 ${a.name} \u5DF2\u5378\u8F7D` }] };
16301
- },
16302
- skill_list_categories: async () => {
16303
- const registryUrl = process.env.SKILL_REGISTRY_URL || "https://raw.githubusercontent.com/atlisp/skill-registry/main/index.json";
16304
- const cachePath = path15.join(os9.homedir(), ".atlisp", "skill-registry-cache.json");
16305
- let cache = null;
16306
- try {
16307
- if (fs11.existsSync(cachePath)) cache = JSON.parse(fs11.readFileSync(cachePath, "utf-8"));
16308
- } catch {
16309
- }
16310
- let index = cache && Date.now() - cache.fetchedAt < 36e5 ? cache : null;
16311
- if (!index) {
16312
- try {
16313
- const resp = await fetch(registryUrl, { headers: { "User-Agent": "atlisp-mcp" } });
16314
- if (resp.ok) {
16315
- index = await resp.json();
16316
- try {
16317
- fs11.writeFileSync(cachePath, JSON.stringify(index, null, 2), "utf-8");
16318
- } catch {
16319
- }
16320
- }
16321
- } catch {
16322
- }
16323
- }
16324
- if (!index || !index.skills) return { content: [{ type: "text", text: "\u65E0\u6CD5\u83B7\u53D6 skill \u6CE8\u518C\u8868\u7D22\u5F15" }], isError: true };
16325
- const cats = /* @__PURE__ */ new Set();
16326
- for (const s of index.skills) for (const t of s.tags || []) cats.add(t);
16327
- const sorted = Array.from(cats).sort();
16328
- return { content: [{ type: "text", text: sorted.length ? `\u6CE8\u518C\u8868\u5206\u7C7B (${sorted.length}):
16329
- ${sorted.join("\n")}` : "\u6682\u65E0\u5206\u7C7B" }] };
16330
- }
17101
+ skill_search,
17102
+ skill_install,
17103
+ skill_list_installed,
17104
+ skill_uninstall,
17105
+ skill_list_categories
16331
17106
  };
16332
17107
  }
16333
17108
  });
@@ -16418,7 +17193,7 @@ var init_mcp_server_state = __esm({
16418
17193
  progressContext = new AsyncLocalStorage();
16419
17194
  sessionServers = /* @__PURE__ */ new Map();
16420
17195
  sessionTransports = /* @__PURE__ */ new Map();
16421
- mcpHandlers = { tools: tools41, handleToolCall };
17196
+ mcpHandlers = { tools: tools43, handleToolCall };
16422
17197
  _stdioServer = null;
16423
17198
  _pendingResourceUpdated = /* @__PURE__ */ new Set();
16424
17199
  _resourceUpdatedTimer = null;
@@ -16442,11 +17217,12 @@ function mcpError(text) {
16442
17217
  return { content: [{ type: "text", text: String(text) }], isError: true };
16443
17218
  }
16444
17219
  async function ensureCadConnected() {
16445
- const { cad: cad2 } = await Promise.resolve().then(() => (init_cad(), cad_exports));
16446
- if (!cad2.connected) {
16447
- await cad2.connect();
17220
+ const { cad: cad2, resolveCadConnection: resolveCadConnection2 } = await Promise.resolve().then(() => (init_cad(), cad_exports));
17221
+ const conn = resolveCadConnection2();
17222
+ if (!conn.connected) {
17223
+ await conn.connect();
16448
17224
  }
16449
- if (!cad2.connected) {
17225
+ if (!conn.connected) {
16450
17226
  throw createError(ERROR_CODES.CAD_NOT_CONNECTED);
16451
17227
  }
16452
17228
  }
@@ -16631,7 +17407,7 @@ __export(resource_handlers_exports, {
16631
17407
  readResource: () => readResource
16632
17408
  });
16633
17409
  import fs13 from "fs";
16634
- import path16 from "path";
17410
+ import path17 from "path";
16635
17411
  function propertyPairsToObject(pairs) {
16636
17412
  if (!Array.isArray(pairs)) return null;
16637
17413
  const obj = {};
@@ -16642,7 +17418,7 @@ function propertyPairsToObject(pairs) {
16642
17418
  }
16643
17419
  return obj;
16644
17420
  }
16645
- async function ensureCad() {
17421
+ async function ensureCad2() {
16646
17422
  if (!cad.connected) await cad.connect();
16647
17423
  if (!cad.connected) throw createError(ERROR_CODES.CAD_NOT_CONNECTED);
16648
17424
  }
@@ -16810,7 +17586,7 @@ function parseTableRecord(rec, tableName) {
16810
17586
  return obj.name ? obj : null;
16811
17587
  }
16812
17588
  async function getEntityByHandle(handle) {
16813
- await ensureCad();
17589
+ await ensureCad2();
16814
17590
  const code = `(progn
16815
17591
  (vl-load-com)
16816
17592
  (if (setq ent (handent "${escapeLispString(handle)}"))
@@ -16920,7 +17696,7 @@ async function getEntities(params = {}) {
16920
17696
  const cacheKey = `dwg:entities:${JSON.stringify(params)}`;
16921
17697
  const cached = getCached(cacheKey);
16922
17698
  if (cached) return cached;
16923
- await ensureCad();
17699
+ await ensureCad2();
16924
17700
  const hasFilter = params.type || params.layer || params.bbox;
16925
17701
  if (!hasFilter) {
16926
17702
  return await getEntitiesSummary(cacheKey);
@@ -16956,7 +17732,7 @@ async function getTextContent(params = {}) {
16956
17732
  const cacheKey = `dwg:text-content:${JSON.stringify(params)}`;
16957
17733
  const cached = getCached(cacheKey);
16958
17734
  if (cached) return cached;
16959
- await ensureCad();
17735
+ await ensureCad2();
16960
17736
  const offset = Math.max(0, parseInt(params.offset) || 0);
16961
17737
  const limit = Math.min(Math.max(1, parseInt(params.limit) || 5e3), 5e3);
16962
17738
  try {
@@ -17106,7 +17882,7 @@ async function combineText(params = {}) {
17106
17882
  return { total: blocks.length, blocks, medianHeight: mh };
17107
17883
  }
17108
17884
  async function getDwgName() {
17109
- await ensureCad();
17885
+ await ensureCad2();
17110
17886
  try {
17111
17887
  const code = `(vl-princ-to-string (getvar "dwgname"))`;
17112
17888
  const name = await cad.sendCommandWithResult(code);
@@ -17116,7 +17892,7 @@ async function getDwgName() {
17116
17892
  }
17117
17893
  }
17118
17894
  async function getDwgPath() {
17119
- await ensureCad();
17895
+ await ensureCad2();
17120
17896
  try {
17121
17897
  const code = `(progn
17122
17898
  (setq prefix (vl-princ-to-string (getvar "dwgprefix")))
@@ -17149,7 +17925,7 @@ async function getBlockRefs(params = {}) {
17149
17925
  const cacheKey = `dwg:block-refs:${params.blockName || "all"}`;
17150
17926
  const cached = getCached(cacheKey);
17151
17927
  if (cached) return cached;
17152
- await ensureCad();
17928
+ await ensureCad2();
17153
17929
  try {
17154
17930
  const code = buildBlockRefsCode(params.blockName);
17155
17931
  const resultStr = await cad.sendCommandWithResult(code);
@@ -17211,7 +17987,7 @@ async function getTables(params = {}) {
17211
17987
  const cacheKey = `dwg:tbl:${tblName}`;
17212
17988
  const cached = getCached(cacheKey);
17213
17989
  if (cached) return cached;
17214
- await ensureCad();
17990
+ await ensureCad2();
17215
17991
  try {
17216
17992
  const code = buildTablesCode(tblName);
17217
17993
  const resultStr = await cad.sendCommandWithResult(code);
@@ -17293,7 +18069,7 @@ async function getPackages(filterName = null) {
17293
18069
  const cacheKey = filterName ? `packages:${filterName}` : "packages:all";
17294
18070
  const cached = getCached(cacheKey);
17295
18071
  if (cached) return cached;
17296
- await ensureCad();
18072
+ await ensureCad2();
17297
18073
  try {
17298
18074
  const code = `(mapcar 'cdr @::*local-pkgs*)`;
17299
18075
  const result = await cad.sendCommandWithResult(code);
@@ -17366,7 +18142,7 @@ async function getDwgList() {
17366
18142
  const cacheKey = "cad:dwgs";
17367
18143
  const cached = getCached(cacheKey);
17368
18144
  if (cached) return cached;
17369
- await ensureCad();
18145
+ await ensureCad2();
17370
18146
  try {
17371
18147
  const code = `(progn
17372
18148
  (vl-load-com)
@@ -17403,7 +18179,7 @@ async function getDwgList() {
17403
18179
  }
17404
18180
  }
17405
18181
  function loadStandardsFile(filename) {
17406
- const filePath = path16.join(STANDARDS_DIR, filename);
18182
+ const filePath = path17.join(STANDARDS_DIR, filename);
17407
18183
  try {
17408
18184
  const raw = fs13.readFileSync(filePath, "utf-8");
17409
18185
  return JSON.parse(raw);
@@ -17436,7 +18212,7 @@ function getCodingStandards() {
17436
18212
  return data || { error: "\u7F16\u7801\u89C4\u8303\u6587\u4EF6\u52A0\u8F7D\u5931\u8D25" };
17437
18213
  }
17438
18214
  async function getCadEvents(params) {
17439
- await ensureCad();
18215
+ await ensureCad2();
17440
18216
  try {
17441
18217
  const code = `(progn (setq cnt 0) (if (setq ss (ssget "_X")) (setq cnt (sslength ss))) (list (cons "entityCount" cnt) (cons "dwgName" (getvar "dwgname")) (cons "dwgPath" (getvar "dwgprefix"))))`;
17442
18218
  const result = await cad.sendCommandWithResult(code);
@@ -17503,6 +18279,119 @@ async function getDwgContext() {
17503
18279
  };
17504
18280
  }
17505
18281
  }
18282
+ async function getDrawingAnalysis(params = {}) {
18283
+ const snapshot = await getCadSnapshot();
18284
+ if (!snapshot || !snapshot.cadConnected) {
18285
+ return { connected: false, error: "CAD \u672A\u8FDE\u63A5" };
18286
+ }
18287
+ try {
18288
+ const detailedCode = `(progn
18289
+ (vl-load-com)
18290
+ (setq r nil)
18291
+ ;; entity counts by type
18292
+ (if (setq ss (ssget "_X"))
18293
+ (progn
18294
+ (setq total (sslength ss) i 0 cnt nil)
18295
+ (while (< i total)
18296
+ (setq typ (cdr (assoc 0 (entget (ssname ss i)))))
18297
+ (setq cnt (if (assoc typ cnt) (subst (cons typ (1+ (cdr (assoc typ cnt)))) (assoc typ cnt) cnt) (cons (cons typ 1) cnt)))
18298
+ (setq i (1+ i)))
18299
+ (setq r (cons (cons "entityCount" total) r))
18300
+ (setq r (cons (cons "entityByType" cnt) r)))
18301
+ (setq r (cons (cons "entityCount" 0) r)))
18302
+ ;; block usage
18303
+ (setq blocks nil)
18304
+ (vlax-for b (vla-get-blocks (vla-get-activedocument (vlax-get-acad-object)))
18305
+ (if (and (= (vla-get-IsXref b) :vlax-false) (/= (vla-get-Name b) "*Model_Space") (/= (vla-get-Name b) "*Paper_Space"))
18306
+ (setq blocks (cons (list "name" (vla-get-Name b) "count" (vla-get-Count b) "isDynamic" (vla-get-IsDynamicBlock b)) blocks))))
18307
+ (setq r (cons (cons "blockUsage" blocks) r))
18308
+ ;; layer summary
18309
+ (setq layers nil le (tblnext "layer" t))
18310
+ (while le
18311
+ (setq ln (cdr (assoc 2 le)) lc (cdr (assoc 62 le)) ll (cdr (assoc 6 le)))
18312
+ (setq layers (cons (list "name" ln "color" (if (< lc 0) (- lc) lc) "off" (< lc 0) "linetype" ll) layers))
18313
+ (setq le (tblnext "layer" nil)))
18314
+ (setq r (cons (cons "layers" layers) r))
18315
+ ;; text stats
18316
+ (if (setq ts (ssget "_X" '((0 . "TEXT,MTEXT"))))
18317
+ (progn
18318
+ (setq tc (sslength ts) th nil ti 0)
18319
+ (while (< ti tc)
18320
+ (setq h (cdr (assoc 40 (entget (ssname ts ti)))))
18321
+ (if h (setq th (cons h th)))
18322
+ (setq ti (1+ ti)))
18323
+ (setq th (vl-sort th '<))
18324
+ (setq r (cons (cons "textCount" tc) r))
18325
+ (setq r (cons (cons "textHeights" (list "min" (car th) "max" (last th) "median" (nth (fix (/ (length th) 2)) th))) r)))
18326
+ (setq r (cons (cons "textCount" 0) r)))
18327
+ ;; system variables
18328
+ (setq r (cons (cons "sysvars" (list
18329
+ (cons "ltscale" (getvar "ltscale"))
18330
+ (cons "dimscale" (getvar "dimscale"))
18331
+ (cons "insunits" (getvar "insunits"))
18332
+ (cons "measurement" (getvar "measurement"))
18333
+ (cons "celtype" (getvar "celtype"))
18334
+ (cons "clayer" (getvar "clayer"))
18335
+ (cons "cecolor" (getvar "cecolor"))
18336
+ (cons "osmode" (getvar "osmode"))
18337
+ (cons "snapmode" (getvar "snapmode"))
18338
+ (cons "gridmode" (getvar "gridmode"))
18339
+ (cons "orthomode" (getvar "orthomode"))
18340
+ (cons "plinetype" (getvar "plinetype"))
18341
+ (cons "fillmode" (getvar "fillmode"))
18342
+ (cons "pickstyle" (getvar "pickstyle"))
18343
+ )) r))
18344
+ (vl-prin1-to-string (reverse r)))`;
18345
+ const resultStr = await cad.sendCommandWithResult(detailedCode);
18346
+ const parsed = parseLispRecursive(resultStr);
18347
+ if (!parsed || !Array.isArray(parsed)) {
18348
+ return { connected: true, error: "\u5206\u6790\u7ED3\u679C\u89E3\u6790\u5931\u8D25" };
18349
+ }
18350
+ const analysis = {};
18351
+ for (const pair of parsed) {
18352
+ if (Array.isArray(pair) && pair.length >= 2) {
18353
+ analysis[String(pair[0])] = pair[1];
18354
+ }
18355
+ }
18356
+ return { connected: true, ...analysis };
18357
+ } catch (e) {
18358
+ log(`getDrawingAnalysis error: ${e.message}`);
18359
+ return { connected: true, error: e.message };
18360
+ }
18361
+ }
18362
+ function getToolCategories() {
18363
+ const cats = {};
18364
+ for (const t of tools43) {
18365
+ const cat = t.annotations?.category || "\u5176\u4ED6";
18366
+ if (!cats[cat]) cats[cat] = { count: 0, tools: [] };
18367
+ cats[cat].count++;
18368
+ cats[cat].tools.push(t.name);
18369
+ }
18370
+ return Object.entries(cats).map(([category, info]) => ({
18371
+ category,
18372
+ count: info.count,
18373
+ tools: info.tools
18374
+ })).sort((a, b) => b.count - a.count);
18375
+ }
18376
+ async function checkDrawingStandards() {
18377
+ const standards = getDraftingStandards();
18378
+ if (!standards || standards.error) {
18379
+ return { ok: false, error: standards?.error || "\u5236\u56FE\u89C4\u8303\u672A\u52A0\u8F7D" };
18380
+ }
18381
+ try {
18382
+ const issues = [];
18383
+ for (const rule of standards.layers || []) {
18384
+ const code = `(tblsearch "LAYER" "${escapeLispString(rule.name || rule.pattern)}")`;
18385
+ const exists = await cad.sendCommandWithResult(code);
18386
+ if (rule.required && (!exists || exists === "nil" || exists === "")) {
18387
+ issues.push({ type: "missing_layer", message: `\u7F3A\u5C11\u5FC5\u9700\u56FE\u5C42: ${rule.name}` });
18388
+ }
18389
+ }
18390
+ return { ok: issues.length === 0, issues };
18391
+ } catch (e) {
18392
+ return { ok: false, error: e.message };
18393
+ }
18394
+ }
17506
18395
  function getToolRelationships() {
17507
18396
  return {
17508
18397
  selection: {
@@ -17758,6 +18647,12 @@ async function readResource(uri) {
17758
18647
  return await getDwgContext();
17759
18648
  case "atlisp://tools/relationships":
17760
18649
  return getToolRelationships();
18650
+ case "atlisp://dwg/analysis":
18651
+ return await getDrawingAnalysis(params);
18652
+ case "atlisp://tools/categories":
18653
+ return getToolCategories();
18654
+ case "atlisp://dwg/standards/check":
18655
+ return await checkDrawingStandards();
17761
18656
  default:
17762
18657
  throw new Error(`\u8D44\u6E90\u4E0D\u5B58\u5728: ${uri}`);
17763
18658
  }
@@ -17773,6 +18668,7 @@ var init_resource_handlers = __esm({
17773
18668
  init_handler_utils();
17774
18669
  init_errors();
17775
18670
  init_config();
18671
+ init_tools();
17776
18672
  ENTITY_PROPERTIES_LISP = `
17777
18673
  (defun entity:to-list (val)
17778
18674
  (cond
@@ -17852,11 +18748,11 @@ init_subscription_manager();
17852
18748
  init_subscription_manager();
17853
18749
  init_sse_session();
17854
18750
  init_config();
17855
- import path19 from "path";
18751
+ import path20 from "path";
17856
18752
  import fs16 from "fs";
17857
18753
  import http from "http";
17858
18754
  import https from "https";
17859
- import { fileURLToPath as fileURLToPath7 } from "url";
18755
+ import { fileURLToPath as fileURLToPath8 } from "url";
17860
18756
  import { createRequire as createRequire2 } from "module";
17861
18757
  import express2 from "express";
17862
18758
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -17869,11 +18765,12 @@ init_cad_pool();
17869
18765
  init_subscription_manager();
17870
18766
  init_mcp_server_state();
17871
18767
  init_sse_session();
18768
+ import os10 from "os";
17872
18769
  import express from "express";
17873
- import { randomUUID as randomUUID5, createHash } from "crypto";
18770
+ import { randomUUID as randomUUID6, createHash } from "crypto";
17874
18771
  import fs14 from "fs";
17875
- import path17 from "path";
17876
- import { fileURLToPath as fileURLToPath6 } from "url";
18772
+ import path18 from "path";
18773
+ import { fileURLToPath as fileURLToPath7 } from "url";
17877
18774
  import iconv from "iconv-lite";
17878
18775
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
17879
18776
 
@@ -17986,7 +18883,6 @@ var TOOL_PERMISSIONS = {
17986
18883
  "measure_*": "read",
17987
18884
  "stream_*": "read",
17988
18885
  "watch_list": "read",
17989
- "pipeline_list": "read",
17990
18886
  // Write tools
17991
18887
  "create_*": "write",
17992
18888
  "set_*": "write",
@@ -18006,8 +18902,6 @@ var TOOL_PERMISSIONS = {
18006
18902
  "import_*": "import",
18007
18903
  // Admin
18008
18904
  "config_*": "admin",
18009
- "pipeline_save": "admin",
18010
- "pipeline_delete": "admin",
18011
18905
  "webhook_*": "admin",
18012
18906
  "watch_remove": "admin"
18013
18907
  };
@@ -18041,11 +18935,11 @@ function getApiKeyRole(apiKey2, config2) {
18041
18935
 
18042
18936
  // src/routes.js
18043
18937
  init_constants();
18044
- var __dirname6 = path17.dirname(fileURLToPath6(import.meta.url));
18045
- var htmlDir = path17.join(__dirname6, "html");
18938
+ var __dirname6 = path18.dirname(fileURLToPath7(import.meta.url));
18939
+ var htmlDir = path18.join(__dirname6, "html");
18046
18940
  function loadHtml(name, replacements = {}) {
18047
18941
  if (!htmlCache[name]) {
18048
- htmlCache[name] = fs14.readFileSync(path17.join(htmlDir, `${name}.html`), "utf-8");
18942
+ htmlCache[name] = fs14.readFileSync(path18.join(htmlDir, `${name}.html`), "utf-8");
18049
18943
  }
18050
18944
  let html = htmlCache[name];
18051
18945
  for (const [key, value] of Object.entries(replacements)) {
@@ -18058,7 +18952,7 @@ var cachedToolsJson = null;
18058
18952
  var cachedToolsJsonAt = 0;
18059
18953
  function getToolsJson() {
18060
18954
  if (cachedToolsJson && Date.now() - cachedToolsJsonAt < 3e4) return cachedToolsJson;
18061
- cachedToolsJson = JSON.stringify(tools41.map((t) => ({
18955
+ cachedToolsJson = JSON.stringify(tools43.map((t) => ({
18062
18956
  name: t.name,
18063
18957
  description: t.description,
18064
18958
  inputSchema: t.inputSchema
@@ -18128,15 +19022,45 @@ function createAuthMiddleware() {
18128
19022
  return res.status(401).json({ error: "Unauthorized" });
18129
19023
  };
18130
19024
  }
19025
+ function originMatches(pattern, origin) {
19026
+ if (pattern === "*") return true;
19027
+ if (!origin) return false;
19028
+ const getHost = (s) => s.replace(/^https?:\/\//, "").replace(/:\d+$/, "").toLowerCase();
19029
+ const oHost = getHost(origin);
19030
+ let pHost = getHost(pattern);
19031
+ if (pHost.startsWith("*.")) {
19032
+ const suffix = pHost.slice(1);
19033
+ return oHost === suffix.slice(1) || oHost.endsWith(suffix);
19034
+ }
19035
+ return oHost === pHost;
19036
+ }
18131
19037
  function createCorsMiddleware() {
19038
+ const allowedOrigins = corsOrigin.split(",").map((s) => s.trim()).filter(Boolean);
19039
+ const allowAll = allowedOrigins.includes("*");
18132
19040
  return (req, res, next) => {
18133
19041
  if (!enableCors) return next();
18134
- res.setHeader("Access-Control-Allow-Origin", corsOrigin);
19042
+ const origin = req.headers.origin;
19043
+ const isPnaPreflight = req.method === "OPTIONS" && req.headers["access-control-request-private-network"] === "true";
19044
+ if (allowAll) {
19045
+ if (isPnaPreflight && origin) {
19046
+ res.setHeader("Access-Control-Allow-Origin", origin);
19047
+ res.setHeader("Vary", "Origin");
19048
+ } else {
19049
+ res.setHeader("Access-Control-Allow-Origin", corsOrigin);
19050
+ }
19051
+ } else if (origin && allowedOrigins.some((p) => originMatches(p, origin))) {
19052
+ res.setHeader("Access-Control-Allow-Origin", origin);
19053
+ res.setHeader("Vary", "Origin");
19054
+ } else if (!origin) {
19055
+ res.setHeader("Access-Control-Allow-Origin", allowedOrigins[0] || "*");
19056
+ } else {
19057
+ return res.status(403).json({ error: "Origin not allowed" });
19058
+ }
18135
19059
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS, CONNECT");
18136
19060
  res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Accept, mcp-session-id, last-event-id");
18137
19061
  res.setHeader("Access-Control-Expose-Headers", "mcp-session-id, content-type, x-accel-buffering");
18138
19062
  res.setHeader("Access-Control-Max-Age", "86400");
18139
- res.setHeader("Vary", "Accept");
19063
+ res.setHeader("Access-Control-Allow-Private-Network", "true");
18140
19064
  if (req.method === "OPTIONS") return res.status(204).end();
18141
19065
  next();
18142
19066
  };
@@ -18227,7 +19151,7 @@ function setupRoutes(app) {
18227
19151
  },
18228
19152
  mcp: {
18229
19153
  sessions: sessionServers.size,
18230
- tools: tools41.length,
19154
+ tools: tools43.length,
18231
19155
  sseSessions: sseCount
18232
19156
  },
18233
19157
  pool: {
@@ -18237,6 +19161,11 @@ function setupRoutes(app) {
18237
19161
  }
18238
19162
  });
18239
19163
  });
19164
+ app.get("/go", (req, res) => {
19165
+ const remoteUrl = "https://atlisp.cn/agent";
19166
+ const cid = config_default.clientId || os10.hostname();
19167
+ res.redirect(302, `${remoteUrl}?clientId=${encodeURIComponent(cid)}`);
19168
+ });
18240
19169
  app.get("/health/full", async (req, res) => {
18241
19170
  const memUsage = process.memoryUsage();
18242
19171
  const poolStats = pool ? pool.getStats() : {};
@@ -18250,7 +19179,7 @@ function setupRoutes(app) {
18250
19179
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
18251
19180
  cad: { connected: cad.connected, platform: cad.getPlatform?.(), version: cad.getVersion?.(), commandQueueSize: cad._getQueueSize ? cad._getQueueSize() : 0, restartCount: cad._getRestartCount ? cad._getRestartCount() : 0 },
18252
19181
  system: { uptime: process.uptime(), memory: { rssMB: Math.round(memUsage.rss / 1024 / 1024), heapUsedMB: Math.round(memUsage.heapUsed / 1024 / 1024), heapTotalMB: Math.round(memUsage.heapTotal / 1024 / 1024) }, cpu: process.cpuUsage() },
18253
- mcp: { sessions: sessionServers.size, tools: tools41.length, sseSessions: sseCount },
19182
+ mcp: { sessions: sessionServers.size, tools: tools43.length, sseSessions: sseCount },
18254
19183
  pool: poolStats
18255
19184
  });
18256
19185
  });
@@ -18288,7 +19217,7 @@ function setupRoutes(app) {
18288
19217
  app.get("/api/docs", (req, res) => {
18289
19218
  const html = loadHtml("api-docs", {
18290
19219
  VERSION: SERVER_VERSION,
18291
- TOOLS_COUNT: tools41.length
19220
+ TOOLS_COUNT: tools43.length
18292
19221
  });
18293
19222
  res.setHeader("Content-Type", "text/html; charset=utf-8");
18294
19223
  res.send(html);
@@ -18309,7 +19238,7 @@ function setupRoutes(app) {
18309
19238
  SESSIONS_COUNT: sessionServers.size,
18310
19239
  CAD_STATUS_CLASS: cadConnected ? "status-ok" : "status-error",
18311
19240
  CAD_STATUS_TEXT: cadStatusText,
18312
- TOOLS_COUNT: tools41.length
19241
+ TOOLS_COUNT: tools43.length
18313
19242
  });
18314
19243
  res.setHeader("Content-Type", "text/html; charset=utf-8");
18315
19244
  res.send(html);
@@ -18327,7 +19256,7 @@ function setupRoutes(app) {
18327
19256
  app.get("/sse", mcpLimiter, handleSse);
18328
19257
  app.post("/message", mcpLimiter, handleMessage);
18329
19258
  app.get("/cad/events", (req, res) => {
18330
- const sessionId = randomUUID5();
19259
+ const sessionId = randomUUID6();
18331
19260
  subscribeEvents(sessionId, res);
18332
19261
  req.on("close", () => {
18333
19262
  unsubscribeEvents(sessionId);
@@ -18337,7 +19266,7 @@ function setupRoutes(app) {
18337
19266
  return app;
18338
19267
  }
18339
19268
  async function handleMcpPost(req, res) {
18340
- const sessionId = req.headers["mcp-session-id"] || req.query.sessionId || randomUUID5();
19269
+ const sessionId = req.headers["mcp-session-id"] || req.query.sessionId || randomUUID6();
18341
19270
  sessionManager.createSession(sessionId);
18342
19271
  const body = req.body;
18343
19272
  if (!body) {
@@ -18405,7 +19334,7 @@ async function getMcpResponse(sessionId, body) {
18405
19334
  }
18406
19335
  async function handleSse(req, res) {
18407
19336
  const lastEventId = parseLastEventId(req);
18408
- let sessionId = randomUUID5();
19337
+ let sessionId = randomUUID6();
18409
19338
  let resumeMessages = [];
18410
19339
  if (lastEventId !== null) {
18411
19340
  const existingSession = sessionManager.get(sessionId);
@@ -18471,7 +19400,7 @@ init_cad();
18471
19400
  init_constants();
18472
19401
  init_tools();
18473
19402
  init_subscription_manager();
18474
- import { randomUUID as randomUUID6 } from "crypto";
19403
+ import { randomUUID as randomUUID7 } from "crypto";
18475
19404
  import { WebSocketServer } from "ws";
18476
19405
  var PING_INTERVAL = 3e4;
18477
19406
  var CAD_POLL_INTERVAL = 5e3;
@@ -18501,7 +19430,7 @@ function sendWelcome(ws, clientId) {
18501
19430
  server: { name: SERVER_NAME, version: SERVER_VERSION },
18502
19431
  capabilities: SERVER_CAPABILITIES,
18503
19432
  clientId,
18504
- toolCount: tools41.length,
19433
+ toolCount: tools43.length,
18505
19434
  cadConnected: cad.connected,
18506
19435
  cadPlatform: cad.connected ? cad.getPlatform() : null,
18507
19436
  cadVersion: cad.connected ? cad.getVersion() : null
@@ -18668,7 +19597,7 @@ function createWebSocketServer(httpServer) {
18668
19597
  startCadPolling();
18669
19598
  _wss = new WebSocketServer({ server: httpServer, path: "/ws" });
18670
19599
  _wss.on("connection", (ws, req) => {
18671
- const clientId = randomUUID6();
19600
+ const clientId = randomUUID7();
18672
19601
  connectedClients.set(clientId, ws);
18673
19602
  log(`WebSocket connected: ${clientId} (${connectedClients.size} total) from ${req.socket.remoteAddress}`);
18674
19603
  sendWelcome(ws, clientId);
@@ -18753,9 +19682,9 @@ function createWebSocketServer(httpServer) {
18753
19682
  // src/plugin-loader.js
18754
19683
  init_logger();
18755
19684
  import fs15 from "fs";
18756
- import path18 from "path";
18757
- import os10 from "os";
18758
- var PLUGIN_DIR = path18.join(os10.homedir(), ".atlisp", "mcp-plugins");
19685
+ import path19 from "path";
19686
+ import os11 from "os";
19687
+ var PLUGIN_DIR = path19.join(os11.homedir(), ".atlisp", "mcp-plugins");
18759
19688
  var loadedPlugins = [];
18760
19689
  var watchCallbacks = [];
18761
19690
  var watcher = null;
@@ -18780,7 +19709,7 @@ function clearModuleCache(pluginPath) {
18780
19709
  if (key) delete import.meta[key];
18781
19710
  }
18782
19711
  async function loadSinglePlugin(file) {
18783
- const pluginPath = path18.join(PLUGIN_DIR, file);
19712
+ const pluginPath = path19.join(PLUGIN_DIR, file);
18784
19713
  clearModuleCache(pluginPath);
18785
19714
  try {
18786
19715
  const plugin = await import(`${pluginPath}?t=${Date.now()}`);
@@ -18830,7 +19759,7 @@ function startPluginWatcher() {
18830
19759
  if (!filename || !filename.endsWith(".js") || filename.startsWith("_")) return;
18831
19760
  const existingIndex = loadedPlugins.findIndex((p) => p.name === filename.replace(".js", "") || p._file === filename);
18832
19761
  if (eventType === "rename") {
18833
- const filePath = path18.join(PLUGIN_DIR, filename);
19762
+ const filePath = path19.join(PLUGIN_DIR, filename);
18834
19763
  if (!fs15.existsSync(filePath)) {
18835
19764
  if (existingIndex !== -1) {
18836
19765
  loadedPlugins.splice(existingIndex, 1);
@@ -18870,19 +19799,104 @@ function stopPluginWatcher() {
18870
19799
  init_tool_registry();
18871
19800
  init_mcp_server_state();
18872
19801
 
19802
+ // src/health-reporter.js
19803
+ init_config();
19804
+ init_cad();
19805
+ init_cad_pool();
19806
+ init_mcp_server_state();
19807
+ init_subscription_manager();
19808
+ init_logger();
19809
+ import os12 from "os";
19810
+ var REPORT_URL = "https://atlisp.cn/api/mcp/health";
19811
+ var REPORT_INTERVAL = 3e4;
19812
+ var intervalHandle = null;
19813
+ function gatherHealth() {
19814
+ let cadResponseTime = 0;
19815
+ if (cad.connected) {
19816
+ const t0 = Date.now();
19817
+ try {
19818
+ cad.ping();
19819
+ } catch {
19820
+ }
19821
+ cadResponseTime = Date.now() - t0;
19822
+ }
19823
+ const memUsage = process.memoryUsage();
19824
+ let sseCount = 0;
19825
+ try {
19826
+ sseCount = sessionManager?.count?.() || 0;
19827
+ } catch {
19828
+ }
19829
+ return {
19830
+ clientId: config_default.clientId || os12.hostname(),
19831
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
19832
+ status: "ok",
19833
+ cad: {
19834
+ connected: cad.connected,
19835
+ platform: cad.getPlatform ? cad.getPlatform() : null,
19836
+ version: cad.getVersion ? cad.getVersion() : null,
19837
+ responseTime: cadResponseTime,
19838
+ commandQueueSize: cad._getQueueSize ? cad._getQueueSize() : 0,
19839
+ restartCount: cad._getRestartCount ? cad._getRestartCount() : 0
19840
+ },
19841
+ system: {
19842
+ uptime: process.uptime(),
19843
+ memory: {
19844
+ rss: Math.round(memUsage.rss / 1024 / 1024),
19845
+ heapUsed: Math.round(memUsage.heapUsed / 1024 / 1024),
19846
+ heapTotal: Math.round(memUsage.heapTotal / 1024 / 1024)
19847
+ }
19848
+ },
19849
+ mcp: {
19850
+ sessions: sessionServers.size,
19851
+ sseSessions: sseCount
19852
+ },
19853
+ pool: pool ? {
19854
+ total: pool.getStats().total,
19855
+ active: pool.getStats().active,
19856
+ instances: pool.getStats().instances
19857
+ } : null
19858
+ };
19859
+ }
19860
+ async function report() {
19861
+ try {
19862
+ const data = gatherHealth();
19863
+ const res = await fetch(REPORT_URL, {
19864
+ method: "POST",
19865
+ headers: { "Content-Type": "application/json" },
19866
+ body: JSON.stringify(data),
19867
+ signal: AbortSignal.timeout(1e4)
19868
+ });
19869
+ if (!res.ok) {
19870
+ error(`Health report failed: ${res.status}`);
19871
+ }
19872
+ } catch (e) {
19873
+ error(`Health report error: ${e.message}`);
19874
+ }
19875
+ }
19876
+ function startHealthReporter() {
19877
+ report();
19878
+ intervalHandle = setInterval(report, REPORT_INTERVAL);
19879
+ }
19880
+ function stopHealthReporter() {
19881
+ if (intervalHandle) {
19882
+ clearInterval(intervalHandle);
19883
+ intervalHandle = null;
19884
+ }
19885
+ }
19886
+
18873
19887
  // src/streamable-http.js
18874
19888
  init_logger();
18875
19889
  init_mcp_handlers();
18876
19890
  init_mcp_tools();
18877
19891
  import { StreamableHTTPServerTransport as StreamableHTTPServerTransport2 } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
18878
- import { randomUUID as randomUUID7 } from "crypto";
19892
+ import { randomUUID as randomUUID8 } from "crypto";
18879
19893
  var mcpServer = null;
18880
19894
  var streamableTransport = null;
18881
19895
  async function initStreamableHttpServer() {
18882
19896
  if (mcpServer) return;
18883
- mcpServer = createMcpServer({ tools: tools41, handleToolCall });
19897
+ mcpServer = createMcpServer({ tools: tools43, handleToolCall });
18884
19898
  streamableTransport = new StreamableHTTPServerTransport2({
18885
- sessionIdGenerator: () => randomUUID7()
19899
+ sessionIdGenerator: () => randomUUID8()
18886
19900
  });
18887
19901
  await mcpServer.connect(streamableTransport);
18888
19902
  streamableTransport.onerror = (err) => error(`Streamable HTTP transport error: ${err.message}`);
@@ -18931,9 +19945,9 @@ function setupStreamableHttpRoutes(app) {
18931
19945
  init_mcp_tools();
18932
19946
  init_function_handlers();
18933
19947
  init_constants();
18934
- var __dirname7 = path19.dirname(fileURLToPath7(import.meta.url));
19948
+ var __dirname7 = path20.dirname(fileURLToPath8(import.meta.url));
18935
19949
  var require3 = createRequire2(import.meta.url);
18936
- var { version: version2 } = require3(path19.join(__dirname7, "..", "package.json"));
19950
+ var { version: version2 } = require3(path20.join(__dirname7, "..", "package.json"));
18937
19951
  var isMcpScript = process.argv[1]?.includes("atlisp-mcp");
18938
19952
  if (isMcpScript) {
18939
19953
  const cliArgs2 = process.argv.slice(2);
@@ -18967,6 +19981,7 @@ Options:
18967
19981
  --no-lint Disable lint pre-check for eval_lisp
18968
19982
  --lint-off Same as --no-lint
18969
19983
  --api-key <key> API key for authentication
19984
+ --client-id <id> Client identifier for remote health report
18970
19985
  --security-level <type> Security level: strict, standard, relaxed (default: strict)
18971
19986
 
18972
19987
  Environment Variables:
@@ -18987,7 +20002,8 @@ Environment Variables:
18987
20002
  LINT_LEVEL Lint level (error/warn/off)
18988
20003
  NO_LINT Disable lint (true/false)
18989
20004
  REQUEST_LOG_ENABLED Enable request logging (true/false, default: true)
18990
- CORS_ORIGIN CORS origin (default: *)
20005
+ CORS_ORIGIN CORS origin (default: https://atlisp.cn)
20006
+ CLIENT_ID Client identifier for remote health report
18991
20007
  RATE_LIMIT_WINDOW Rate limit window in ms (default: 60000)
18992
20008
  RATE_LIMIT_MAX Max requests per window (default: 100)
18993
20009
 
@@ -19043,6 +20059,8 @@ For more info: https://atlisp.cn
19043
20059
  process.env.SSL_CERT_PATH = cliArgs2[++i];
19044
20060
  } else if (arg === "--api-key" && cliArgs2[i + 1]) {
19045
20061
  process.env.MCP_API_KEY = cliArgs2[++i];
20062
+ } else if (arg === "--client-id" && cliArgs2[i + 1]) {
20063
+ process.env.CLIENT_ID = cliArgs2[++i];
19046
20064
  } else if (arg === "--metrics-enabled" && cliArgs2[i + 1]) {
19047
20065
  process.env.METRICS_ENABLED = cliArgs2[++i];
19048
20066
  }
@@ -19065,7 +20083,7 @@ async function initCadConnection() {
19065
20083
  }
19066
20084
  async function startServer() {
19067
20085
  await loadPlugins();
19068
- initPlugins({ cad, config: config_default, log, registerTool, tools: tools41 });
20086
+ initPlugins({ cad, config: config_default, log, registerTool, tools: tools43 });
19069
20087
  onPluginChange((type, name) => {
19070
20088
  log(`Plugin changed: ${type} ${name}`);
19071
20089
  broadcastToolListChanged();
@@ -19075,7 +20093,7 @@ async function startServer() {
19075
20093
  startPluginWatcher();
19076
20094
  let promptsWatcher = null;
19077
20095
  try {
19078
- const promptsDir = path19.join(__dirname7, "..", "prompts");
20096
+ const promptsDir = path20.join(__dirname7, "..", "prompts");
19079
20097
  if (fs16.existsSync(promptsDir)) {
19080
20098
  promptsWatcher = fs16.watch(promptsDir, (eventType, filename) => {
19081
20099
  if (filename && filename.endsWith(".json")) {
@@ -19089,7 +20107,7 @@ async function startServer() {
19089
20107
  }
19090
20108
  if (config_default.transport === "stdio") {
19091
20109
  await initCadConnection();
19092
- const mcpServer2 = createStdioServer({ tools: tools41, handleToolCall });
20110
+ const mcpServer2 = createStdioServer({ tools: tools43, handleToolCall });
19093
20111
  const transport = new StdioServerTransport();
19094
20112
  const sessionId = "stdio-session";
19095
20113
  sessionManager.createSession(sessionId);
@@ -19137,10 +20155,12 @@ async function startServer() {
19137
20155
  const proto = useSsl ? "https" : "http";
19138
20156
  const mode = config_default.transport === "ws" ? "WebSocket" : config_default.transport === "streamable-http" ? "Streamable HTTP" : "\u591A\u4F1A\u8BDD";
19139
20157
  console.error(`@lisp MCP Server \u542F\u52A8 - ${proto}://${config_default.host}:${config_default.port} (${mode}\u6A21\u5F0F)`);
20158
+ startHealthReporter();
19140
20159
  });
19141
20160
  async function shutdown(signal) {
19142
20161
  console.error(`
19143
20162
  \u6536\u5230 ${signal}\uFF0C\u6B63\u5728\u4F18\u96C5\u5173\u95ED...`);
20163
+ stopHealthReporter();
19144
20164
  stopPluginWatcher();
19145
20165
  if (promptsWatcher) {
19146
20166
  promptsWatcher.close();
@@ -19190,5 +20210,5 @@ export {
19190
20210
  handleToolCall,
19191
20211
  loadAtlibFunctionLib,
19192
20212
  startServer,
19193
- tools41 as tools
20213
+ tools43 as tools
19194
20214
  };