@atlisp/mcp 1.8.23 → 1.8.26

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;
@@ -770,7 +851,7 @@ var init_cad = __esm({
770
851
  throw new Error(result.error || "\u53D1\u9001\u5931\u8D25");
771
852
  });
772
853
  }
773
- async sendCommandWithResult(code, encoding = null) {
854
+ async sendCommandWithResult(code, encoding = null, raw = false) {
774
855
  if (!this.connected) throw new Error("\u672A\u8FDE\u63A5 CAD");
775
856
  return new Promise((resolve, reject) => {
776
857
  this._commandQueue.enqueue({
@@ -782,7 +863,12 @@ var init_cad = __esm({
782
863
  reject
783
864
  }, PRIORITY_NORMAL);
784
865
  }).then((result) => {
785
- if (result.success) return result.result || "";
866
+ if (result.success) {
867
+ if (raw && result.rawBase64 !== void 0) {
868
+ return { text: result.result || "", rawBase64: result.rawBase64 };
869
+ }
870
+ return result.result || "";
871
+ }
786
872
  throw new Error(result.error || "\u53D1\u9001\u5931\u8D25");
787
873
  });
788
874
  }
@@ -845,21 +931,8 @@ var init_cad = __esm({
845
931
  return `Platform: ${this._product}, Version: ${this._version}, Status: Connected`;
846
932
  }
847
933
  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();
934
+ this._reset();
935
+ this._cb.reset();
863
936
  }
864
937
  async ping() {
865
938
  try {
@@ -878,12 +951,6 @@ var init_cad = __esm({
878
951
  this._activeDocName = null;
879
952
  this._monitorStarted = false;
880
953
  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
954
  this._resetWorker();
888
955
  }
889
956
  _getQueueSize() {
@@ -892,44 +959,6 @@ var init_cad = __esm({
892
959
  _getRestartCount() {
893
960
  return this._restartCount;
894
961
  }
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
962
  _spawnWorker(type) {
934
963
  const w = spawn("node", [workerPath], { stdio: ["pipe", "pipe", "pipe"] });
935
964
  w.connected = true;
@@ -940,7 +969,7 @@ var init_cad = __esm({
940
969
  w.connected = false;
941
970
  if (type === "primary") {
942
971
  if (w !== this._primaryWorker) return;
943
- this._cbFailure();
972
+ this._cb.recordFailure();
944
973
  const exitError = new Error(`Primary worker exited with code ${code}`);
945
974
  for (const [id, entry] of this._pendingRequests) {
946
975
  clearTimeout(entry.timeout);
@@ -977,7 +1006,7 @@ var init_cad = __esm({
977
1006
  this._primaryWorker = null;
978
1007
  }
979
1008
  this._primaryWorker = this._spawnWorker("primary");
980
- this._cbSuccess();
1009
+ this._cb.recordSuccess();
981
1010
  if (!this._heartbeatTimer) this._startHeartbeat();
982
1011
  this._restartCount = 0;
983
1012
  }
@@ -1118,14 +1147,9 @@ var init_cad = __esm({
1118
1147
  }, delay);
1119
1148
  }
1120
1149
  async _sendMessage(msg) {
1121
- if (!this._cbAllowRequest()) {
1150
+ if (!this._cb.allowRequest()) {
1122
1151
  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
- }
1152
+ this._cb.reset();
1129
1153
  await this._resetWorker();
1130
1154
  await this._getWorker(false);
1131
1155
  }
@@ -1141,17 +1165,17 @@ var init_cad = __esm({
1141
1165
  const p = self._pendingRequests.get(requestId);
1142
1166
  if (p) {
1143
1167
  self._pendingRequests.delete(requestId);
1144
- if (!noCircuitBreaker) self._cbFailure();
1168
+ if (!noCircuitBreaker) self._cb.recordFailure();
1145
1169
  reject(new Error("Timeout waiting for worker"));
1146
1170
  }
1147
1171
  }, MESSAGE_TIMEOUT);
1148
1172
  const entry = {
1149
1173
  resolve: (v) => {
1150
- if (!noCircuitBreaker && !entry._skipCb) self._cbSuccess();
1174
+ if (!noCircuitBreaker && !entry._skipCb) self._cb.recordSuccess();
1151
1175
  resolve(v);
1152
1176
  },
1153
1177
  reject: (e) => {
1154
- if (!noCircuitBreaker && !entry._skipCb) self._cbFailure();
1178
+ if (!noCircuitBreaker && !entry._skipCb) self._cb.recordFailure();
1155
1179
  reject(e);
1156
1180
  },
1157
1181
  timeout
@@ -1162,11 +1186,15 @@ var init_cad = __esm({
1162
1186
  } catch (e) {
1163
1187
  clearTimeout(timeout);
1164
1188
  self._pendingRequests.delete(requestId);
1165
- if (!noCircuitBreaker) self._cbFailure();
1189
+ if (!noCircuitBreaker) self._cb.recordFailure();
1166
1190
  reject(new Error(`Failed to write to worker: ${e.message}`));
1167
1191
  }
1168
1192
  });
1169
1193
  }
1194
+ _isReadOnlyMessage(msg) {
1195
+ if (msg.code) return isReadCommand(msg.code);
1196
+ return false;
1197
+ }
1170
1198
  };
1171
1199
  _activeConnection = new CadConnection();
1172
1200
  _resolveInstanceConn = null;
@@ -1175,79 +1203,79 @@ var init_cad = __esm({
1175
1203
  return _resolveConn().connected;
1176
1204
  },
1177
1205
  set connected(v) {
1178
- _activeConnection.connected = v;
1206
+ _resolveConn().connected = v;
1179
1207
  },
1180
1208
  get version() {
1181
- return _activeConnection._version;
1209
+ return _resolveConn()._version;
1182
1210
  },
1183
1211
  set version(v) {
1184
- _activeConnection._version = v;
1212
+ _resolveConn()._version = v;
1185
1213
  },
1186
1214
  get product() {
1187
- return _activeConnection._product;
1215
+ return _resolveConn()._product;
1188
1216
  },
1189
1217
  set product(v) {
1190
- _activeConnection._product = v;
1218
+ _resolveConn()._product = v;
1191
1219
  },
1192
1220
  get _platform() {
1193
- return _activeConnection._platform;
1221
+ return _resolveConn()._platform;
1194
1222
  },
1195
1223
  set _platform(v) {
1196
- _activeConnection._platform = v;
1224
+ _resolveConn()._platform = v;
1197
1225
  },
1198
1226
  async connect(platform) {
1199
- return _activeConnection.connect(platform);
1227
+ return _resolveConn().connect(platform);
1200
1228
  },
1201
1229
  async sendCommand(code) {
1202
1230
  return _resolveConn().sendCommand(code);
1203
1231
  },
1204
- async sendCommandWithResult(code, encoding) {
1205
- return _resolveConn().sendCommandWithResult(code, encoding);
1232
+ async sendCommandWithResult(code, encoding, raw) {
1233
+ return _resolveConn().sendCommandWithResult(code, encoding, raw);
1206
1234
  },
1207
1235
  getVersion() {
1208
- return _activeConnection.getVersion();
1236
+ return _resolveConn().getVersion();
1209
1237
  },
1210
1238
  getPlatform() {
1211
- return _activeConnection.getPlatform();
1239
+ return _resolveConn().getPlatform();
1212
1240
  },
1213
1241
  getConnectionId() {
1214
- return _activeConnection.getConnectionId();
1242
+ return _resolveConn().getConnectionId();
1215
1243
  },
1216
1244
  isAvailable() {
1217
- return _activeConnection.isAvailable();
1245
+ return _resolveConn().isAvailable();
1218
1246
  },
1219
1247
  async isBusy() {
1220
- return _activeConnection.isBusy();
1248
+ return _resolveConn().isBusy();
1221
1249
  },
1222
1250
  async hasDoc() {
1223
- return _activeConnection.hasDoc();
1251
+ return _resolveConn().hasDoc();
1224
1252
  },
1225
1253
  async newDoc() {
1226
- return _activeConnection.newDoc();
1254
+ return _resolveConn().newDoc();
1227
1255
  },
1228
1256
  async bringToFront() {
1229
- return _activeConnection.bringToFront();
1257
+ return _resolveConn().bringToFront();
1230
1258
  },
1231
1259
  async getActiveDocInfo() {
1232
- return _activeConnection.getActiveDocInfo();
1260
+ return _resolveConn().getActiveDocInfo();
1233
1261
  },
1234
1262
  async getInfo() {
1235
- return _activeConnection.getInfo();
1263
+ return _resolveConn().getInfo();
1236
1264
  },
1237
1265
  async disconnect() {
1238
- return _activeConnection.disconnect();
1266
+ return _resolveConn().disconnect();
1239
1267
  },
1240
1268
  async ping() {
1241
- return _activeConnection.ping();
1269
+ return _resolveConn().ping();
1242
1270
  },
1243
1271
  _reset() {
1244
- return _activeConnection._reset();
1272
+ return _resolveConn()._reset();
1245
1273
  },
1246
1274
  _getQueueSize() {
1247
- return _activeConnection._getQueueSize();
1275
+ return _resolveConn()._getQueueSize();
1248
1276
  },
1249
1277
  _getRestartCount() {
1250
- return _activeConnection._getRestartCount();
1278
+ return _resolveConn()._getRestartCount();
1251
1279
  }
1252
1280
  };
1253
1281
  cad_default = CadConnection;
@@ -1258,7 +1286,7 @@ var init_cad = __esm({
1258
1286
  import path3 from "path";
1259
1287
  import { fileURLToPath as fileURLToPath2 } from "url";
1260
1288
  import { createRequire } from "module";
1261
- var CAD_PLATFORMS, FILE_EXTENSIONS, __dirname2, require2, version, SERVER_NAME, SERVER_VERSION, SERVER_CAPABILITIES, MOCK_PACKAGES;
1289
+ var CAD_PLATFORMS, FILE_EXTENSIONS, __dirname2, require2, version, SERVER_NAME, SERVER_VERSION, SERVER_CAPABILITIES, MOCK_PACKAGES, CAD_RESOURCE_URIS;
1262
1290
  var init_constants = __esm({
1263
1291
  "src/constants.js"() {
1264
1292
  CAD_PLATFORMS = ["AutoCAD", "ZWCAD", "GStarCAD", "BricsCAD"];
@@ -1281,6 +1309,17 @@ var init_constants = __esm({
1281
1309
  completions: {}
1282
1310
  };
1283
1311
  MOCK_PACKAGES = [];
1312
+ CAD_RESOURCE_URIS = [
1313
+ "atlisp://cad/info",
1314
+ "atlisp://dwg/name",
1315
+ "atlisp://dwg/path",
1316
+ "atlisp://dwg/entities",
1317
+ "atlisp://dwg/tbl",
1318
+ "atlisp://dwg/block-refs",
1319
+ "atlisp://dwg/text-content",
1320
+ "atlisp://cad/dwgs",
1321
+ "atlisp://packages"
1322
+ ];
1284
1323
  }
1285
1324
  });
1286
1325
 
@@ -1481,7 +1520,10 @@ var init_resource_defs = __esm({
1481
1520
  { uri: "atlisp://standards/drafting", name: "Drafting Standards", description: "CAD \u5236\u56FE\u89C4\u8303", mimeType: "application/json", subscribable: false },
1482
1521
  { uri: "atlisp://standards/coding", name: "Coding Standards", description: "@lisp \u7F16\u7801\u89C4\u8303", mimeType: "application/json", subscribable: false },
1483
1522
  { 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 }
1523
+ { 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 },
1524
+ { 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 },
1525
+ { 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 },
1526
+ { 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
1527
  ];
1486
1528
  TBL_QUERIES = {
1487
1529
  layer: { name: "layer", lisp: `(progn
@@ -1528,49 +1570,70 @@ var init_resource_defs = __esm({
1528
1570
  });
1529
1571
 
1530
1572
  // src/handlers/resource-cache.js
1573
+ function _getCache() {
1574
+ const key = _instanceKey || _defaultKey;
1575
+ if (!_instanceCaches.has(key)) {
1576
+ _instanceCaches.set(key, /* @__PURE__ */ new Map());
1577
+ }
1578
+ return _instanceCaches.get(key);
1579
+ }
1580
+ function _ttl() {
1581
+ return config_default.resourceCacheTtl;
1582
+ }
1531
1583
  function ensureCleanup() {
1532
1584
  if (cleanupTimer) return;
1533
1585
  cleanupTimer = setInterval(() => {
1534
1586
  const now = Date.now();
1535
- for (const [key, entry] of resourceCache) {
1536
- if (now - entry.timestamp >= CACHE_TTL) {
1537
- resourceCache.delete(key);
1587
+ const ttl = _ttl();
1588
+ for (const cache of _instanceCaches.values()) {
1589
+ for (const [key, entry] of cache) {
1590
+ if (now - entry.timestamp >= ttl) {
1591
+ cache.delete(key);
1592
+ }
1538
1593
  }
1539
1594
  }
1540
1595
  }, CLEANUP_INTERVAL);
1541
1596
  cleanupTimer.unref();
1542
1597
  }
1598
+ function setInstanceKey(key) {
1599
+ _instanceKey = key || null;
1600
+ }
1543
1601
  function getCached(key) {
1544
- const entry = resourceCache.get(key);
1545
- if (entry && Date.now() - entry.timestamp < CACHE_TTL) {
1602
+ const cache = _getCache();
1603
+ const entry = cache.get(key);
1604
+ if (entry && Date.now() - entry.timestamp < _ttl()) {
1546
1605
  return entry.data;
1547
1606
  }
1548
- resourceCache.delete(key);
1607
+ cache.delete(key);
1549
1608
  return null;
1550
1609
  }
1551
1610
  function setCache(key, data) {
1552
- resourceCache.set(key, { data, timestamp: Date.now() });
1611
+ _getCache().set(key, { data, timestamp: Date.now() });
1553
1612
  }
1554
1613
  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);
1614
+ const basePrefix = URI_CACHE_PREFIX[uri];
1615
+ if (basePrefix) {
1616
+ const cache = _getCache();
1617
+ for (const key of cache.keys()) {
1618
+ if (key === basePrefix || key.startsWith(basePrefix + ":")) {
1619
+ cache.delete(key);
1560
1620
  }
1561
1621
  }
1562
1622
  } else if (!uri) {
1563
- resourceCache.clear();
1623
+ const key = _instanceKey || _defaultKey;
1624
+ _instanceCaches.delete(key);
1625
+ _instanceCaches.set(key, /* @__PURE__ */ new Map());
1564
1626
  }
1565
1627
  }
1566
- var CACHE_TTL, resourceCache, CLEANUP_INTERVAL, cleanupTimer, URI_CACHE_PREFIX;
1628
+ var CLEANUP_INTERVAL, cleanupTimer, _instanceKey, _instanceCaches, _defaultKey, URI_CACHE_PREFIX;
1567
1629
  var init_resource_cache = __esm({
1568
1630
  "src/handlers/resource-cache.js"() {
1569
1631
  init_config();
1570
- CACHE_TTL = config_default.resourceCacheTtl;
1571
- resourceCache = /* @__PURE__ */ new Map();
1572
1632
  CLEANUP_INTERVAL = 6e4;
1573
1633
  cleanupTimer = null;
1634
+ _instanceKey = null;
1635
+ _instanceCaches = /* @__PURE__ */ new Map();
1636
+ _defaultKey = "__default__";
1574
1637
  ensureCleanup();
1575
1638
  URI_CACHE_PREFIX = {
1576
1639
  "atlisp://cad/info": "cad:info",
@@ -1591,6 +1654,23 @@ function createError(code, details = {}) {
1591
1654
  const def = ERROR_DEFS[code] || ERROR_DEFS[ERROR_CODES.INTERNAL_ERROR];
1592
1655
  return new MCPError(code, def.message, details);
1593
1656
  }
1657
+ function toMcpError(error2) {
1658
+ if (error2 instanceof MCPError) return error2;
1659
+ if (error2 instanceof Error) {
1660
+ return new MCPError(ERROR_CODES.INTERNAL_ERROR, error2.message, { stack: error2.stack });
1661
+ }
1662
+ return new MCPError(ERROR_CODES.INTERNAL_ERROR, String(error2));
1663
+ }
1664
+ function mcpErrorResponse(error2) {
1665
+ const mcpErr = toMcpError(error2);
1666
+ const json = mcpErr.toJSON();
1667
+ return {
1668
+ content: [{ type: "text", text: `[${json.code}] ${json.message}
1669
+ \u63D0\u793A: ${json.hint}
1670
+ \u5EFA\u8BAE\u5DE5\u5177: ${json.suggestedTools.join(", ") || "\u65E0"}` }],
1671
+ isError: true
1672
+ };
1673
+ }
1594
1674
  function mcpSuccessResponse(text) {
1595
1675
  return { content: [{ type: "text", text: String(text) }] };
1596
1676
  }
@@ -1604,9 +1684,14 @@ var init_errors = __esm({
1604
1684
  LISP_PAREN_MISMATCH: "LISP_PAREN_MISMATCH",
1605
1685
  TIMEOUT: "TIMEOUT",
1606
1686
  INVALID_PARAMS: "INVALID_PARAMS",
1687
+ INVALID_ARGS: "INVALID_ARGS",
1607
1688
  METHOD_NOT_FOUND: "METHOD_NOT_FOUND",
1689
+ HANDLER_NOT_FOUND: "HANDLER_NOT_FOUND",
1608
1690
  RESOURCE_NOT_FOUND: "RESOURCE_NOT_FOUND",
1609
1691
  PACKAGE_NOT_FOUND: "PACKAGE_NOT_FOUND",
1692
+ OPERATION_FAILED: "OPERATION_FAILED",
1693
+ RATE_LIMITED: "RATE_LIMITED",
1694
+ SESSION_NOT_FOUND: "SESSION_NOT_FOUND",
1610
1695
  INTERNAL_ERROR: "INTERNAL_ERROR"
1611
1696
  };
1612
1697
  ERROR_DEFS = {
@@ -1640,6 +1725,31 @@ var init_errors = __esm({
1640
1725
  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
1726
  tools: ["search_tools", "list_tools_by_category"]
1642
1727
  },
1728
+ [ERROR_CODES.INVALID_ARGS]: {
1729
+ message: "\u53C2\u6570\u6821\u9A8C\u5931\u8D25",
1730
+ 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",
1731
+ tools: ["search_tools", "list_tools_by_category"]
1732
+ },
1733
+ [ERROR_CODES.HANDLER_NOT_FOUND]: {
1734
+ message: "\u5DE5\u5177\u672A\u627E\u5230",
1735
+ 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",
1736
+ tools: ["search_tools", "list_tools_by_category"]
1737
+ },
1738
+ [ERROR_CODES.OPERATION_FAILED]: {
1739
+ message: "\u64CD\u4F5C\u6267\u884C\u5931\u8D25",
1740
+ 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",
1741
+ tools: ["get_system_status", "get_cad_info"]
1742
+ },
1743
+ [ERROR_CODES.RATE_LIMITED]: {
1744
+ message: "\u8C03\u7528\u9891\u7387\u8D85\u9650",
1745
+ hint: "\u5DE5\u5177\u8C03\u7528\u8FC7\u4E8E\u9891\u7E41\u3002\u8BF7\u7B49\u5F85\u4E00\u6BB5\u65F6\u95F4\u540E\u518D\u8BD5",
1746
+ tools: []
1747
+ },
1748
+ [ERROR_CODES.SESSION_NOT_FOUND]: {
1749
+ message: "\u4F1A\u8BDD\u672A\u627E\u5230",
1750
+ hint: "\u6307\u5B9A ID \u7684\u4F1A\u8BDD\u4E0D\u5B58\u5728\u3002\u8BF7\u68C0\u67E5 sessionId \u53C2\u6570\u662F\u5426\u6B63\u786E",
1751
+ tools: ["lisp_repl_list"]
1752
+ },
1643
1753
  [ERROR_CODES.METHOD_NOT_FOUND]: {
1644
1754
  message: "\u65B9\u6CD5\u4E0D\u5B58\u5728",
1645
1755
  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 +1894,8 @@ __export(renderer_exports, {
1784
1894
  renderPrompt: () => renderPrompt,
1785
1895
  renderTemplate: () => renderTemplate
1786
1896
  });
1787
- function resolvePath(obj, path20) {
1788
- const parts = path20.split(/[.\[\]]/g).filter(Boolean);
1897
+ function resolvePath(obj, path21) {
1898
+ const parts = path21.split(/[.\[\]]/g).filter(Boolean);
1789
1899
  let val = obj;
1790
1900
  for (const p of parts) {
1791
1901
  if (val == null) return void 0;
@@ -1795,8 +1905,8 @@ function resolvePath(obj, path20) {
1795
1905
  }
1796
1906
  function renderTemplate(template, args) {
1797
1907
  return template.replace(PARAM_RE, (match, rawExpr, expr) => {
1798
- const path20 = rawExpr || expr;
1799
- const val = resolvePath(args, path20);
1908
+ const path21 = rawExpr || expr;
1909
+ const val = resolvePath(args, path21);
1800
1910
  if (val == null) return "";
1801
1911
  const strVal = String(val);
1802
1912
  if (rawExpr) return strVal;
@@ -2228,8 +2338,8 @@ var init_sse_session = __esm({
2228
2338
  sendMessage(data) {
2229
2339
  return this.sendEvent(SSE_EVENTS.MESSAGE, data);
2230
2340
  }
2231
- sendEndpoint(path20) {
2232
- return this.sendEvent(SSE_EVENTS.ENDPOINT, path20);
2341
+ sendEndpoint(path21) {
2342
+ return this.sendEvent(SSE_EVENTS.ENDPOINT, path21);
2233
2343
  }
2234
2344
  sendError(code, message, id = null) {
2235
2345
  return this.sendEvent(SSE_EVENTS.ERROR, { code, message }, id);
@@ -2951,7 +3061,8 @@ var init_info_tools = __esm({
2951
3061
  }
2952
3062
  },
2953
3063
  { 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)" } } } }
3064
+ { 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)" } } } },
3065
+ { 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
3066
  ];
2956
3067
  }
2957
3068
  });
@@ -5492,12 +5603,12 @@ var init_pool_tools = __esm({
5492
5603
  inputSchema: {
5493
5604
  type: "object",
5494
5605
  properties: {
5495
- key: { type: "string", description: "\u5B9E\u4F8B\u6807\u8BC6\u952E\u540D\uFF08\u5982 autocad, zwcad, mycad\uFF09" },
5606
+ 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
5607
  platform: { type: "string", description: "CAD \u5E73\u53F0: AutoCAD, ZWCAD, GStarCAD, BricsCAD\uFF08\u53EF\u9009\uFF0C\u81EA\u52A8\u68C0\u6D4B\uFF09" },
5608
+ 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
5609
  label: { type: "string", description: "\u5B9E\u4F8B\u663E\u793A\u6807\u7B7E\uFF08\u53EF\u9009\uFF09" },
5498
5610
  makeActive: { type: "boolean", description: "\u8FDE\u63A5\u540E\u8BBE\u4E3A\u6D3B\u52A8\u5B9E\u4F8B\uFF08\u9ED8\u8BA4 true\uFF09" }
5499
- },
5500
- required: ["key"]
5611
+ }
5501
5612
  }
5502
5613
  },
5503
5614
  {
@@ -6457,7 +6568,66 @@ var init_debug_tools = __esm({
6457
6568
  }
6458
6569
  });
6459
6570
 
6571
+ // src/tools/concurrent-tools.js
6572
+ var tools42;
6573
+ var init_concurrent_tools = __esm({
6574
+ "src/tools/concurrent-tools.js"() {
6575
+ tools42 = [
6576
+ {
6577
+ name: "batch_concurrent_tasks",
6578
+ 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",
6579
+ inputSchema: {
6580
+ type: "object",
6581
+ properties: {
6582
+ tasks: {
6583
+ type: "array",
6584
+ items: {
6585
+ type: "object",
6586
+ properties: {
6587
+ tool: { type: "string", description: "\u5DE5\u5177\u540D\u79F0" },
6588
+ args: { type: "object", description: "\u5DE5\u5177\u53C2\u6570\uFF08\u4E0D\u542B instance\uFF09" },
6589
+ instance: { type: "string", description: "\u76EE\u6807\u5B9E\u4F8B key" }
6590
+ },
6591
+ required: ["tool", "instance"]
6592
+ },
6593
+ description: "\u8981\u5E76\u53D1\u6267\u884C\u7684\u4EFB\u52A1\u5217\u8868"
6594
+ }
6595
+ },
6596
+ required: ["tasks"]
6597
+ }
6598
+ }
6599
+ ];
6600
+ }
6601
+ });
6602
+
6460
6603
  // src/tools/index.js
6604
+ function getToolCategory(name) {
6605
+ const n = name;
6606
+ 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";
6607
+ if (/^(move_|copy_|rotate_|scale_|mirror_|offset_|array_|batch_)/.test(n)) return "\u4FEE\u6539";
6608
+ if (/^(trim_|extend_|fillet|chamfer|break_|join_|stretch)/.test(n)) return "\u7F16\u8F91";
6609
+ if (/^(create_layer|delete_layer|set_layer|layer_)/.test(n)) return "\u56FE\u5C42";
6610
+ if (/^(create_block|insert_block|explode_block|get_block|set_block|batch_get_block|batch_set_block)/.test(n)) return "\u5757";
6611
+ if (/^(create_dim|list_dim|set_dim|create_hatch|set_hatch)/.test(n)) return "\u6807\u6CE8\u4E0E\u586B\u5145";
6612
+ 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";
6613
+ if (/^(create_viewport|set_viewport|lock_viewport|unlock_viewport|maximize_viewport)/.test(n)) return "\u89C6\u53E3";
6614
+ if (/^find_/.test(n)) return "\u7A7A\u95F4\u67E5\u8BE2";
6615
+ if (/^(select_|get_entity|set_entity|delete_entity|get_bounding_box)/.test(n)) return "\u9009\u62E9\u4E0E\u5C5E\u6027";
6616
+ if (/^(open_dwg|save_dwg|export_|import_|close_dwg|publish_)/.test(n)) return "\u6587\u4EF6";
6617
+ if (/^(plot_|save_pdf|batch_plot|publish_layouts)/.test(n)) return "\u6253\u5370";
6618
+ 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";
6619
+ 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";
6620
+ 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";
6621
+ 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";
6622
+ if (/^(purge_|drawing_audit|drawing_recover|overkill)/.test(n)) return "\u6E05\u7406";
6623
+ if (/^(find_text|replace_text|set_text_|justify_|convert_text|explode_text)/.test(n)) return "\u6587\u5B57";
6624
+ 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";
6625
+ if (/^(list_packages|search_packages|install_package|get_function_usage|list_symbols|import_funlib)/.test(n)) return "\u5305\u4E0E\u51FD\u6570";
6626
+ if (/^(watch_|lisp_repl|webhook_|sandbox_|audit_|cad_pool_|external_|list_data|export_to|import_external)/.test(n)) return "\u9AD8\u7EA7";
6627
+ if (/^(list_prompts|get_prompt|search_tools|list_tools_by_category|get_tool_info)/.test(n)) return "\u5143\u5DE5\u5177";
6628
+ if (/^(agent_)/.test(n)) return "Agent\u4E0A\u4E0B\u6587";
6629
+ return "\u5176\u4ED6";
6630
+ }
6461
6631
  function addAnnotations(tool) {
6462
6632
  const isDeprecated = DEPRECATED_TOOLS.has(tool.name);
6463
6633
  const isPoolTool = tool.name.startsWith("cad_pool_") || tool.name === "session_pin_instance" || tool.name === "session_unpin_instance";
@@ -6477,11 +6647,12 @@ function addAnnotations(tool) {
6477
6647
  destructiveHint: tool.name.startsWith("delete_") || tool.name.startsWith("purge_") || tool.name.startsWith("boolean_") || EXTRA_DESTRUCTIVE.has(tool.name),
6478
6648
  idempotentHint: READ_ONLY_TOOLS.has(tool.name) || IDEMPOTENT_WRITE_TOOLS.has(tool.name),
6479
6649
  openWorldHint: OPEN_WORLD_TOOLS.has(tool.name),
6650
+ category: getToolCategory(tool.name),
6480
6651
  ...isDeprecated ? { deprecatedReason: "This tool has been superseded. Check documentation for alternatives." } : {}
6481
6652
  }
6482
6653
  };
6483
6654
  }
6484
- var READ_ONLY_TOOLS, DEPRECATED_TOOLS, EXTRA_DESTRUCTIVE, IDEMPOTENT_WRITE_TOOLS, OPEN_WORLD_TOOLS, tools42, toolsByName;
6655
+ var READ_ONLY_TOOLS, DEPRECATED_TOOLS, EXTRA_DESTRUCTIVE, IDEMPOTENT_WRITE_TOOLS, OPEN_WORLD_TOOLS, tools43, toolsByName;
6485
6656
  var init_tools = __esm({
6486
6657
  "src/tools/index.js"() {
6487
6658
  init_cad_tools();
@@ -6525,6 +6696,7 @@ var init_tools = __esm({
6525
6696
  init_batch_transaction_tools();
6526
6697
  init_lint_tools();
6527
6698
  init_debug_tools();
6699
+ init_concurrent_tools();
6528
6700
  READ_ONLY_TOOLS = /* @__PURE__ */ new Set([
6529
6701
  "get_cad_info",
6530
6702
  "get_platform_info",
@@ -6664,7 +6836,7 @@ var init_tools = __esm({
6664
6836
  "import_page_setup",
6665
6837
  "load_linetype"
6666
6838
  ]);
6667
- tools42 = [
6839
+ tools43 = [
6668
6840
  ...tools,
6669
6841
  ...tools2,
6670
6842
  ...tools3,
@@ -6705,9 +6877,10 @@ var init_tools = __esm({
6705
6877
  ...tools39,
6706
6878
  ...tools37,
6707
6879
  ...tools40,
6708
- ...tools41
6880
+ ...tools41,
6881
+ ...tools42
6709
6882
  ].map(addAnnotations);
6710
- toolsByName = new Map(tools42.map((t) => [t.name, t]));
6883
+ toolsByName = new Map(tools43.map((t) => [t.name, t]));
6711
6884
  }
6712
6885
  });
6713
6886
 
@@ -6760,7 +6933,7 @@ function parseLispList(str) {
6760
6933
  });
6761
6934
  }
6762
6935
  function createMcpServer(handlers = {}) {
6763
- const { tools: tools43 = [], handleToolCall: handleToolCall2 = () => ({ content: [] }) } = handlers;
6936
+ const { tools: tools44 = [], handleToolCall: handleToolCall2 = () => ({ content: [] }) } = handlers;
6764
6937
  const server = new Server(
6765
6938
  { name: SERVER_NAME, version: SERVER_VERSION },
6766
6939
  {
@@ -6783,8 +6956,8 @@ function createMcpServer(handlers = {}) {
6783
6956
  const params = request.params || {};
6784
6957
  const cursor = decodeCursor(params.cursor);
6785
6958
  const PAGE_SIZE = 100;
6786
- const page = tools43.slice(cursor, cursor + PAGE_SIZE);
6787
- const nextCursor = cursor + PAGE_SIZE < tools43.length ? encodeCursor(cursor + PAGE_SIZE) : void 0;
6959
+ const page = tools44.slice(cursor, cursor + PAGE_SIZE);
6960
+ const nextCursor = cursor + PAGE_SIZE < tools44.length ? encodeCursor(cursor + PAGE_SIZE) : void 0;
6788
6961
  return { tools: page, nextCursor };
6789
6962
  });
6790
6963
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
@@ -7133,11 +7306,11 @@ function createMcpServer(handlers = {}) {
7133
7306
  if (p.toLowerCase().startsWith(prefix)) values.push(p);
7134
7307
  }
7135
7308
  } else if (ref?.type === "tool") {
7136
- for (const t of tools43) {
7309
+ for (const t of tools44) {
7137
7310
  if (t.name.toLowerCase().startsWith(prefix)) values.push(t.name);
7138
7311
  }
7139
7312
  } else if (ref?.type === "ref/parameter") {
7140
- const toolDef = tools43.find((t) => t.name === ref.name);
7313
+ const toolDef = tools44.find((t) => t.name === ref.name);
7141
7314
  if (toolDef?.inputSchema?.properties) {
7142
7315
  const paramName = argument.name;
7143
7316
  const paramDef = toolDef.inputSchema.properties[paramName];
@@ -8361,6 +8534,7 @@ __export(cad_handlers_exports, {
8361
8534
  newDocument: () => newDocument,
8362
8535
  setSystemVariable: () => setSystemVariable
8363
8536
  });
8537
+ import iconv from "iconv-lite";
8364
8538
  async function connectCad(platform = null) {
8365
8539
  log(`connect_cad called${platform ? ` (target: ${platform})` : ""}, cad.connected before: ${cad.connected}`);
8366
8540
  try {
@@ -8443,9 +8617,15 @@ var init_cad_handlers = __esm({
8443
8617
  ${detail}`);
8444
8618
  }
8445
8619
  if (withResult) {
8446
- const result = await cad.sendCommandWithResult(trimmed, encoding);
8447
- if (result !== null) {
8448
- return mcpSuccess(result);
8620
+ const useRaw = encoding ? true : false;
8621
+ const resp = await cad.sendCommandWithResult(trimmed, encoding, useRaw);
8622
+ if (resp !== null) {
8623
+ if (useRaw && resp.rawBase64) {
8624
+ const buf = Buffer.from(resp.rawBase64, "base64");
8625
+ const decoded = iconv.decode(buf, encoding);
8626
+ return mcpSuccess(decoded);
8627
+ }
8628
+ return mcpSuccess(resp);
8449
8629
  }
8450
8630
  return mcpError("\u6267\u884C\u5931\u8D25\u6216\u65E0\u8FD4\u56DE\u503C");
8451
8631
  }
@@ -10670,6 +10850,87 @@ var init_lint_handlers = __esm({
10670
10850
  }
10671
10851
  });
10672
10852
 
10853
+ // src/handlers/meta-handlers.js
10854
+ var meta_handlers_exports = {};
10855
+ __export(meta_handlers_exports, {
10856
+ get_prompt_handler: () => get_prompt_handler,
10857
+ get_tool_info: () => get_tool_info,
10858
+ list_prompts_handler: () => list_prompts_handler,
10859
+ list_tools_by_category: () => list_tools_by_category,
10860
+ search_tools: () => search_tools
10861
+ });
10862
+ async function search_tools(a) {
10863
+ const q = (a.query || "").toLowerCase();
10864
+ const results = tools43.filter((t) => t.name.toLowerCase().includes(q) || (t.description || "").toLowerCase().includes(q));
10865
+ const text = results.length ? `\u627E\u5230 ${results.length} \u4E2A\u5DE5\u5177:
10866
+ ${results.map((t) => `${t.name}: ${t.description}`).join("\n")}` : `\u672A\u627E\u5230\u5305\u542B "${a.query}" \u7684\u5DE5\u5177`;
10867
+ return { content: [{ type: "text", text }] };
10868
+ }
10869
+ async function list_tools_by_category(a) {
10870
+ const categories = {};
10871
+ for (const t of tools43) {
10872
+ const cat = t.annotations?.category || "\u5176\u4ED6";
10873
+ if (!categories[cat]) categories[cat] = [];
10874
+ categories[cat].push(t.name);
10875
+ }
10876
+ if (a.category) {
10877
+ const names = categories[a.category];
10878
+ return { content: [{ type: "text", text: names ? `${a.category} (${names.length}):
10879
+ ${names.join("\n")}` : `\u672A\u627E\u5230\u5206\u7C7B: ${a.category}` }] };
10880
+ }
10881
+ const lines = [];
10882
+ for (const [cat, names] of Object.entries(categories)) {
10883
+ lines.push(`## ${cat} (${names.length})`);
10884
+ }
10885
+ lines.push(`
10886
+ \u5171 ${Object.keys(categories).length} \u4E2A\u5206\u7C7B`);
10887
+ return { content: [{ type: "text", text: lines.join("\n") }] };
10888
+ }
10889
+ async function get_tool_info(a) {
10890
+ if (!a.name) return { content: [{ type: "text", text: "\u9700\u8981 name \u53C2\u6570\uFF08\u5DE5\u5177\u540D\u79F0\uFF09" }], isError: true };
10891
+ const tool = tools43.find((t) => t.name === a.name);
10892
+ if (!tool) return { content: [{ type: "text", text: `\u672A\u627E\u5230\u5DE5\u5177: ${a.name}` }], isError: true };
10893
+ const ann = tool.annotations || {};
10894
+ const required = (tool.inputSchema?.required || []).join(", ") || "\u65E0";
10895
+ const props = tool.inputSchema?.properties || {};
10896
+ const params = Object.entries(props).map(([k, v]) => {
10897
+ const req = required.includes(k) ? "\u5FC5\u586B" : "\u53EF\u9009";
10898
+ const desc = v.description || "";
10899
+ return ` ${k} (${req}): ${desc}`;
10900
+ }).join("\n");
10901
+ const lines = [
10902
+ `## ${tool.name}`,
10903
+ `\u63CF\u8FF0: ${tool.description || "\u65E0"}`,
10904
+ `\u5206\u7C7B: ${ann.category || "\u5176\u4ED6"}`,
10905
+ `\u53EA\u8BFB: ${ann.readOnlyHint ? "\u662F" : "\u5426"}`,
10906
+ `\u7834\u574F\u6027: ${ann.destructiveHint ? "\u662F" : "\u5426"}`,
10907
+ `\u5E42\u7B49: ${ann.idempotentHint ? "\u662F" : "\u5426"}`,
10908
+ `\u5F00\u653E\u4E16\u754C: ${ann.openWorldHint ? "\u662F" : "\u5426"}`,
10909
+ ``,
10910
+ `\u53C2\u6570:`,
10911
+ params || " \u65E0\u53C2\u6570"
10912
+ ];
10913
+ if (ann.deprecatedReason) {
10914
+ lines.push(`
10915
+ \u5DF2\u5F03\u7528: ${ann.deprecatedReason}`);
10916
+ }
10917
+ return { content: [{ type: "text", text: lines.join("\n") }] };
10918
+ }
10919
+ async function list_prompts_handler() {
10920
+ const prompts = await listPrompts2();
10921
+ return { content: [{ type: "text", text: JSON.stringify(prompts) }] };
10922
+ }
10923
+ async function get_prompt_handler(a) {
10924
+ const prompt = await getPrompt2(a.name, a.args || {});
10925
+ return { content: prompt.messages.map((m) => ({ type: "text", text: m.content.text || m.content })) };
10926
+ }
10927
+ var init_meta_handlers = __esm({
10928
+ "src/handlers/meta-handlers.js"() {
10929
+ init_tools();
10930
+ init_prompt_handlers();
10931
+ }
10932
+ });
10933
+
10673
10934
  // src/handlers/package-handlers.js
10674
10935
  var package_handlers_exports = {};
10675
10936
  __export(package_handlers_exports, {
@@ -10679,7 +10940,7 @@ __export(package_handlers_exports, {
10679
10940
  });
10680
10941
  async function fetchPackages() {
10681
10942
  const now = Date.now();
10682
- if (cachedPackages && cachedAt2 && now - cachedAt2 < CACHE_TTL2) {
10943
+ if (cachedPackages && cachedAt2 && now - cachedAt2 < CACHE_TTL) {
10683
10944
  return cachedPackages;
10684
10945
  }
10685
10946
  try {
@@ -10756,7 +11017,7 @@ async function installPackage(packageName) {
10756
11017
  }
10757
11018
  return { content: [{ type: "text", text: `\u5305 "${packageName}" \u5B89\u88C5\u547D\u4EE4\u5DF2\u53D1\u9001\uFF01` }] };
10758
11019
  }
10759
- var PACKAGES_LIST_URL, cachedPackages, cachedAt2, CACHE_TTL2;
11020
+ var PACKAGES_LIST_URL, cachedPackages, cachedAt2, CACHE_TTL;
10760
11021
  var init_package_handlers = __esm({
10761
11022
  "src/handlers/package-handlers.js"() {
10762
11023
  init_logger();
@@ -10765,7 +11026,7 @@ var init_package_handlers = __esm({
10765
11026
  PACKAGES_LIST_URL = process.env.PACKAGES_LIST_URL || "http://s3.atlisp.cn/packages-list?fmt=json";
10766
11027
  cachedPackages = null;
10767
11028
  cachedAt2 = null;
10768
- CACHE_TTL2 = 10 * 60 * 1e3;
11029
+ CACHE_TTL = 10 * 60 * 1e3;
10769
11030
  }
10770
11031
  });
10771
11032
 
@@ -12589,6 +12850,192 @@ var init_sheetset_handlers = __esm({
12589
12850
  }
12590
12851
  });
12591
12852
 
12853
+ // src/handlers/skill-handlers.js
12854
+ var skill_handlers_exports = {};
12855
+ __export(skill_handlers_exports, {
12856
+ skill_install: () => skill_install,
12857
+ skill_list_categories: () => skill_list_categories,
12858
+ skill_list_installed: () => skill_list_installed,
12859
+ skill_search: () => skill_search,
12860
+ skill_uninstall: () => skill_uninstall
12861
+ });
12862
+ import fs8 from "fs";
12863
+ import path12 from "path";
12864
+ import os7 from "os";
12865
+ function readCache() {
12866
+ try {
12867
+ if (fs8.existsSync(CACHE_PATH)) return JSON.parse(fs8.readFileSync(CACHE_PATH, "utf-8"));
12868
+ } catch {
12869
+ }
12870
+ return null;
12871
+ }
12872
+ function writeCache(data) {
12873
+ try {
12874
+ fs8.writeFileSync(CACHE_PATH, JSON.stringify(data, null, 2), "utf-8");
12875
+ } catch {
12876
+ }
12877
+ }
12878
+ async function fetchIndex() {
12879
+ let index = readCache();
12880
+ if (index && Date.now() - index.fetchedAt < 36e5) return index;
12881
+ try {
12882
+ const resp = await fetch(REGISTRY_URL, { headers: { "User-Agent": "atlisp-mcp" } });
12883
+ if (resp.ok) {
12884
+ index = await resp.json();
12885
+ index.fetchedAt = Date.now();
12886
+ writeCache(index);
12887
+ }
12888
+ } catch {
12889
+ }
12890
+ return index;
12891
+ }
12892
+ async function skill_search(a) {
12893
+ const index = await fetchIndex();
12894
+ if (!index?.skills) return { content: [{ type: "text", text: "\u65E0\u6CD5\u83B7\u53D6 skill \u6CE8\u518C\u8868\u7D22\u5F15" }], isError: true };
12895
+ const q = (a.query || "").toLowerCase();
12896
+ const results = index.skills.filter(
12897
+ (s) => s.name.toLowerCase().includes(q) || (s.description || "").toLowerCase().includes(q) || (s.tags || []).some((t) => t.toLowerCase().includes(q))
12898
+ );
12899
+ if (!results.length) return { content: [{ type: "text", text: `\u672A\u627E\u5230\u5339\u914D "${a.query}" \u7684 skill` }] };
12900
+ const lines = [`\u627E\u5230 ${results.length} \u4E2A skill:
12901
+ `];
12902
+ for (const s of results) {
12903
+ lines.push(`**${s.name}**`);
12904
+ if (s.description) lines.push(` \u63CF\u8FF0: ${s.description}`);
12905
+ if (s.tags?.length) lines.push(` \u6807\u7B7E: ${s.tags.join(", ")}`);
12906
+ if (s.version) lines.push(` \u7248\u672C: ${s.version}`);
12907
+ lines.push("");
12908
+ }
12909
+ return { content: [{ type: "text", text: lines.join("\n") }] };
12910
+ }
12911
+ async function skill_install(a) {
12912
+ if (!a.name) return { content: [{ type: "text", text: "\u9700\u8981 skill \u540D\u79F0" }], isError: true };
12913
+ const index = await fetchIndex();
12914
+ if (!index?.skills) return { content: [{ type: "text", text: "\u65E0\u6CD5\u83B7\u53D6 skill \u6CE8\u518C\u8868\u7D22\u5F15" }], isError: true };
12915
+ const skill = index.skills.find((s) => s.name === a.name);
12916
+ if (!skill) return { content: [{ type: "text", text: `\u672A\u627E\u5230 skill: ${a.name}` }], isError: true };
12917
+ const downloadUrl = skill.download_url || skill.url;
12918
+ if (!downloadUrl) return { content: [{ type: "text", text: `Skill "${a.name}" \u6CA1\u6709\u4E0B\u8F7D\u5730\u5740` }], isError: true };
12919
+ let content;
12920
+ try {
12921
+ const resp = await fetch(downloadUrl, { headers: { "User-Agent": "atlisp-mcp" } });
12922
+ if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
12923
+ content = await resp.text();
12924
+ } catch (e) {
12925
+ return { content: [{ type: "text", text: `\u4E0B\u8F7D\u5931\u8D25: ${e.message}` }], isError: true };
12926
+ }
12927
+ try {
12928
+ fs8.mkdirSync(SKILLS_DIR, { recursive: true });
12929
+ } catch {
12930
+ }
12931
+ const safeName = a.name.replace(/[^a-zA-Z0-9\u4e00-\u9fff_-]/g, "-").substring(0, 40);
12932
+ const skillFile = path12.join(SKILLS_DIR, `skill-${safeName}.md`);
12933
+ if (fs8.existsSync(skillFile)) {
12934
+ return { content: [{ type: "text", text: `Skill "${a.name}" \u5DF2\u5B89\u88C5 (${skillFile})` }], isError: true };
12935
+ }
12936
+ let finalContent = content;
12937
+ if (!content.match(/^---\n/)) {
12938
+ const tags = (skill.tags || []).join(", ");
12939
+ finalContent = `---
12940
+ name: ${a.name}
12941
+ description: ${skill.description || ""}
12942
+ tags: ${tags}
12943
+ ---
12944
+
12945
+ ${content}`;
12946
+ }
12947
+ fs8.writeFileSync(skillFile, finalContent, "utf-8");
12948
+ return { content: [{ type: "text", text: `${skill.name} \u5B89\u88C5\u6210\u529F
12949
+ \u6587\u4EF6: ${skillFile}${skill.version ? `
12950
+ \u7248\u672C: ${skill.version}` : ""}` }] };
12951
+ }
12952
+ async function skill_list_installed() {
12953
+ if (!fs8.existsSync(SKILLS_DIR)) return { content: [{ type: "text", text: "\u6682\u65E0\u5DF2\u5B89\u88C5\u7684 skill" }] };
12954
+ const results = [];
12955
+ try {
12956
+ const entries = fs8.readdirSync(SKILLS_DIR, { withFileTypes: true });
12957
+ for (const entry of entries) {
12958
+ if (entry.isFile() && entry.name.endsWith(".md")) {
12959
+ const fullPath = path12.join(SKILLS_DIR, entry.name);
12960
+ try {
12961
+ const raw = fs8.readFileSync(fullPath, "utf-8");
12962
+ const match = raw.match(/^---\n([\s\S]*?)\n---/);
12963
+ if (match) {
12964
+ const fm = {};
12965
+ for (const line of match[1].split("\n")) {
12966
+ const sep = line.indexOf(":");
12967
+ if (sep > 0) fm[line.slice(0, sep).trim()] = line.slice(sep + 1).trim();
12968
+ }
12969
+ results.push({ name: fm.name || entry.name.replace(".md", ""), description: fm.description || "", tags: fm.tags || "" });
12970
+ } else {
12971
+ results.push({ name: entry.name.replace(".md", ""), description: "", tags: "" });
12972
+ }
12973
+ } catch {
12974
+ }
12975
+ }
12976
+ }
12977
+ } catch {
12978
+ }
12979
+ if (!results.length) return { content: [{ type: "text", text: "\u6682\u65E0\u5DF2\u5B89\u88C5\u7684 skill" }] };
12980
+ const lines = [`\u5DF2\u5B89\u88C5 ${results.length} \u4E2A skill:
12981
+ `];
12982
+ for (const s of results) {
12983
+ lines.push(`**${s.name}**${s.description ? ` \u2014 ${s.description}` : ""}`);
12984
+ if (s.tags) lines.push(` \u6807\u7B7E: ${s.tags}`);
12985
+ }
12986
+ return { content: [{ type: "text", text: lines.join("\n") }] };
12987
+ }
12988
+ async function skill_uninstall(a) {
12989
+ if (!a.name) return { content: [{ type: "text", text: "\u9700\u8981 skill \u540D\u79F0" }], isError: true };
12990
+ if (!fs8.existsSync(SKILLS_DIR)) return { content: [{ type: "text", text: `\u672A\u5B89\u88C5 skill: ${a.name}` }], isError: true };
12991
+ let found = null;
12992
+ try {
12993
+ const entries = fs8.readdirSync(SKILLS_DIR, { withFileTypes: true });
12994
+ for (const entry of entries) {
12995
+ if (entry.isFile() && entry.name.endsWith(".md")) {
12996
+ const fullPath = path12.join(SKILLS_DIR, entry.name);
12997
+ try {
12998
+ const raw = fs8.readFileSync(fullPath, "utf-8");
12999
+ const match = raw.match(/^---\n([\s\S]*?)\n---/);
13000
+ if (match) {
13001
+ for (const line of match[1].split("\n")) {
13002
+ if (line.startsWith("name:") && line.slice(5).trim() === a.name) {
13003
+ found = fullPath;
13004
+ break;
13005
+ }
13006
+ }
13007
+ } else if (entry.name.replace(".md", "") === a.name) {
13008
+ found = fullPath;
13009
+ }
13010
+ } catch {
13011
+ }
13012
+ if (found) break;
13013
+ }
13014
+ }
13015
+ } catch {
13016
+ }
13017
+ if (!found) return { content: [{ type: "text", text: `\u672A\u5B89\u88C5 skill: ${a.name}` }], isError: true };
13018
+ fs8.unlinkSync(found);
13019
+ return { content: [{ type: "text", text: `${a.name} \u5DF2\u5378\u8F7D` }] };
13020
+ }
13021
+ async function skill_list_categories() {
13022
+ const index = await fetchIndex();
13023
+ if (!index?.skills) return { content: [{ type: "text", text: "\u65E0\u6CD5\u83B7\u53D6 skill \u6CE8\u518C\u8868\u7D22\u5F15" }], isError: true };
13024
+ const cats = /* @__PURE__ */ new Set();
13025
+ for (const s of index.skills) for (const t of s.tags || []) cats.add(t);
13026
+ const sorted = Array.from(cats).sort();
13027
+ return { content: [{ type: "text", text: sorted.length ? `\u6CE8\u518C\u8868\u5206\u7C7B (${sorted.length}):
13028
+ ${sorted.join("\n")}` : "\u6682\u65E0\u5206\u7C7B" }] };
13029
+ }
13030
+ var SKILLS_DIR, REGISTRY_URL, CACHE_PATH;
13031
+ var init_skill_handlers = __esm({
13032
+ "src/handlers/skill-handlers.js"() {
13033
+ SKILLS_DIR = path12.join(os7.homedir(), ".atlisp", "skills");
13034
+ REGISTRY_URL = process.env.SKILL_REGISTRY_URL || "https://raw.githubusercontent.com/atlisp/skill-registry/main/index.json";
13035
+ CACHE_PATH = path12.join(os7.homedir(), ".atlisp", "skill-registry-cache.json");
13036
+ }
13037
+ });
13038
+
12592
13039
  // src/handlers/spatial-handlers.js
12593
13040
  var spatial_handlers_exports = {};
12594
13041
  __export(spatial_handlers_exports, {
@@ -13960,13 +14407,13 @@ function lispPoint(pt) {
13960
14407
  }
13961
14408
  async function attachXref(args) {
13962
14409
  try {
13963
- const path20 = esc3(args.path);
14410
+ const path21 = esc3(args.path);
13964
14411
  const point = lispPoint(args.point);
13965
14412
  const scale = args.scale || 1;
13966
14413
  const rotation = args.rotation || 0;
13967
14414
  const overlay = args.overlay ? "_O" : "_A";
13968
14415
  const result = await cad.sendCommandWithResult(
13969
- `(command "_.XATTACH" "${path20}" "${overlay}" "${point}" "${scale}" "${rotation}") (princ)`
14416
+ `(command "_.XATTACH" "${path21}" "${overlay}" "${point}" "${scale}" "${rotation}") (princ)`
13970
14417
  );
13971
14418
  return mcpSuccess(`\u5916\u90E8\u53C2\u7167\u5DF2\u9644\u7740: ${args.path}`);
13972
14419
  } catch (e) {
@@ -14294,6 +14741,7 @@ var init_ = __esm({
14294
14741
  "./handlers/function-handlers.js": () => Promise.resolve().then(() => (init_function_handlers(), function_handlers_exports)),
14295
14742
  "./handlers/layer-handlers.js": () => Promise.resolve().then(() => (init_layer_handlers(), layer_handlers_exports)),
14296
14743
  "./handlers/lint-handlers.js": () => Promise.resolve().then(() => (init_lint_handlers(), lint_handlers_exports)),
14744
+ "./handlers/meta-handlers.js": () => Promise.resolve().then(() => (init_meta_handlers(), meta_handlers_exports)),
14297
14745
  "./handlers/package-handlers.js": () => Promise.resolve().then(() => (init_package_handlers(), package_handlers_exports)),
14298
14746
  "./handlers/parametric-handlers.js": () => Promise.resolve().then(() => (init_parametric_handlers(), parametric_handlers_exports)),
14299
14747
  "./handlers/pdf-annotation-handlers.js": () => Promise.resolve().then(() => (init_pdf_annotation_handlers(), pdf_annotation_handlers_exports)),
@@ -14302,6 +14750,7 @@ var init_ = __esm({
14302
14750
  "./handlers/query-print-handlers.js": () => Promise.resolve().then(() => (init_query_print_handlers(), query_print_handlers_exports)),
14303
14751
  "./handlers/resource-handlers.js": () => Promise.resolve().then(() => (init_resource_handlers(), resource_handlers_exports)),
14304
14752
  "./handlers/sheetset-handlers.js": () => Promise.resolve().then(() => (init_sheetset_handlers(), sheetset_handlers_exports)),
14753
+ "./handlers/skill-handlers.js": () => Promise.resolve().then(() => (init_skill_handlers(), skill_handlers_exports)),
14305
14754
  "./handlers/spatial-handlers.js": () => Promise.resolve().then(() => (init_spatial_handlers(), spatial_handlers_exports)),
14306
14755
  "./handlers/style-handlers.js": () => Promise.resolve().then(() => (init_style_handlers(), style_handlers_exports)),
14307
14756
  "./handlers/text-handlers.js": () => Promise.resolve().then(() => (init_text_handlers(), text_handlers_exports)),
@@ -14314,8 +14763,8 @@ var init_ = __esm({
14314
14763
  });
14315
14764
 
14316
14765
  // src/handler-loader.js
14317
- import fs8 from "fs";
14318
- import path12 from "path";
14766
+ import fs9 from "fs";
14767
+ import path13 from "path";
14319
14768
  import { fileURLToPath as fileURLToPath5 } from "url";
14320
14769
  function toolNameToFuncName(toolName) {
14321
14770
  if (FUNCTION_OVERRIDES[toolName]) return FUNCTION_OVERRIDES[toolName];
@@ -14365,10 +14814,10 @@ async function getHandlerModule(name) {
14365
14814
  async function initHandlerModules() {
14366
14815
  if (_handlerModulesInit) return;
14367
14816
  _handlerModulesInit = true;
14368
- const handlerDir = path12.join(__dirname5, "handlers");
14817
+ const handlerDir = path13.join(__dirname5, "handlers");
14369
14818
  let names;
14370
14819
  try {
14371
- names = await fs8.promises.readdir(handlerDir);
14820
+ names = await fs9.promises.readdir(handlerDir);
14372
14821
  } catch {
14373
14822
  log("Handler directory not found, using static manifest");
14374
14823
  return;
@@ -14379,6 +14828,7 @@ async function initHandlerModules() {
14379
14828
  handlerModules[name] = () => globImport_handlers_handlers_js(`./handlers/${name}-handlers.js`);
14380
14829
  }
14381
14830
  }
14831
+ watchHandlers();
14382
14832
  }
14383
14833
  async function getHandler(toolName) {
14384
14834
  const resolved = await resolveHandler(toolName);
@@ -14387,12 +14837,40 @@ async function getHandler(toolName) {
14387
14837
  if (!mod || typeof mod[resolved.funcName] !== "function") return null;
14388
14838
  return mod[resolved.funcName];
14389
14839
  }
14390
- var __dirname5, _handlerModulesInit, _handlerNameMapInit, handlerModules, moduleCache, resolvedCache, FUNCTION_OVERRIDES;
14840
+ function watchHandlers() {
14841
+ if (_watcherInitialized) return;
14842
+ _watcherInitialized = true;
14843
+ const handlerDir = path13.join(__dirname5, "handlers");
14844
+ let debounceTimer = null;
14845
+ try {
14846
+ if (!fs9.existsSync(handlerDir)) return;
14847
+ fs9.watch(handlerDir, (eventType, filename) => {
14848
+ if (!filename || !filename.endsWith("-handlers.js")) return;
14849
+ if (debounceTimer) clearTimeout(debounceTimer);
14850
+ debounceTimer = setTimeout(async () => {
14851
+ const name = filename.replace("-handlers.js", "");
14852
+ moduleCache.delete(name);
14853
+ resolvedCache.clear();
14854
+ _handlerNameMapInit = false;
14855
+ _handlerModulesInit = false;
14856
+ try {
14857
+ await initHandlerModules();
14858
+ log(`Handler hot-reloaded: ${filename}`);
14859
+ } catch (e) {
14860
+ log(`Handler reload failed for ${filename}: ${e.message}`);
14861
+ }
14862
+ }, 300);
14863
+ });
14864
+ } catch (e) {
14865
+ log(`Handler watch error: ${e.message}`);
14866
+ }
14867
+ }
14868
+ var __dirname5, _handlerModulesInit, _handlerNameMapInit, handlerModules, moduleCache, resolvedCache, FUNCTION_OVERRIDES, _watcherInitialized;
14391
14869
  var init_handler_loader = __esm({
14392
14870
  "src/handler-loader.js"() {
14393
14871
  init_logger();
14394
14872
  init_();
14395
- __dirname5 = path12.dirname(fileURLToPath5(import.meta.url));
14873
+ __dirname5 = path13.dirname(fileURLToPath5(import.meta.url));
14396
14874
  _handlerModulesInit = false;
14397
14875
  _handlerNameMapInit = false;
14398
14876
  handlerModules = {};
@@ -14404,16 +14882,18 @@ var init_handler_loader = __esm({
14404
14882
  get_dimension: "getDimensionValue",
14405
14883
  eval_lisp_with_result: "evalLisp"
14406
14884
  };
14885
+ _watcherInitialized = false;
14407
14886
  }
14408
14887
  });
14409
14888
 
14410
14889
  // src/streaming.js
14411
- async function streamEntitiesByType({ type = null, layer = null, chunk = STREAM_CHUNK_SIZE, offset = 0 }) {
14412
- const hasFilter = type || layer;
14413
- const filters = [];
14414
- if (type) filters.push(`(cons 0 "${type}")`);
14415
- if (layer) filters.push(`(cons 8 "${layer}")`);
14416
- const ssgetExpr = filters.length > 0 ? `(ssget "_X" (list ${filters.join(" ")}))` : `(ssget "_X")`;
14890
+ function escapeLispStr(str) {
14891
+ if (typeof str !== "string") return "";
14892
+ return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
14893
+ }
14894
+ async function streamEntities({ filterExpr, extractFields, itemMapper, offset, limit, hasFilter, warnThreshold, overLimitMsg, resultProp }) {
14895
+ const ssgetExpr = filterExpr ? `(ssget "_X" ${filterExpr})` : `(ssget "_X")`;
14896
+ const extractCode = extractFields.length === 1 ? `(list ${extractFields.join(" ")})` : `(list ${extractFields.join(" ")})`;
14417
14897
  const lisp = `(progn
14418
14898
  (setq ss ${ssgetExpr})
14419
14899
  (setq total (if ss (sslength ss) 0))
@@ -14425,10 +14905,10 @@ async function streamEntitiesByType({ type = null, layer = null, chunk = STREAM_
14425
14905
  (setq result (list total))
14426
14906
  (setq i ${offset})
14427
14907
  (setq count 0)
14428
- (while (and (< i total) (< count ${chunk}))
14908
+ (while (and (< i total) (< count ${limit}))
14429
14909
  (setq e (ssname ss i))
14430
14910
  (setq elist (entget e))
14431
- (setq result (cons (list (cdr (assoc 0 elist)) (cdr (assoc 5 elist)) (cdr (assoc 8 elist))) result))
14911
+ (setq result (cons ${extractCode} result))
14432
14912
  (setq i (1+ i))
14433
14913
  (setq count (1+ count)))
14434
14914
  (reverse result)))))`;
@@ -14442,108 +14922,68 @@ async function streamEntitiesByType({ type = null, layer = null, chunk = STREAM_
14442
14922
  offset: 0,
14443
14923
  count: 0,
14444
14924
  hasMore: false,
14445
- entities: [],
14446
- error: `\u5B9E\u4F53\u6570\u91CF (${parsed[1]}) \u8D85\u8FC7\u6700\u5927\u9650\u5236 (${MAX_ENTITY_LIMIT})\uFF0C\u8BF7\u4F7F\u7528 type/layer \u8FC7\u6EE4`
14925
+ [resultProp]: [],
14926
+ error: `\u5B9E\u4F53\u6570\u91CF (${parsed[1]}) \u8D85\u8FC7\u6700\u5927\u9650\u5236 (${MAX_ENTITY_LIMIT})\uFF0C${overLimitMsg}`
14447
14927
  };
14448
14928
  }
14449
- const entities = parsed.slice(1);
14450
- const result = {
14451
- total,
14452
- offset,
14453
- count: entities.length,
14454
- hasMore: offset + entities.length < total,
14455
- entities
14456
- };
14457
- if (!hasFilter && total > UNFILTERED_WARN_THRESHOLD) {
14458
- 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`;
14929
+ const items = parsed.slice(1).map(itemMapper);
14930
+ const result = { total, offset, count: items.length, hasMore: offset + items.length < total, [resultProp]: items };
14931
+ if (!hasFilter && total > warnThreshold) {
14932
+ 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`;
14459
14933
  }
14460
14934
  return result;
14461
14935
  } catch {
14462
- return { total: 0, offset: 0, count: 0, hasMore: false, entities: [] };
14936
+ return { total: 0, offset: 0, count: 0, hasMore: false, [resultProp]: [] };
14463
14937
  }
14464
14938
  }
14939
+ async function streamEntitiesByType({ type = null, layer = null, chunk = STREAM_CHUNK_SIZE, offset = 0 }) {
14940
+ const filters = [];
14941
+ if (type) filters.push(`(cons 0 "${escapeLispStr(type)}")`);
14942
+ if (layer) filters.push(`(cons 8 "${escapeLispStr(layer)}")`);
14943
+ const filterExpr = filters.length > 0 ? `(list ${filters.join(" ")})` : null;
14944
+ return streamEntities({
14945
+ filterExpr,
14946
+ extractFields: ["(list (cdr (assoc 0 elist)) (cdr (assoc 5 elist)) (cdr (assoc 8 elist)))"],
14947
+ itemMapper: (item) => ({ type: item[0], handle: item[1], layer: item[2] }),
14948
+ offset,
14949
+ limit: chunk,
14950
+ hasFilter: !!(type || layer),
14951
+ warnThreshold: UNFILTERED_WARN_THRESHOLD,
14952
+ overLimitMsg: "\u8BF7\u4F7F\u7528 type/layer \u8FC7\u6EE4",
14953
+ resultProp: "entities"
14954
+ });
14955
+ }
14465
14956
  async function streamTextContent({ layer = null, bbox = null, offset = 0, limit = 100 }) {
14466
14957
  const textFilters = [`(cons 0 "TEXT")`, `(cons 0 "MTEXT")`];
14467
- if (layer) textFilters.push(`(cons 8 "${layer}")`);
14468
- const lisp = `(progn
14469
- (setq ss (ssget "_X" (list '(-4 . "<OR") ${textFilters.join(" ")} '(-4 . "OR>"))))
14470
- (setq total (if ss (sslength ss) 0))
14471
- (if (> total ${MAX_ENTITY_LIMIT})
14472
- (list -1 total)
14473
- (if (>= ${offset} total)
14474
- (list total)
14475
- (progn
14476
- (setq result (list total))
14477
- (setq i ${offset})
14478
- (setq count 0)
14479
- (while (and (< i total) (< count ${limit}))
14480
- (setq e (ssname ss i))
14481
- (setq elist (entget e))
14482
- (setq txt (cdr (assoc 1 elist)))
14483
- (setq result (cons (list (cdr (assoc 5 elist)) txt (cdr (assoc 10 elist))) result))
14484
- (setq i (1+ i))
14485
- (setq count (1+ count)))
14486
- (reverse result)))))`;
14487
- const raw = await cad.sendCommandWithResult(lisp);
14488
- try {
14489
- const parsed = JSON.parse(raw);
14490
- const total = parsed[0] || 0;
14491
- if (total === -1) {
14492
- return {
14493
- total: parsed[1] || 0,
14494
- offset: 0,
14495
- count: 0,
14496
- hasMore: false,
14497
- texts: [],
14498
- 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`
14499
- };
14500
- }
14501
- const texts = parsed.slice(1).map((t) => ({ handle: t[0], text: t[1], point: t[2] }));
14502
- return { total, offset, count: texts.length, hasMore: offset + texts.length < total, texts };
14503
- } catch {
14504
- return { total: 0, offset: 0, count: 0, hasMore: false, texts: [] };
14505
- }
14958
+ if (layer) textFilters.push(`(cons 8 "${escapeLispStr(layer)}")`);
14959
+ const filterExpr = `(list '(-4 . "<OR") ${textFilters.join(" ")} '(-4 . "OR>"))`;
14960
+ return streamEntities({
14961
+ filterExpr,
14962
+ extractFields: ["(list (cdr (assoc 5 elist)) (cdr (assoc 1 elist)) (cdr (assoc 10 elist)))"],
14963
+ itemMapper: (item) => ({ handle: item[0], text: item[1], point: item[2] }),
14964
+ offset,
14965
+ limit,
14966
+ hasFilter: !!layer,
14967
+ warnThreshold: UNFILTERED_WARN_THRESHOLD,
14968
+ overLimitMsg: "\u8BF7\u4F7F\u7528 layer \u8FC7\u6EE4",
14969
+ resultProp: "texts"
14970
+ });
14506
14971
  }
14507
14972
  async function streamBlockRefs({ blockName = null, offset = 0, limit = 100 }) {
14508
14973
  const filters = [`(cons 0 "INSERT")`];
14509
- if (blockName) filters.push(`(cons 2 "${blockName}")`);
14510
- const lisp = `(progn
14511
- (setq ss (ssget "_X" (list ${filters.join(" ")})))
14512
- (setq total (if ss (sslength ss) 0))
14513
- (if (> total ${MAX_ENTITY_LIMIT})
14514
- (list -1 total)
14515
- (if (>= ${offset} total)
14516
- (list total)
14517
- (progn
14518
- (setq result (list total))
14519
- (setq i ${offset})
14520
- (setq count 0)
14521
- (while (and (< i total) (< count ${limit}))
14522
- (setq e (ssname ss i))
14523
- (setq elist (entget e))
14524
- (setq result (cons (list (cdr (assoc 2 elist)) (cdr (assoc 5 elist)) (cdr (assoc 10 elist))) result))
14525
- (setq i (1+ i))
14526
- (setq count (1+ count)))
14527
- (reverse result)))))`;
14528
- const raw = await cad.sendCommandWithResult(lisp);
14529
- try {
14530
- const parsed = JSON.parse(raw);
14531
- const total = parsed[0] || 0;
14532
- if (total === -1) {
14533
- return {
14534
- total: parsed[1] || 0,
14535
- offset: 0,
14536
- count: 0,
14537
- hasMore: false,
14538
- refs: [],
14539
- error: `\u5757\u53C2\u7167\u6570\u91CF (${parsed[1]}) \u8D85\u8FC7\u6700\u5927\u9650\u5236 (${MAX_ENTITY_LIMIT})\uFF0C\u8BF7\u4F7F\u7528 blockName \u8FC7\u6EE4`
14540
- };
14541
- }
14542
- const refs = parsed.slice(1).map((r) => ({ blockName: r[0], handle: r[1], insertionPoint: r[2] }));
14543
- return { total, offset, count: refs.length, hasMore: offset + refs.length < total, refs };
14544
- } catch {
14545
- return { total: 0, offset: 0, count: 0, hasMore: false, refs: [] };
14546
- }
14974
+ if (blockName) filters.push(`(cons 2 "${escapeLispStr(blockName)}")`);
14975
+ const filterExpr = filters.length > 0 ? `(list ${filters.join(" ")})` : null;
14976
+ return streamEntities({
14977
+ filterExpr,
14978
+ extractFields: ["(list (cdr (assoc 2 elist)) (cdr (assoc 5 elist)) (cdr (assoc 10 elist)))"],
14979
+ itemMapper: (item) => ({ blockName: item[0], handle: item[1], insertionPoint: item[2] }),
14980
+ offset,
14981
+ limit,
14982
+ hasFilter: !!blockName,
14983
+ warnThreshold: UNFILTERED_WARN_THRESHOLD,
14984
+ overLimitMsg: "\u8BF7\u4F7F\u7528 blockName \u8FC7\u6EE4",
14985
+ resultProp: "refs"
14986
+ });
14547
14987
  }
14548
14988
  var STREAM_CHUNK_SIZE, MAX_ENTITY_LIMIT, UNFILTERED_WARN_THRESHOLD;
14549
14989
  var init_streaming = __esm({
@@ -14555,6 +14995,74 @@ var init_streaming = __esm({
14555
14995
  }
14556
14996
  });
14557
14997
 
14998
+ // src/cad-rot-helper.js
14999
+ import edge from "edge-js";
15000
+ import path14 from "path";
15001
+ import { fileURLToPath as fileURLToPath6 } from "url";
15002
+ function getRotHelper() {
15003
+ if (_rotHelper) return _rotHelper;
15004
+ const dllPath = path14.join(projectRoot, "dist", "cad-rothelper.dll");
15005
+ try {
15006
+ _rotHelper = edge.func({
15007
+ assemblyFile: dllPath,
15008
+ typeName: "CadRotHelper.RotEnumerator",
15009
+ methodName: "EnumerateAll"
15010
+ });
15011
+ return _rotHelper;
15012
+ } catch (e) {
15013
+ error(`ROT \u5E2E\u52A9\u5668\u52A0\u8F7D\u5931\u8D25: ${e.message}`);
15014
+ return null;
15015
+ }
15016
+ }
15017
+ async function discoverCadInstances() {
15018
+ const helper = getRotHelper();
15019
+ if (!helper) {
15020
+ log("ROT \u5E2E\u52A9\u5668\u4E0D\u53EF\u7528\uFF0C\u8DF3\u8FC7 CAD \u5B9E\u4F8B\u53D1\u73B0");
15021
+ return [];
15022
+ }
15023
+ return new Promise((resolve) => {
15024
+ helper(null, (err, result) => {
15025
+ if (err) {
15026
+ error(`ROT \u679A\u4E3E\u5931\u8D25: ${err.message}`);
15027
+ resolve([]);
15028
+ return;
15029
+ }
15030
+ if (!Array.isArray(result)) {
15031
+ resolve([]);
15032
+ return;
15033
+ }
15034
+ const seen = /* @__PURE__ */ new Set();
15035
+ const instances = [];
15036
+ for (const entry of result) {
15037
+ if (!entry.appName) continue;
15038
+ const pidKey = `${entry.appName}-${entry.pid}`;
15039
+ if (seen.has(pidKey)) continue;
15040
+ seen.add(pidKey);
15041
+ const key = `${entry.appName.toLowerCase()}-${entry.pid}`;
15042
+ instances.push({
15043
+ key,
15044
+ platform: entry.appName,
15045
+ pid: entry.pid,
15046
+ hwnd: entry.hwnd,
15047
+ version: entry.version,
15048
+ moniker: entry.name
15049
+ });
15050
+ }
15051
+ log(`ROT \u53D1\u73B0: ${instances.length} \u4E2A CAD \u5B9E\u4F8B (${instances.map((i) => `${i.platform}#${i.pid}`).join(", ")})`);
15052
+ resolve(instances);
15053
+ });
15054
+ });
15055
+ }
15056
+ var __filename, projectRoot, _rotHelper;
15057
+ var init_cad_rot_helper = __esm({
15058
+ "src/cad-rot-helper.js"() {
15059
+ init_logger();
15060
+ __filename = fileURLToPath6(import.meta.url);
15061
+ projectRoot = path14.dirname(path14.dirname(__filename));
15062
+ _rotHelper = null;
15063
+ }
15064
+ });
15065
+
14558
15066
  // src/cad-pool.js
14559
15067
  var POOL_STRATEGIES, CadConnectionPool, pool;
14560
15068
  var init_cad_pool = __esm({
@@ -14562,6 +15070,8 @@ var init_cad_pool = __esm({
14562
15070
  init_cad();
14563
15071
  init_logger();
14564
15072
  init_config();
15073
+ init_cad_rot_helper();
15074
+ init_resource_cache();
14565
15075
  POOL_STRATEGIES = {
14566
15076
  ROUND_ROBIN: "round-robin",
14567
15077
  LEAST_BUSY: "least-busy",
@@ -14570,9 +15080,8 @@ var init_cad_pool = __esm({
14570
15080
  CadConnectionPool = class {
14571
15081
  _instances = /* @__PURE__ */ new Map();
14572
15082
  _maxPoolSize = 3;
14573
- _activeKey = "default";
15083
+ _activeKey = null;
14574
15084
  _strategy = POOL_STRATEGIES.MANUAL;
14575
- _rrIndex = 0;
14576
15085
  _reconnectTimers = /* @__PURE__ */ new Map();
14577
15086
  _reconnectCounts = /* @__PURE__ */ new Map();
14578
15087
  _maxReconnectAttempts = 10;
@@ -14582,30 +15091,8 @@ var init_cad_pool = __esm({
14582
15091
  this._maxPoolSize = config_default.maxPoolSize || 3;
14583
15092
  }
14584
15093
  getConnection(key) {
14585
- this._ensureDefault();
14586
15094
  return this._instances.get(key)?.connection || null;
14587
15095
  }
14588
- /**
14589
- * Ensure the default entry exists. Created lazily to avoid
14590
- * calling getActiveConnection() at module import time (which
14591
- * breaks tests that mock cad.js).
14592
- */
14593
- _ensureDefault() {
14594
- if (!this._instances.has("default")) {
14595
- const defaultConn = getActiveConnection();
14596
- this._instances.set("default", {
14597
- key: "default",
14598
- connection: defaultConn,
14599
- platform: null,
14600
- version: null,
14601
- connected: false,
14602
- lastPing: Date.now(),
14603
- createdAt: Date.now(),
14604
- label: "\u9ED8\u8BA4 CAD",
14605
- autoReconnect: true
14606
- });
14607
- }
14608
- }
14609
15096
  /**
14610
15097
  * Connect to a CAD instance and add it to the pool.
14611
15098
  * @param {string} key - Instance identifier
@@ -14613,14 +15100,30 @@ var init_cad_pool = __esm({
14613
15100
  * @param {object} [options] - Additional options
14614
15101
  * @param {string} [options.label] - Human-readable label
14615
15102
  * @param {boolean} [options.autoReconnect=true] - Auto-reconnect on failure
15103
+ * @param {number} [options.pid] - Target process PID (find via ROT)
14616
15104
  * @param {boolean} [options.makeActive=true] - Set as active after connecting
14617
15105
  */
14618
15106
  async connect(key = "default", platform = null, options = {}) {
14619
- this._ensureDefault();
14620
- const { label, autoReconnect = true, makeActive = true } = options || {};
15107
+ const { label, autoReconnect = true, makeActive = true, pid } = options || {};
14621
15108
  if (this._instances.size >= this._maxPoolSize && !this._instances.has(key)) {
14622
15109
  throw new Error(`\u8FDE\u63A5\u6C60\u5DF2\u6EE1 (${this._maxPoolSize})\uFF0C\u65E0\u6CD5\u6DFB\u52A0\u65B0\u5B9E\u4F8B "${key}"`);
14623
15110
  }
15111
+ if (pid != null) {
15112
+ const instances = await discoverCadInstances();
15113
+ const match = instances.find((i) => i.pid === pid);
15114
+ if (!match) {
15115
+ error(`\u8FDE\u63A5\u6C60: \u672A\u627E\u5230 PID ${pid} \u7684 CAD \u5B9E\u4F8B`);
15116
+ return false;
15117
+ }
15118
+ platform = platform || match.platform;
15119
+ if (key === "default" || key === pid.toString()) {
15120
+ key = match.key;
15121
+ }
15122
+ if (match.moniker) {
15123
+ if (!options) options = {};
15124
+ options._moniker = match.moniker;
15125
+ }
15126
+ }
14624
15127
  let entry = this._instances.get(key);
14625
15128
  if (entry && entry.connected) {
14626
15129
  return true;
@@ -14642,7 +15145,9 @@ var init_cad_pool = __esm({
14642
15145
  }
14643
15146
  const conn = entry.connection;
14644
15147
  try {
14645
- const ok = await conn.connect(platform);
15148
+ const moniker = options._moniker || entry.moniker;
15149
+ const connectOpts = moniker ? { moniker, key } : {};
15150
+ const ok = await conn.connect(platform, connectOpts);
14646
15151
  if (ok) {
14647
15152
  entry.connected = true;
14648
15153
  entry.platform = conn.getPlatform();
@@ -14666,17 +15171,6 @@ var init_cad_pool = __esm({
14666
15171
  * Disconnect and remove an instance from the pool.
14667
15172
  */
14668
15173
  async disconnect(key) {
14669
- this._ensureDefault();
14670
- if (key === "default") {
14671
- const entry2 = this._instances.get("default");
14672
- if (entry2) {
14673
- await entry2.connection.disconnect();
14674
- entry2.connected = false;
14675
- entry2.platform = null;
14676
- entry2.version = null;
14677
- }
14678
- return true;
14679
- }
14680
15174
  const entry = this._instances.get(key);
14681
15175
  if (!entry) return false;
14682
15176
  this._clearReconnectTimer(key);
@@ -14687,7 +15181,12 @@ var init_cad_pool = __esm({
14687
15181
  }
14688
15182
  this._instances.delete(key);
14689
15183
  if (this._activeKey === key) {
14690
- this.setActive("default");
15184
+ const remaining = Array.from(this._instances.keys());
15185
+ if (remaining.length > 0) {
15186
+ this.setActive(remaining[0]);
15187
+ } else {
15188
+ this._activeKey = null;
15189
+ }
14691
15190
  }
14692
15191
  log(`\u8FDE\u63A5\u6C60: \u5B9E\u4F8B "${key}" \u5DF2\u65AD\u5F00\u5E76\u79FB\u9664`);
14693
15192
  return true;
@@ -14696,7 +15195,6 @@ var init_cad_pool = __esm({
14696
15195
  * Set the active instance. All cad.* calls will be routed here.
14697
15196
  */
14698
15197
  setActive(key) {
14699
- this._ensureDefault();
14700
15198
  const entry = this._instances.get(key);
14701
15199
  if (!entry) {
14702
15200
  error(`\u8FDE\u63A5\u6C60: \u5B9E\u4F8B "${key}" \u4E0D\u5B58\u5728`);
@@ -14704,47 +15202,88 @@ var init_cad_pool = __esm({
14704
15202
  }
14705
15203
  this._activeKey = key;
14706
15204
  setActiveConnection(entry.connection);
15205
+ setInstanceKey(key);
14707
15206
  cad.connected = entry.connected;
14708
15207
  log(`\u8FDE\u63A5\u6C60: \u5DF2\u5207\u6362\u5230\u5B9E\u4F8B "${key}"`);
15208
+ if (this._onInstanceSwitch) {
15209
+ try {
15210
+ this._onInstanceSwitch(key, entry);
15211
+ } catch (e) {
15212
+ error(`\u5207\u6362\u56DE\u8C03\u7528\u9519\u8BEF: ${e.message}`);
15213
+ }
15214
+ }
14709
15215
  return true;
14710
15216
  }
15217
+ /**
15218
+ * Register a callback invoked after every instance switch.
15219
+ * @param {function} fn - (key, entry) => void
15220
+ */
15221
+ onInstanceSwitch(fn) {
15222
+ this._onInstanceSwitch = fn;
15223
+ }
14711
15224
  /**
14712
15225
  * Discover and add all running CAD instances.
14713
- * Tries each platform to find running CAD processes.
15226
+ * Uses ROT enumeration to find all instances (including multiple of the same platform).
15227
+ * Falls back to GetActiveObject per platform when ROT is unavailable.
14714
15228
  */
14715
15229
  async discover() {
14716
- this._ensureDefault();
14717
- const platforms = ["AutoCAD", "ZWCAD", "GStarCAD", "BricsCAD"];
14718
- let foundCount = 0;
14719
- for (const platform of platforms) {
14720
- const key = platform.toLowerCase();
15230
+ const resultEntries = [];
15231
+ const rotInstances = await discoverCadInstances();
15232
+ const rotHadResults = rotInstances.length > 0;
15233
+ for (const inst of rotInstances) {
15234
+ const key = inst.key;
14721
15235
  if (this._instances.has(key) && this._instances.get(key).connected) {
14722
- foundCount++;
15236
+ resultEntries.push(this._instances.get(key));
14723
15237
  continue;
14724
15238
  }
14725
- try {
14726
- const tempConn = createCadConnection();
14727
- const ok = await tempConn.connect(platform);
14728
- if (ok) {
14729
- const label = `${platform} ${tempConn.getVersion() || ""}`.trim();
14730
- this._instances.set(key, {
14731
- key,
14732
- connection: tempConn,
14733
- platform: tempConn.getPlatform(),
14734
- version: tempConn.getVersion(),
14735
- connected: true,
14736
- lastPing: Date.now(),
14737
- createdAt: Date.now(),
14738
- label,
14739
- autoReconnect: true
14740
- });
14741
- foundCount++;
14742
- log(`\u8FDE\u63A5\u6C60: \u53D1\u73B0 ${label}`);
15239
+ this._instances.set(key, {
15240
+ key,
15241
+ connection: null,
15242
+ platform: inst.platform,
15243
+ pid: inst.pid,
15244
+ version: inst.version,
15245
+ moniker: inst.moniker,
15246
+ connected: false,
15247
+ lastPing: Date.now(),
15248
+ createdAt: Date.now(),
15249
+ label: `${inst.platform} #${inst.pid} ${inst.version || ""}`.trim(),
15250
+ autoReconnect: false
15251
+ });
15252
+ log(`\u8FDE\u63A5\u6C60: \u53D1\u73B0 ${inst.platform} \u5B9E\u4F8B PID=${inst.pid}`);
15253
+ resultEntries.push(this._instances.get(key));
15254
+ }
15255
+ if (!rotHadResults) {
15256
+ const connectWithTimeout = (conn, platform, ms = 4e3) => Promise.race([
15257
+ conn.connect(platform),
15258
+ new Promise((_, reject) => setTimeout(() => reject(new Error("timeout")), ms))
15259
+ ]);
15260
+ for (const platform of ["AutoCAD", "ZWCAD", "GStarCAD", "BricsCAD"]) {
15261
+ const key = platform.toLowerCase();
15262
+ if (this._instances.has(key) && this._instances.get(key).connected) continue;
15263
+ try {
15264
+ const tempConn = createCadConnection();
15265
+ const ok = await connectWithTimeout(tempConn, platform);
15266
+ if (ok) {
15267
+ const label = `${platform} ${tempConn.getVersion() || ""}`.trim();
15268
+ this._instances.set(key, {
15269
+ key,
15270
+ connection: tempConn,
15271
+ platform: tempConn.getPlatform(),
15272
+ version: tempConn.getVersion(),
15273
+ connected: true,
15274
+ lastPing: Date.now(),
15275
+ createdAt: Date.now(),
15276
+ label,
15277
+ autoReconnect: true
15278
+ });
15279
+ log(`\u8FDE\u63A5\u6C60: \u53D1\u73B0 ${label}`);
15280
+ resultEntries.push(this._instances.get(key));
15281
+ }
15282
+ } catch (e) {
14743
15283
  }
14744
- } catch (e) {
14745
15284
  }
14746
15285
  }
14747
- return Array.from(this._instances.values()).map((e) => ({
15286
+ return resultEntries.map((e) => ({
14748
15287
  key: e.key,
14749
15288
  connected: e.connected,
14750
15289
  platform: e.platform,
@@ -14757,7 +15296,6 @@ var init_cad_pool = __esm({
14757
15296
  * Health check for all instances.
14758
15297
  */
14759
15298
  async healthCheck() {
14760
- this._ensureDefault();
14761
15299
  const results = [];
14762
15300
  for (const [key, entry] of this._instances) {
14763
15301
  try {
@@ -14803,14 +15341,21 @@ var init_cad_pool = __esm({
14803
15341
  * Get the active instance.
14804
15342
  */
14805
15343
  getActive() {
14806
- this._ensureDefault();
14807
15344
  return this._instances.get(this._activeKey) || null;
14808
15345
  }
14809
15346
  /**
14810
15347
  * Get stats for all instances.
14811
15348
  */
14812
15349
  getStats() {
14813
- this._ensureDefault();
15350
+ if (this._instances.size === 0) {
15351
+ return {
15352
+ total: 0,
15353
+ maxPoolSize: this._maxPoolSize,
15354
+ active: null,
15355
+ strategy: this._strategy,
15356
+ instances: []
15357
+ };
15358
+ }
14814
15359
  return {
14815
15360
  total: this._instances.size,
14816
15361
  maxPoolSize: this._maxPoolSize,
@@ -14822,7 +15367,7 @@ var init_cad_pool = __esm({
14822
15367
  platform: i.platform,
14823
15368
  version: i.version,
14824
15369
  label: i.label,
14825
- queueSize: i.connection._getQueueSize ? i.connection._getQueueSize() : 0,
15370
+ queueSize: i.connection?._getQueueSize ? i.connection._getQueueSize() : 0,
14826
15371
  lastPingAgo: Date.now() - (i.lastPing || 0),
14827
15372
  uptime: i.connected ? Math.round((Date.now() - i.createdAt) / 1e3) : 0
14828
15373
  }))
@@ -14832,7 +15377,7 @@ var init_cad_pool = __esm({
14832
15377
  * List all instances.
14833
15378
  */
14834
15379
  listInstances() {
14835
- this._ensureDefault();
15380
+ if (this._instances.size === 0) return [];
14836
15381
  return Array.from(this._instances.values()).map((i) => ({
14837
15382
  key: i.key,
14838
15383
  connected: i.connected,
@@ -14844,7 +15389,7 @@ var init_cad_pool = __esm({
14844
15389
  }));
14845
15390
  }
14846
15391
  /**
14847
- * Disconnect all instances.
15392
+ * Disconnect all instances in parallel.
14848
15393
  */
14849
15394
  async disconnectAll() {
14850
15395
  this.stopHealthCheck();
@@ -14853,27 +15398,16 @@ var init_cad_pool = __esm({
14853
15398
  }
14854
15399
  this._reconnectTimers.clear();
14855
15400
  this._reconnectCounts.clear();
14856
- for (const [key, entry] of this._instances) {
15401
+ const entries = Array.from(this._instances.entries());
15402
+ this._instances.clear();
15403
+ this._activeKey = null;
15404
+ await Promise.allSettled(entries.map(async ([key, entry]) => {
14857
15405
  try {
14858
15406
  await entry.connection.disconnect();
14859
15407
  } catch (e) {
14860
15408
  error(`\u8FDE\u63A5\u6C60: \u65AD\u5F00 "${key}" \u51FA\u9519: ${e.message}`);
14861
15409
  }
14862
- }
14863
- this._instances.clear();
14864
- this._activeKey = "default";
14865
- const defaultConn = getActiveConnection();
14866
- this._instances.set("default", {
14867
- key: "default",
14868
- connection: defaultConn,
14869
- platform: null,
14870
- version: null,
14871
- connected: false,
14872
- lastPing: Date.now(),
14873
- createdAt: Date.now(),
14874
- label: "\u9ED8\u8BA4 CAD",
14875
- autoReconnect: true
14876
- });
15410
+ }));
14877
15411
  log("\u8FDE\u63A5\u6C60: \u6240\u6709\u5B9E\u4F8B\u5DF2\u65AD\u5F00");
14878
15412
  }
14879
15413
  // --- Internal ---
@@ -14933,7 +15467,7 @@ var init_cad_pool = __esm({
14933
15467
  });
14934
15468
 
14935
15469
  // src/trigger-engine.js
14936
- function escapeLispStr(v) {
15470
+ function escapeLispStr2(v) {
14937
15471
  if (typeof v !== "string") return v;
14938
15472
  return v.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
14939
15473
  }
@@ -14941,10 +15475,10 @@ function buildFilterExpr(filter) {
14941
15475
  if (!filter || typeof filter !== "object") return "nil";
14942
15476
  const parts = [];
14943
15477
  if (filter.type) {
14944
- parts.push(`(cons 0 "${escapeLispStr(filter.type)}")`);
15478
+ parts.push(`(cons 0 "${escapeLispStr2(filter.type)}")`);
14945
15479
  }
14946
15480
  if (filter.layer) {
14947
- parts.push(`(cons 8 "${escapeLispStr(filter.layer)}")`);
15481
+ parts.push(`(cons 8 "${escapeLispStr2(filter.layer)}")`);
14948
15482
  }
14949
15483
  if (parts.length === 0) return "nil";
14950
15484
  return `(list ${parts.join(" ")})`;
@@ -14993,7 +15527,7 @@ async function evaluateWatch(watch) {
14993
15527
  return { matched, data: { count } };
14994
15528
  }
14995
15529
  case "layer_exists": {
14996
- const lName = escapeLispStr(watch.layerName || "");
15530
+ const lName = escapeLispStr2(watch.layerName || "");
14997
15531
  result = await cad.sendCommandWithResult(
14998
15532
  `(progn (if (tblsearch "LAYER" "${lName}") (list 1) (list 0)))`
14999
15533
  );
@@ -15001,7 +15535,7 @@ async function evaluateWatch(watch) {
15001
15535
  return { matched: exists, data: { exists, layerName: watch.layerName } };
15002
15536
  }
15003
15537
  case "doc_name": {
15004
- const pattern = escapeLispStr(watch.pattern || "*");
15538
+ const pattern = escapeLispStr2(watch.pattern || "*");
15005
15539
  result = await cad.sendCommandWithResult(
15006
15540
  `(progn (setq _dn (getvar "dwgname")) (setq _dm (if (wcmatch _dn "${pattern}") 1 0)) (list _dm _dn))`
15007
15541
  );
@@ -15011,14 +15545,14 @@ async function evaluateWatch(watch) {
15011
15545
  return { matched, data: { matched: Boolean(matched), name } };
15012
15546
  }
15013
15547
  case "doc_path": {
15014
- const pattern = escapeLispStr(watch.pattern || "*");
15548
+ const pattern = escapeLispStr2(watch.pattern || "*");
15015
15549
  result = await cad.sendCommandWithResult(
15016
15550
  `(progn (setq _dp (getvar "dwgprefix")) (setq _dm (if (wcmatch _dp "${pattern}") 1 0)) (list _dm _dp))`
15017
15551
  );
15018
15552
  const arr = parseLispList2(result) || [];
15019
15553
  const matched = Number(arr[0]) !== 0;
15020
- const path20 = String(arr[1] || "");
15021
- return { matched, data: { matched: Boolean(matched), path: path20 } };
15554
+ const path21 = String(arr[1] || "");
15555
+ return { matched, data: { matched: Boolean(matched), path: path21 } };
15022
15556
  }
15023
15557
  case "entity_selected": {
15024
15558
  result = await cad.sendCommandWithResult(
@@ -15188,53 +15722,6 @@ function stripStringsAndComments(code) {
15188
15722
  const withoutStrings = code.replace(/"([^"\\]*(?:\\.[^"\\]*)*)"/g, "");
15189
15723
  return withoutStrings.replace(/;.*$/gm, "");
15190
15724
  }
15191
- var THREAT_BLOCK, THREAT_WARN, THREAT_ALLOW, CVT, SECURITY_RULES, _cachedLevel;
15192
- var init_lisp_security = __esm({
15193
- "src/lisp-security.js"() {
15194
- init_logger();
15195
- init_config();
15196
- THREAT_BLOCK = "block";
15197
- THREAT_WARN = "warn";
15198
- THREAT_ALLOW = "allow";
15199
- CVT = {
15200
- strict: THREAT_BLOCK,
15201
- standard: THREAT_WARN,
15202
- relaxed: THREAT_ALLOW
15203
- };
15204
- SECURITY_RULES = [
15205
- { re: /\bstartapp\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 startapp \u51FD\u6570" },
15206
- { re: /\bvl-bt\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 vl-bt \u51FD\u6570" },
15207
- { re: /\bvlax-import-type-library\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u5BFC\u5165\u7C7B\u578B\u5E93" },
15208
- { re: /\bdos_command_string\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 dos_command_string" },
15209
- { re: /\beval\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 eval \u51FD\u6570" },
15210
- { 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" },
15211
- { re: /\bload\s*[(\s"]/i, baseSeverity: THREAT_BLOCK, msg: "load \u51FD\u6570\u53EF\u80FD\u52A0\u8F7D\u5916\u90E8\u4EE3\u7801" },
15212
- { 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" },
15213
- { re: /\bvl-file-copy\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u6587\u4EF6\u590D\u5236" },
15214
- { re: /\bvl-file-delete\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u6587\u4EF6\u5220\u9664" },
15215
- { re: /\bvl-file-rename\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u6587\u4EF6\u91CD\u547D\u540D" },
15216
- { re: /\bvl-registry-write[\s\)]/i, baseSeverity: THREAT_BLOCK, msg: "vl-registry-write \u4F1A\u4FEE\u6539\u6CE8\u518C\u8868" },
15217
- { re: /\bvl-exit-with-value\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 vl-exit-with-value" },
15218
- { re: /\bvl-exit-with-error\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 vl-exit-with-error" },
15219
- { re: /\bvlax-add-cmd\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u6CE8\u518C\u65B0\u547D\u4EE4" },
15220
- { re: /\bvla-Delete\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u76F4\u63A5\u5220\u9664 vla \u5BF9\u8C61" },
15221
- { re: /\bvla-Save\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u76F4\u63A5\u4FDD\u5B58\u6587\u6863" },
15222
- { re: /\bdirectory\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 directory \u51FD\u6570" },
15223
- { re: /\bvla-deletefile\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u5220\u9664\u6587\u4EF6" },
15224
- { re: /\bvla-copyfile\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u590D\u5236\u6587\u4EF6" },
15225
- { re: /\bvla-movefile\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u79FB\u52A8\u6587\u4EF6" }
15226
- ];
15227
- _cachedLevel = null;
15228
- try {
15229
- onConfigChange(() => {
15230
- _cachedLevel = null;
15231
- });
15232
- } catch (_) {
15233
- }
15234
- }
15235
- });
15236
-
15237
- // src/lisp-sandbox.js
15238
15725
  function getSessionAllowlist(sessionId) {
15239
15726
  if (!sessionAllowlist.has(sessionId)) {
15240
15727
  sessionAllowlist.set(sessionId, /* @__PURE__ */ new Set());
@@ -15278,12 +15765,49 @@ function validateLispCode(code, sessionId = null) {
15278
15765
  summary: blocked ? `\u5B89\u5168\u9650\u5236: ${results.filter((r) => r.blocked).map((r) => r.message).join("; ")}` : "\u901A\u8FC7"
15279
15766
  };
15280
15767
  }
15281
- var RULES2, sessionAllowlist;
15282
- var init_lisp_sandbox = __esm({
15283
- "src/lisp-sandbox.js"() {
15768
+ var THREAT_BLOCK, THREAT_WARN, THREAT_ALLOW, CVT, SECURITY_RULES, _cachedLevel, RULES2, sessionAllowlist;
15769
+ var init_lisp_security = __esm({
15770
+ "src/lisp-security.js"() {
15284
15771
  init_logger();
15285
15772
  init_config();
15286
- init_lisp_security();
15773
+ THREAT_BLOCK = "block";
15774
+ THREAT_WARN = "warn";
15775
+ THREAT_ALLOW = "allow";
15776
+ CVT = {
15777
+ strict: THREAT_BLOCK,
15778
+ standard: THREAT_WARN,
15779
+ relaxed: THREAT_ALLOW
15780
+ };
15781
+ SECURITY_RULES = [
15782
+ { re: /\bstartapp\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 startapp \u51FD\u6570" },
15783
+ { re: /\bvl-bt\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 vl-bt \u51FD\u6570" },
15784
+ { re: /\bvlax-import-type-library\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u5BFC\u5165\u7C7B\u578B\u5E93" },
15785
+ { re: /\bdos_command_string\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 dos_command_string" },
15786
+ { re: /\beval\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 eval \u51FD\u6570" },
15787
+ { 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" },
15788
+ { re: /\bload\s*[(\s"]/i, baseSeverity: THREAT_BLOCK, msg: "load \u51FD\u6570\u53EF\u80FD\u52A0\u8F7D\u5916\u90E8\u4EE3\u7801" },
15789
+ { 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" },
15790
+ { re: /\bvl-file-copy\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u6587\u4EF6\u590D\u5236" },
15791
+ { re: /\bvl-file-delete\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u6587\u4EF6\u5220\u9664" },
15792
+ { re: /\bvl-file-rename\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u6587\u4EF6\u91CD\u547D\u540D" },
15793
+ { re: /\bvl-registry-write[\s\)]/i, baseSeverity: THREAT_BLOCK, msg: "vl-registry-write \u4F1A\u4FEE\u6539\u6CE8\u518C\u8868" },
15794
+ { re: /\bvl-exit-with-value\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 vl-exit-with-value" },
15795
+ { re: /\bvl-exit-with-error\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 vl-exit-with-error" },
15796
+ { re: /\bvlax-add-cmd\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u6CE8\u518C\u65B0\u547D\u4EE4" },
15797
+ { re: /\bvla-Delete\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u76F4\u63A5\u5220\u9664 vla \u5BF9\u8C61" },
15798
+ { re: /\bvla-Save\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u76F4\u63A5\u4FDD\u5B58\u6587\u6863" },
15799
+ { re: /\bdirectory\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 directory \u51FD\u6570" },
15800
+ { re: /\bvla-deletefile\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u5220\u9664\u6587\u4EF6" },
15801
+ { re: /\bvla-copyfile\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u590D\u5236\u6587\u4EF6" },
15802
+ { re: /\bvla-movefile\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u79FB\u52A8\u6587\u4EF6" }
15803
+ ];
15804
+ _cachedLevel = null;
15805
+ try {
15806
+ onConfigChange(() => {
15807
+ _cachedLevel = null;
15808
+ });
15809
+ } catch (_) {
15810
+ }
15287
15811
  RULES2 = SECURITY_RULES;
15288
15812
  sessionAllowlist = /* @__PURE__ */ new Map();
15289
15813
  }
@@ -15404,13 +15928,13 @@ ${analysis.errors.map((e) => ` L${e.line}: [${e.rule}] ${e.message}`).join("\n"
15404
15928
  });
15405
15929
 
15406
15930
  // src/webhook-engine.js
15407
- import fs9 from "fs";
15408
- import path13 from "path";
15409
- import os7 from "os";
15931
+ import fs10 from "fs";
15932
+ import path15 from "path";
15933
+ import os8 from "os";
15410
15934
  function loadWebhooks() {
15411
15935
  try {
15412
- if (fs9.existsSync(WEBHOOKS_FILE)) {
15413
- webhooks = JSON.parse(fs9.readFileSync(WEBHOOKS_FILE, "utf-8"));
15936
+ if (fs10.existsSync(WEBHOOKS_FILE)) {
15937
+ webhooks = JSON.parse(fs10.readFileSync(WEBHOOKS_FILE, "utf-8"));
15414
15938
  }
15415
15939
  } catch (e) {
15416
15940
  error(`Webhook load error: ${e.message}`);
@@ -15418,8 +15942,8 @@ function loadWebhooks() {
15418
15942
  }
15419
15943
  function saveWebhooks() {
15420
15944
  try {
15421
- ensureDir(path13.dirname(WEBHOOKS_FILE));
15422
- fs9.writeFileSync(WEBHOOKS_FILE, JSON.stringify(webhooks, null, 2), "utf-8");
15945
+ ensureDir(path15.dirname(WEBHOOKS_FILE));
15946
+ fs10.writeFileSync(WEBHOOKS_FILE, JSON.stringify(webhooks, null, 2), "utf-8");
15423
15947
  } catch (e) {
15424
15948
  error(`Webhook save error: ${e.message}`);
15425
15949
  }
@@ -15476,10 +16000,10 @@ function listWebhooks() {
15476
16000
  }
15477
16001
  function getWebhookLog(id) {
15478
16002
  const sanitized = String(id).replace(/[^a-zA-Z0-9_-]/g, "_");
15479
- const logPath = path13.join(os7.homedir(), ".atlisp", `webhook-${sanitized}.log`);
16003
+ const logPath = path15.join(os8.homedir(), ".atlisp", `webhook-${sanitized}.log`);
15480
16004
  try {
15481
- if (!fs9.existsSync(logPath)) return [];
15482
- return fs9.readFileSync(logPath, "utf-8").split("\n").filter(Boolean).map((l) => JSON.parse(l));
16005
+ if (!fs10.existsSync(logPath)) return [];
16006
+ return fs10.readFileSync(logPath, "utf-8").split("\n").filter(Boolean).map((l) => JSON.parse(l));
15483
16007
  } catch (e) {
15484
16008
  error(`Webhook log read error [${sanitized}]: ${e.message}`);
15485
16009
  return [];
@@ -15490,30 +16014,30 @@ var init_webhook_engine = __esm({
15490
16014
  "src/webhook-engine.js"() {
15491
16015
  init_logger();
15492
16016
  init_handler_utils();
15493
- WEBHOOKS_FILE = path13.join(os7.homedir(), ".atlisp", "webhooks.json");
16017
+ WEBHOOKS_FILE = path15.join(os8.homedir(), ".atlisp", "webhooks.json");
15494
16018
  webhooks = [];
15495
16019
  loadWebhooks();
15496
16020
  }
15497
16021
  });
15498
16022
 
15499
16023
  // src/audit-log.js
15500
- import fs10 from "fs";
15501
- import path14 from "path";
15502
- import os8 from "os";
16024
+ import fs11 from "fs";
16025
+ import path16 from "path";
16026
+ import os9 from "os";
15503
16027
  import zlib from "zlib";
15504
16028
  function rotateIfNeeded() {
15505
16029
  try {
15506
- if (fs10.existsSync(AUDIT_FILE) && fs10.statSync(AUDIT_FILE).size > MAX_LOG_SIZE2) {
16030
+ if (fs11.existsSync(AUDIT_FILE) && fs11.statSync(AUDIT_FILE).size > MAX_LOG_SIZE2) {
15507
16031
  const rotated = AUDIT_FILE.replace(".log", `.${Date.now()}.log`);
15508
- fs10.renameSync(AUDIT_FILE, rotated);
16032
+ fs11.renameSync(AUDIT_FILE, rotated);
15509
16033
  try {
15510
- const compressed = zlib.gzipSync(fs10.readFileSync(rotated));
15511
- fs10.writeFileSync(rotated + ".gz", compressed);
15512
- fs10.unlinkSync(rotated);
16034
+ const compressed = zlib.gzipSync(fs11.readFileSync(rotated));
16035
+ fs11.writeFileSync(rotated + ".gz", compressed);
16036
+ fs11.unlinkSync(rotated);
15513
16037
  } catch (e) {
15514
16038
  error(`Audit log compression failed, keeping raw file: ${e.message}`);
15515
16039
  try {
15516
- fs10.unlinkSync(rotated + ".gz");
16040
+ fs11.unlinkSync(rotated + ".gz");
15517
16041
  } catch {
15518
16042
  }
15519
16043
  }
@@ -15537,15 +16061,15 @@ function writeAuditEntry(entry) {
15537
16061
  user: entry.user || "",
15538
16062
  role: entry.role || ""
15539
16063
  }) + "\n";
15540
- fs10.appendFileSync(AUDIT_FILE, line, "utf-8");
16064
+ fs11.appendFileSync(AUDIT_FILE, line, "utf-8");
15541
16065
  } catch (e) {
15542
16066
  error(`Audit log write error: ${e.message}`);
15543
16067
  }
15544
16068
  }
15545
16069
  function queryAuditLog(filters = {}) {
15546
16070
  try {
15547
- if (!fs10.existsSync(AUDIT_FILE)) return { total: 0, offset: 0, count: 0, entries: [] };
15548
- const content = fs10.readFileSync(AUDIT_FILE, "utf-8");
16071
+ if (!fs11.existsSync(AUDIT_FILE)) return { total: 0, offset: 0, count: 0, entries: [] };
16072
+ const content = fs11.readFileSync(AUDIT_FILE, "utf-8");
15549
16073
  const lines = content.trim().split("\n").filter(Boolean);
15550
16074
  let entries = lines.map((l) => {
15551
16075
  try {
@@ -15577,8 +16101,8 @@ function queryAuditLog(filters = {}) {
15577
16101
  }
15578
16102
  function getAuditStats() {
15579
16103
  try {
15580
- if (!fs10.existsSync(AUDIT_FILE)) return { totalEntries: 0, tools: {}, errors: 0 };
15581
- const content = fs10.readFileSync(AUDIT_FILE, "utf-8");
16104
+ if (!fs11.existsSync(AUDIT_FILE)) return { totalEntries: 0, tools: {}, errors: 0 };
16105
+ const content = fs11.readFileSync(AUDIT_FILE, "utf-8");
15582
16106
  const lines = content.trim().split("\n").filter(Boolean);
15583
16107
  const toolCounts = {};
15584
16108
  let errors = 0;
@@ -15594,7 +16118,7 @@ function getAuditStats() {
15594
16118
  totalEntries: lines.length,
15595
16119
  tools: toolCounts,
15596
16120
  errors,
15597
- fileSize: fs10.existsSync(AUDIT_FILE) ? fs10.statSync(AUDIT_FILE).size : 0,
16121
+ fileSize: fs11.existsSync(AUDIT_FILE) ? fs11.statSync(AUDIT_FILE).size : 0,
15598
16122
  filePath: AUDIT_FILE
15599
16123
  };
15600
16124
  } catch (e) {
@@ -15606,8 +16130,8 @@ var init_audit_log = __esm({
15606
16130
  "src/audit-log.js"() {
15607
16131
  init_logger();
15608
16132
  init_handler_utils();
15609
- AUDIT_DIR = path14.join(os8.homedir(), "@lisp", "logs");
15610
- AUDIT_FILE = path14.join(AUDIT_DIR, "audit.log");
16133
+ AUDIT_DIR = path16.join(os9.homedir(), "@lisp", "logs");
16134
+ AUDIT_FILE = path16.join(AUDIT_DIR, "audit.log");
15611
16135
  MAX_LOG_SIZE2 = 10 * 1024 * 1024;
15612
16136
  MAX_LOG_AGE = 90 * 24 * 60 * 60 * 1e3;
15613
16137
  }
@@ -15641,14 +16165,17 @@ var init_agent_context = __esm({
15641
16165
  });
15642
16166
 
15643
16167
  // src/tool-validators.js
15644
- function validateProps(args, props, required, path20 = "") {
16168
+ function validationError(msg) {
16169
+ return new MCPError(ERROR_CODES.INVALID_ARGS, msg);
16170
+ }
16171
+ function validateProps(args, props, required, path21 = "") {
15645
16172
  for (const [key, prop] of Object.entries(props)) {
15646
- const fullKey = path20 ? `${path20}.${key}` : key;
16173
+ const fullKey = path21 ? `${path21}.${key}` : key;
15647
16174
  const isOptional = !required.includes(key);
15648
16175
  const val = args[key];
15649
16176
  if (val === void 0 || val === null) {
15650
16177
  if (isOptional) continue;
15651
- throw new Error(`Missing required argument: ${fullKey}`);
16178
+ throw validationError(`Missing required argument: ${fullKey}`);
15652
16179
  }
15653
16180
  if (prop.type === "object" && prop.properties && typeof val === "object" && !Array.isArray(val)) {
15654
16181
  const subRequired = prop.required || [];
@@ -15663,46 +16190,46 @@ function validateProps(args, props, required, path20 = "") {
15663
16190
  const actualType = typeof val;
15664
16191
  const expectedType = prop.type || "string";
15665
16192
  if (expectedType === "string" && actualType !== "string" && actualType !== "number") {
15666
- throw new Error(`Invalid type for argument ${fullKey}: expected string, got ${actualType}`);
16193
+ throw validationError(`Invalid type for argument ${fullKey}: expected string, got ${actualType}`);
15667
16194
  }
15668
16195
  if ((expectedType === "object" || expectedType === "array") && (actualType !== "object" || val === null)) {
15669
- throw new Error(`Invalid type for argument ${fullKey}: expected ${expectedType}, got ${actualType}`);
16196
+ throw validationError(`Invalid type for argument ${fullKey}: expected ${expectedType}, got ${actualType}`);
15670
16197
  }
15671
16198
  if (expectedType === "array" && !Array.isArray(val)) {
15672
- throw new Error(`Invalid type for argument ${fullKey}: expected array, got ${actualType}`);
16199
+ throw validationError(`Invalid type for argument ${fullKey}: expected array, got ${actualType}`);
15673
16200
  }
15674
16201
  if (expectedType === "object" && Array.isArray(val)) {
15675
- throw new Error(`Invalid type for argument ${fullKey}: expected object, got array`);
16202
+ throw validationError(`Invalid type for argument ${fullKey}: expected object, got array`);
15676
16203
  }
15677
16204
  if ((expectedType === "number" || expectedType === "integer") && actualType !== "number") {
15678
16205
  if (expectedType === "integer" && !Number.isInteger(val)) {
15679
- throw new Error(`Invalid type for argument ${fullKey}: expected integer, got ${actualType}`);
16206
+ throw validationError(`Invalid type for argument ${fullKey}: expected integer, got ${actualType}`);
15680
16207
  }
15681
16208
  if (expectedType === "number") {
15682
- throw new Error(`Invalid type for argument ${fullKey}: expected number, got ${actualType}`);
16209
+ throw validationError(`Invalid type for argument ${fullKey}: expected number, got ${actualType}`);
15683
16210
  }
15684
16211
  }
15685
16212
  if (expectedType === "boolean" && actualType !== "boolean") {
15686
- throw new Error(`Invalid type for argument ${fullKey}: expected boolean, got ${actualType}`);
16213
+ throw validationError(`Invalid type for argument ${fullKey}: expected boolean, got ${actualType}`);
15687
16214
  }
15688
16215
  if (prop.enum && !prop.enum.includes(val)) {
15689
- throw new Error(`Invalid value for argument ${fullKey}: must be one of ${prop.enum.join(", ")}`);
16216
+ throw validationError(`Invalid value for argument ${fullKey}: must be one of ${prop.enum.join(", ")}`);
15690
16217
  }
15691
16218
  if (expectedType === "string" && typeof prop.maxLength === "number" && String(val).length > prop.maxLength) {
15692
- throw new Error(`Argument ${fullKey} exceeds max length of ${prop.maxLength}`);
16219
+ throw validationError(`Argument ${fullKey} exceeds max length of ${prop.maxLength}`);
15693
16220
  }
15694
16221
  if ((expectedType === "number" || expectedType === "integer") && prop.minimum != null && val < prop.minimum) {
15695
- throw new Error(`Argument ${fullKey} must be >= ${prop.minimum}`);
16222
+ throw validationError(`Argument ${fullKey} must be >= ${prop.minimum}`);
15696
16223
  }
15697
16224
  if ((expectedType === "number" || expectedType === "integer") && prop.maximum != null && val > prop.maximum) {
15698
- throw new Error(`Argument ${fullKey} must be <= ${prop.maximum}`);
16225
+ throw validationError(`Argument ${fullKey} must be <= ${prop.maximum}`);
15699
16226
  }
15700
16227
  }
15701
16228
  }
15702
16229
  function validateToolArgs(name, args) {
15703
16230
  const toolDef = toolsByName.get(name);
15704
16231
  if (!toolDef) {
15705
- throw new Error(`Unknown tool: ${name}`);
16232
+ throw validationError(`Unknown tool: ${name}`);
15706
16233
  }
15707
16234
  const schema = toolDef.inputSchema;
15708
16235
  if (!schema || !schema.properties) return true;
@@ -15710,7 +16237,7 @@ function validateToolArgs(name, args) {
15710
16237
  const required = schema.required || [];
15711
16238
  for (const req of required) {
15712
16239
  if (!(req in (args || {}))) {
15713
- throw new Error(`Missing required argument: ${req}`);
16240
+ throw validationError(`Missing required argument: ${req}`);
15714
16241
  }
15715
16242
  }
15716
16243
  validateProps(args || {}, props, required);
@@ -15719,6 +16246,7 @@ function validateToolArgs(name, args) {
15719
16246
  var init_tool_validators = __esm({
15720
16247
  "src/tool-validators.js"() {
15721
16248
  init_tools();
16249
+ init_errors();
15722
16250
  }
15723
16251
  });
15724
16252
 
@@ -15829,7 +16357,7 @@ function registerTool(toolDef, handler) {
15829
16357
  }
15830
16358
  customTools.push(toolDef);
15831
16359
  customHandlers.set(toolDef.name, handler);
15832
- tools42.push(toolDef);
16360
+ tools43.push(toolDef);
15833
16361
  return true;
15834
16362
  }
15835
16363
  function callCustomTool(name, args) {
@@ -15849,73 +16377,92 @@ var init_tool_registry = __esm({
15849
16377
  });
15850
16378
 
15851
16379
  // src/tool-executor.js
16380
+ function resolveConn(instance) {
16381
+ if (instance) return pool.getConnection(instance) || resolveCadConnection();
16382
+ return cad;
16383
+ }
16384
+ async function writeAuditSuccess(name, args, options) {
16385
+ writeAuditEntry({
16386
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
16387
+ tool: name,
16388
+ args: args || {},
16389
+ sessionId: options.sessionId || "",
16390
+ result: "success"
16391
+ });
16392
+ }
16393
+ async function writeAuditError(name, args, options, error2) {
16394
+ writeAuditEntry({
16395
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
16396
+ tool: name,
16397
+ args: args || {},
16398
+ sessionId: options.sessionId || "",
16399
+ result: "error",
16400
+ error: error2.message
16401
+ });
16402
+ }
16403
+ async function beginUndo(instance) {
16404
+ const conn = resolveConn(instance);
16405
+ if (conn?.connected) {
16406
+ try {
16407
+ await conn.sendCommand('(command "_.UNDO" "_BEGIN")');
16408
+ return true;
16409
+ } catch (e) {
16410
+ error(`UNDO_BEGIN failed: ${e.message}`);
16411
+ }
16412
+ }
16413
+ return false;
16414
+ }
16415
+ async function endUndo(instance) {
16416
+ const conn = resolveConn(instance);
16417
+ try {
16418
+ await conn.sendCommand('(command "_.UNDO" "_END")');
16419
+ } catch (_) {
16420
+ }
16421
+ }
15852
16422
  async function handleToolCall(name, args, options = {}) {
15853
16423
  const handler = TOOL_HANDLERS[name];
15854
16424
  if (!handler) {
15855
16425
  const customResult = callCustomTool(name, args);
15856
16426
  if (customResult !== null) return customResult;
15857
- return { content: [{ type: "text", text: `\u672A\u77E5\u5DE5\u5177: ${name}` }], isError: true };
16427
+ return mcpErrorResponse(new MCPError(ERROR_CODES.HANDLER_NOT_FOUND, `\u672A\u77E5\u5DE5\u5177: ${name}`));
15858
16428
  }
15859
16429
  const isReadOnly = READ_ONLY_TOOLS.has(name);
15860
16430
  const notifyProgress = options.notifyProgress || null;
15861
16431
  const rateCheck = checkToolRateLimit(name, isReadOnly, options.sessionId);
15862
16432
  if (!rateCheck.allowed) {
15863
- 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 };
16433
+ return mcpErrorResponse(new MCPError(ERROR_CODES.RATE_LIMITED, `\u5DE5\u5177\u8C03\u7528\u9891\u7387\u8D85\u9650\uFF0C\u8BF7 ${rateCheck.retryAfter} \u79D2\u540E\u91CD\u8BD5`));
15864
16434
  }
15865
16435
  return progressContext.run({ notifyProgress }, async () => {
15866
16436
  setNotifyProgress(notifyProgress);
16437
+ const instance = args?.instance || null;
15867
16438
  let undoBegun = false;
15868
16439
  try {
15869
16440
  validateToolArgs(name, args || {});
15870
- const instance = args?.instance || null;
15871
16441
  if (instance) {
15872
16442
  const ctx = mcpContext.getStore();
15873
16443
  ctx._toolInstance = instance;
15874
16444
  }
15875
16445
  if (!isReadOnly) {
15876
- const targetConn = instance ? pool.getConnection(instance) : cad;
15877
- if (targetConn?.connected) {
15878
- try {
15879
- await targetConn.sendCommand('(command "_.UNDO" "_BEGIN")');
15880
- undoBegun = true;
15881
- } catch (e) {
15882
- error(`UNDO_BEGIN failed: ${e.message}`);
15883
- }
15884
- }
16446
+ undoBegun = await beginUndo(instance);
15885
16447
  }
15886
16448
  const np = getNotifyProgress();
15887
16449
  if (np) np(1, 1, `\u6B63\u5728\u6267\u884C ${name}...`);
15888
16450
  const result = await handler(args || {});
15889
16451
  if (np) np(1, 1, `${name} \u6267\u884C\u5B8C\u6210`);
15890
- writeAuditEntry({
15891
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
15892
- tool: name,
15893
- args: args || {},
15894
- sessionId: options.sessionId || "",
15895
- result: result?.isError ? "error" : "success"
15896
- });
16452
+ await writeAuditSuccess(name, args, options);
15897
16453
  notifyResourceChanges(name);
15898
16454
  return result;
15899
16455
  } catch (e) {
15900
16456
  error(`Tool call error [${name}]: ${e.message}`, { tool: name, args });
15901
- writeAuditEntry({
15902
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
15903
- tool: name,
15904
- args: args || {},
15905
- sessionId: options.sessionId || "",
15906
- result: "error",
15907
- error: e.message
15908
- });
15909
- return { content: [{ type: "text", text: `\u5DE5\u5177\u6267\u884C\u9519\u8BEF: ${e.message}` }], isError: true };
16457
+ await writeAuditError(name, args, options, e);
16458
+ if (e.message.startsWith("Missing required") || e.message.startsWith("Invalid type")) {
16459
+ return mcpErrorResponse(new MCPError(ERROR_CODES.INVALID_ARGS, e.message));
16460
+ }
16461
+ return mcpErrorResponse(e);
15910
16462
  } finally {
15911
16463
  const ctx = mcpContext.getStore();
15912
16464
  if (ctx) ctx._toolInstance = null;
15913
- if (undoBegun) {
15914
- try {
15915
- await cad.sendCommand('(command "_.UNDO" "_END")');
15916
- } catch (_) {
15917
- }
15918
- }
16465
+ if (undoBegun) await endUndo(instance);
15919
16466
  }
15920
16467
  });
15921
16468
  }
@@ -15932,14 +16479,12 @@ var init_tool_executor = __esm({
15932
16479
  init_cad();
15933
16480
  init_cad_pool();
15934
16481
  init_audit_log();
16482
+ init_errors();
15935
16483
  init_tool_registry();
15936
16484
  }
15937
16485
  });
15938
16486
 
15939
16487
  // src/mcp-tools.js
15940
- import fs11 from "fs";
15941
- import path15 from "path";
15942
- import os9 from "os";
15943
16488
  import { formatCode as formatCode2 } from "@atlisp/lint/dist/formatter.js";
15944
16489
  import { lintProject as lintProject2 } from "@atlisp/lint/dist/project.js";
15945
16490
  import { RULES as RULES3 } from "@atlisp/lint/dist/rules.js";
@@ -15956,7 +16501,6 @@ var TOOL_HANDLERS;
15956
16501
  var init_mcp_tools = __esm({
15957
16502
  "src/mcp-tools.js"() {
15958
16503
  init_handler_loader();
15959
- init_prompt_handlers();
15960
16504
  init_streaming();
15961
16505
  init_constants();
15962
16506
  init_cad();
@@ -15964,13 +16508,24 @@ var init_mcp_tools = __esm({
15964
16508
  init_tools();
15965
16509
  init_mcp_server_state();
15966
16510
  init_trigger_engine();
15967
- init_lisp_sandbox();
16511
+ init_lisp_security();
15968
16512
  init_lint_analyzer();
15969
16513
  init_repl_engine();
15970
16514
  init_webhook_engine();
15971
16515
  init_audit_log();
15972
16516
  init_agent_context();
16517
+ init_resource_handlers();
16518
+ init_subscription_manager();
16519
+ init_skill_handlers();
16520
+ init_meta_handlers();
15973
16521
  init_tool_executor();
16522
+ pool.onInstanceSwitch(() => {
16523
+ for (const uri of CAD_RESOURCE_URIS) {
16524
+ if (isSubscribed(uri)) {
16525
+ notify2(uri, null);
16526
+ }
16527
+ }
16528
+ });
15974
16529
  TOOL_HANDLERS = {
15975
16530
  // === CAD Connection ===
15976
16531
  connect_cad: lazy("connect_cad", (a) => a?.platform),
@@ -16007,14 +16562,8 @@ ${CAD_PLATFORMS.join("\n")}
16007
16562
  return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
16008
16563
  },
16009
16564
  // === Prompts ===
16010
- list_prompts: async () => {
16011
- const prompts = await listPrompts2();
16012
- return { content: [{ type: "text", text: JSON.stringify(prompts) }] };
16013
- },
16014
- get_prompt: async (a) => {
16015
- const prompt = await getPrompt2(a.name, a.args || {});
16016
- return { content: prompt.messages.map((m) => ({ type: "text", text: m.content.text || m.content })) };
16017
- },
16565
+ list_prompts: list_prompts_handler,
16566
+ get_prompt: get_prompt_handler,
16018
16567
  // === Entity Operations ===
16019
16568
  get_entity: lazy("get_entity", (a) => a?.handle),
16020
16569
  set_entity: lazy("set_entity", (a) => [a?.handle, a?.property, a?.value]),
@@ -16301,6 +16850,35 @@ ${CAD_PLATFORMS.join("\n")}
16301
16850
  draw_ray: lazy("draw_ray", (a) => [a.point, a.direction]),
16302
16851
  draw_leader: lazy("draw_leader", (a) => [a.points, a.annotation]),
16303
16852
  draw_table: lazy("draw_table", (a) => [a.insertionPoint, a.rows, a.columns, a.rowHeight, a.colWidth]),
16853
+ // === Batch Concurrent ===
16854
+ batch_concurrent_tasks: async (a) => {
16855
+ if (!a.tasks || !Array.isArray(a.tasks) || a.tasks.length === 0) {
16856
+ return { content: [{ type: "text", text: "\u9700\u8981 tasks \u6570\u7EC4" }], isError: true };
16857
+ }
16858
+ if (a.tasks.length > 50) {
16859
+ return { content: [{ type: "text", text: "\u6700\u591A\u652F\u6301 50 \u4E2A\u5E76\u53D1\u4EFB\u52A1" }], isError: true };
16860
+ }
16861
+ const results = await Promise.allSettled(a.tasks.map(async (task, idx) => {
16862
+ const handler = TOOL_HANDLERS[task.tool];
16863
+ if (!handler) return { index: idx, tool: task.tool, instance: task.instance, ok: false, error: `\u672A\u77E5\u5DE5\u5177: ${task.tool}` };
16864
+ const ctx = mcpContext.getStore();
16865
+ const prevInstance = ctx?._toolInstance || null;
16866
+ if (ctx && task.instance) ctx._toolInstance = task.instance;
16867
+ try {
16868
+ const result = await handler(task.args || {});
16869
+ return { index: idx, tool: task.tool, instance: task.instance, ok: !result.isError, data: result.content?.[0]?.text || result };
16870
+ } catch (e) {
16871
+ return { index: idx, tool: task.tool, instance: task.instance, ok: false, error: e.message };
16872
+ } finally {
16873
+ if (ctx) ctx._toolInstance = prevInstance;
16874
+ }
16875
+ }));
16876
+ const output = results.map((r, idx) => {
16877
+ if (r.status === "fulfilled") return r.value;
16878
+ return { index: idx, tool: a.tasks[idx].tool, instance: a.tasks[idx].instance, ok: false, error: r.reason?.message || "Unknown error" };
16879
+ });
16880
+ return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }] };
16881
+ },
16304
16882
  // === Batch Transaction ===
16305
16883
  batch_transaction: lazy("batch_transaction"),
16306
16884
  // === Watch / Trigger ===
@@ -16457,6 +17035,14 @@ ${CAD_PLATFORMS.join("\n")}
16457
17035
  },
16458
17036
  cad_pool_switch: (a) => {
16459
17037
  const ok = pool.setActive(a.key);
17038
+ if (ok) {
17039
+ clearCache();
17040
+ for (const uri of CAD_RESOURCE_URIS) {
17041
+ if (isSubscribed(uri)) {
17042
+ notify2(uri, null);
17043
+ }
17044
+ }
17045
+ }
16460
17046
  return { content: [{ type: "text", text: ok ? `\u5DF2\u5207\u6362\u5230\u5B9E\u4F8B ${a.key}` : `\u5B9E\u4F8B ${a.key} \u4E0D\u5B58\u5728` }] };
16461
17047
  },
16462
17048
  cad_pool_health: async () => {
@@ -16468,8 +17054,9 @@ ${CAD_PLATFORMS.join("\n")}
16468
17054
  return { content: [{ type: "text", text: JSON.stringify(stats, null, 2) }] };
16469
17055
  },
16470
17056
  cad_pool_connect: async (a) => {
16471
- const ok = await pool.connect(a.key, a.platform, { label: a.label, makeActive: a.makeActive !== false });
16472
- return { content: [{ type: "text", text: ok ? `\u5B9E\u4F8B "${a.key}" \u5DF2\u8FDE\u63A5` : `\u5B9E\u4F8B "${a.key}" \u8FDE\u63A5\u5931\u8D25` }] };
17057
+ const ok = await pool.connect(a.key, a.platform, { label: a.label, pid: a.pid, makeActive: a.makeActive !== false });
17058
+ const label = a.key || (a.pid != null ? `PID ${a.pid}` : "default");
17059
+ return { content: [{ type: "text", text: ok ? `\u5B9E\u4F8B "${label}" \u5DF2\u8FDE\u63A5` : `\u5B9E\u4F8B "${label}" \u8FDE\u63A5\u5931\u8D25` }] };
16473
17060
  },
16474
17061
  cad_pool_disconnect: async (a) => {
16475
17062
  const ok = await pool.disconnect(a.key);
@@ -16519,261 +17106,15 @@ ${CAD_PLATFORMS.join("\n")}
16519
17106
  return { content: [{ type: "text", text: "\u4E0A\u4E0B\u6587\u5DF2\u6E05\u9664" }] };
16520
17107
  },
16521
17108
  // === Meta Tools ===
16522
- search_tools: async (a) => {
16523
- const q = (a.query || "").toLowerCase();
16524
- const results = tools42.filter((t) => t.name.toLowerCase().includes(q) || (t.description || "").toLowerCase().includes(q));
16525
- const text = results.length ? `\u627E\u5230 ${results.length} \u4E2A\u5DE5\u5177:
16526
- ${results.map((t) => `${t.name}: ${t.description}`).join("\n")}` : `\u672A\u627E\u5230\u5305\u542B "${a.query}" \u7684\u5DE5\u5177`;
16527
- return { content: [{ type: "text", text }] };
16528
- },
16529
- list_tools_by_category: async (a) => {
16530
- const categories = {};
16531
- for (const t of tools42) {
16532
- let cat = "\u5176\u4ED6";
16533
- const n = t.name;
16534
- 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";
16535
- else if (/^(move_|copy_|rotate_|scale_|mirror_|offset_|array_|batch_)/.test(n)) cat = "\u4FEE\u6539";
16536
- else if (/^(trim_|extend_|fillet|chamfer|break_|join_|stretch)/.test(n)) cat = "\u7F16\u8F91";
16537
- else if (/^(create_layer|delete_layer|set_layer|layer_)/.test(n)) cat = "\u56FE\u5C42";
16538
- else if (/^(create_block|insert_block|explode_block|get_block|set_block|batch_get_block|batch_set_block)/.test(n)) cat = "\u5757";
16539
- else if (/^(create_dim|list_dim|set_dim|create_hatch|set_hatch)/.test(n)) cat = "\u6807\u6CE8\u4E0E\u586B\u5145";
16540
- 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";
16541
- else if (/^(create_viewport|set_viewport|lock_viewport|unlock_viewport|maximize_viewport)/.test(n)) cat = "\u89C6\u53E3";
16542
- else if (/^find_/.test(n)) cat = "\u7A7A\u95F4\u67E5\u8BE2";
16543
- else if (/^(select_|get_entity|set_entity|delete_entity|get_bounding_box)/.test(n)) cat = "\u9009\u62E9\u4E0E\u5C5E\u6027";
16544
- else if (/^(open_dwg|save_dwg|export_|import_|close_dwg|publish_)/.test(n)) cat = "\u6587\u4EF6";
16545
- else if (/^(plot_|save_pdf|batch_plot|publish_layouts)/.test(n)) cat = "\u6253\u5370";
16546
- 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";
16547
- 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";
16548
- 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";
16549
- 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";
16550
- else if (/^(purge_|drawing_audit|drawing_recover|overkill)/.test(n)) cat = "\u6E05\u7406";
16551
- else if (/^(find_text|replace_text|set_text_|justify_|convert_text|explode_text)/.test(n)) cat = "\u6587\u5B57";
16552
- 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";
16553
- else if (/^(list_packages|search_packages|install_package|get_function_usage|list_symbols|import_funlib)/.test(n)) cat = "\u5305\u4E0E\u51FD\u6570";
16554
- else if (/^(pipeline_|watch_|lisp_repl|webhook_|sandbox_|audit_|cad_pool_|external_|list_data|export_to|import_external)/.test(n)) cat = "\u9AD8\u7EA7";
16555
- else if (/^(list_prompts|get_prompt|search_tools|list_tools_by_category)/.test(n)) cat = "\u5143\u5DE5\u5177";
16556
- else if (/^(agent_)/.test(n)) cat = "Agent\u4E0A\u4E0B\u6587";
16557
- if (!categories[cat]) categories[cat] = [];
16558
- categories[cat].push(t.name);
16559
- }
16560
- if (a.category) {
16561
- const names = categories[a.category];
16562
- return { content: [{ type: "text", text: names ? `${a.category} (${names.length}):
16563
- ${names.join("\n")}` : `\u672A\u627E\u5230\u5206\u7C7B: ${a.category}` }] };
16564
- }
16565
- const lines = [];
16566
- for (const [cat, names] of Object.entries(categories)) {
16567
- lines.push(`## ${cat} (${names.length})`);
16568
- }
16569
- lines.push(`
16570
- \u5171 ${Object.keys(categories).length} \u4E2A\u5206\u7C7B`);
16571
- return { content: [{ type: "text", text: lines.join("\n") }] };
16572
- },
17109
+ search_tools,
17110
+ list_tools_by_category,
17111
+ get_tool_info,
16573
17112
  // === Skill Management ===
16574
- skill_search: async (a) => {
16575
- const registryUrl = process.env.SKILL_REGISTRY_URL || "https://raw.githubusercontent.com/atlisp/skill-registry/main/index.json";
16576
- const cachePath = path15.join(os9.homedir(), ".atlisp", "skill-registry-cache.json");
16577
- let cache = null;
16578
- try {
16579
- if (fs11.existsSync(cachePath)) cache = JSON.parse(fs11.readFileSync(cachePath, "utf-8"));
16580
- } catch {
16581
- }
16582
- let index = cache && Date.now() - cache.fetchedAt < 36e5 ? cache : null;
16583
- if (!index) {
16584
- try {
16585
- const resp = await fetch(registryUrl, { headers: { "User-Agent": "atlisp-mcp" } });
16586
- if (resp.ok) {
16587
- index = await resp.json();
16588
- index.fetchedAt = Date.now();
16589
- try {
16590
- fs11.writeFileSync(cachePath, JSON.stringify(index, null, 2), "utf-8");
16591
- } catch {
16592
- }
16593
- }
16594
- } catch {
16595
- }
16596
- }
16597
- if (!index || !index.skills) return { content: [{ type: "text", text: "\u65E0\u6CD5\u83B7\u53D6 skill \u6CE8\u518C\u8868\u7D22\u5F15" }], isError: true };
16598
- const q = (a.query || "").toLowerCase();
16599
- const results = index.skills.filter(
16600
- (s) => s.name.toLowerCase().includes(q) || (s.description || "").toLowerCase().includes(q) || (s.tags || []).some((t) => t.toLowerCase().includes(q))
16601
- );
16602
- if (!results.length) return { content: [{ type: "text", text: `\u672A\u627E\u5230\u5339\u914D "${a.query}" \u7684 skill` }] };
16603
- const lines = [`\u627E\u5230 ${results.length} \u4E2A skill:
16604
- `];
16605
- for (const s of results) {
16606
- lines.push(`**${s.name}**`);
16607
- if (s.description) lines.push(` \u63CF\u8FF0: ${s.description}`);
16608
- if (s.tags?.length) lines.push(` \u6807\u7B7E: ${s.tags.join(", ")}`);
16609
- if (s.version) lines.push(` \u7248\u672C: ${s.version}`);
16610
- lines.push("");
16611
- }
16612
- return { content: [{ type: "text", text: lines.join("\n") }] };
16613
- },
16614
- skill_install: async (a) => {
16615
- if (!a.name) return { content: [{ type: "text", text: "\u9700\u8981 skill \u540D\u79F0" }], isError: true };
16616
- const registryUrl = process.env.SKILL_REGISTRY_URL || "https://raw.githubusercontent.com/atlisp/skill-registry/main/index.json";
16617
- const cachePath = path15.join(os9.homedir(), ".atlisp", "skill-registry-cache.json");
16618
- let cache = null;
16619
- try {
16620
- if (fs11.existsSync(cachePath)) cache = JSON.parse(fs11.readFileSync(cachePath, "utf-8"));
16621
- } catch {
16622
- }
16623
- let index = cache;
16624
- if (!index || Date.now() - index.fetchedAt > 36e5) {
16625
- try {
16626
- const resp = await fetch(registryUrl, { headers: { "User-Agent": "atlisp-mcp" } });
16627
- if (resp.ok) {
16628
- index = await resp.json();
16629
- index.fetchedAt = Date.now();
16630
- try {
16631
- fs11.writeFileSync(cachePath, JSON.stringify(index, null, 2), "utf-8");
16632
- } catch {
16633
- }
16634
- }
16635
- } catch {
16636
- }
16637
- }
16638
- if (!index || !index.skills) return { content: [{ type: "text", text: "\u65E0\u6CD5\u83B7\u53D6 skill \u6CE8\u518C\u8868\u7D22\u5F15" }], isError: true };
16639
- const skill = index.skills.find((s) => s.name === a.name);
16640
- if (!skill) return { content: [{ type: "text", text: `\u672A\u627E\u5230 skill: ${a.name}` }], isError: true };
16641
- const downloadUrl = skill.download_url || skill.url;
16642
- if (!downloadUrl) return { content: [{ type: "text", text: `Skill "${a.name}" \u6CA1\u6709\u4E0B\u8F7D\u5730\u5740` }], isError: true };
16643
- let content;
16644
- try {
16645
- const resp = await fetch(downloadUrl, { headers: { "User-Agent": "atlisp-mcp" } });
16646
- if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
16647
- content = await resp.text();
16648
- } catch (e) {
16649
- return { content: [{ type: "text", text: `\u4E0B\u8F7D\u5931\u8D25: ${e.message}` }], isError: true };
16650
- }
16651
- const skillsDir = path15.join(os9.homedir(), ".atlisp", "skills");
16652
- try {
16653
- fs11.mkdirSync(skillsDir, { recursive: true });
16654
- } catch {
16655
- }
16656
- const safeName = a.name.replace(/[^a-zA-Z0-9\u4e00-\u9fff_-]/g, "-").substring(0, 40);
16657
- const skillFile = path15.join(skillsDir, `skill-${safeName}.md`);
16658
- if (fs11.existsSync(skillFile)) {
16659
- return { content: [{ type: "text", text: `Skill "${a.name}" \u5DF2\u5B89\u88C5 (${skillFile})` }], isError: true };
16660
- }
16661
- let finalContent = content;
16662
- if (!content.match(/^---\n/)) {
16663
- const tags = (skill.tags || []).join(", ");
16664
- finalContent = `---
16665
- name: ${a.name}
16666
- description: ${skill.description || ""}
16667
- tags: ${tags}
16668
- ---
16669
-
16670
- ${content}`;
16671
- }
16672
- fs11.writeFileSync(skillFile, finalContent, "utf-8");
16673
- return { content: [{ type: "text", text: `\u2705 ${skill.name} \u5B89\u88C5\u6210\u529F
16674
- \u6587\u4EF6: ${skillFile}${skill.version ? `
16675
- \u7248\u672C: ${skill.version}` : ""}` }] };
16676
- },
16677
- skill_list_installed: async () => {
16678
- const skillsDir = path15.join(os9.homedir(), ".atlisp", "skills");
16679
- if (!fs11.existsSync(skillsDir)) return { content: [{ type: "text", text: "\u6682\u65E0\u5DF2\u5B89\u88C5\u7684 skill" }] };
16680
- const results = [];
16681
- try {
16682
- const entries = fs11.readdirSync(skillsDir, { withFileTypes: true });
16683
- for (const entry of entries) {
16684
- if (entry.isFile() && entry.name.endsWith(".md")) {
16685
- const fullPath = path15.join(skillsDir, entry.name);
16686
- try {
16687
- const raw = fs11.readFileSync(fullPath, "utf-8");
16688
- const match = raw.match(/^---\n([\s\S]*?)\n---/);
16689
- if (match) {
16690
- const fm = {};
16691
- for (const line of match[1].split("\n")) {
16692
- const sep = line.indexOf(":");
16693
- if (sep > 0) fm[line.slice(0, sep).trim()] = line.slice(sep + 1).trim();
16694
- }
16695
- results.push({ name: fm.name || entry.name.replace(".md", ""), description: fm.description || "", tags: fm.tags || "" });
16696
- } else {
16697
- results.push({ name: entry.name.replace(".md", ""), description: "", tags: "" });
16698
- }
16699
- } catch {
16700
- }
16701
- }
16702
- }
16703
- } catch {
16704
- }
16705
- if (!results.length) return { content: [{ type: "text", text: "\u6682\u65E0\u5DF2\u5B89\u88C5\u7684 skill" }] };
16706
- const lines = [`\u5DF2\u5B89\u88C5 ${results.length} \u4E2A skill:
16707
- `];
16708
- for (const s of results) {
16709
- lines.push(`**${s.name}**${s.description ? ` \u2014 ${s.description}` : ""}`);
16710
- if (s.tags) lines.push(` \u6807\u7B7E: ${s.tags}`);
16711
- }
16712
- return { content: [{ type: "text", text: lines.join("\n") }] };
16713
- },
16714
- skill_uninstall: async (a) => {
16715
- if (!a.name) return { content: [{ type: "text", text: "\u9700\u8981 skill \u540D\u79F0" }], isError: true };
16716
- const skillsDir = path15.join(os9.homedir(), ".atlisp", "skills");
16717
- if (!fs11.existsSync(skillsDir)) return { content: [{ type: "text", text: `\u672A\u5B89\u88C5 skill: ${a.name}` }], isError: true };
16718
- let found = null;
16719
- try {
16720
- const entries = fs11.readdirSync(skillsDir, { withFileTypes: true });
16721
- for (const entry of entries) {
16722
- if (entry.isFile() && entry.name.endsWith(".md")) {
16723
- const fullPath = path15.join(skillsDir, entry.name);
16724
- try {
16725
- const raw = fs11.readFileSync(fullPath, "utf-8");
16726
- const match = raw.match(/^---\n([\s\S]*?)\n---/);
16727
- if (match) {
16728
- for (const line of match[1].split("\n")) {
16729
- if (line.startsWith("name:") && line.slice(5).trim() === a.name) {
16730
- found = fullPath;
16731
- break;
16732
- }
16733
- }
16734
- } else if (entry.name.replace(".md", "") === a.name) {
16735
- found = fullPath;
16736
- }
16737
- } catch {
16738
- }
16739
- if (found) break;
16740
- }
16741
- }
16742
- } catch {
16743
- }
16744
- if (!found) return { content: [{ type: "text", text: `\u672A\u5B89\u88C5 skill: ${a.name}` }], isError: true };
16745
- fs11.unlinkSync(found);
16746
- return { content: [{ type: "text", text: `\u2705 ${a.name} \u5DF2\u5378\u8F7D` }] };
16747
- },
16748
- skill_list_categories: async () => {
16749
- const registryUrl = process.env.SKILL_REGISTRY_URL || "https://raw.githubusercontent.com/atlisp/skill-registry/main/index.json";
16750
- const cachePath = path15.join(os9.homedir(), ".atlisp", "skill-registry-cache.json");
16751
- let cache = null;
16752
- try {
16753
- if (fs11.existsSync(cachePath)) cache = JSON.parse(fs11.readFileSync(cachePath, "utf-8"));
16754
- } catch {
16755
- }
16756
- let index = cache && Date.now() - cache.fetchedAt < 36e5 ? cache : null;
16757
- if (!index) {
16758
- try {
16759
- const resp = await fetch(registryUrl, { headers: { "User-Agent": "atlisp-mcp" } });
16760
- if (resp.ok) {
16761
- index = await resp.json();
16762
- try {
16763
- fs11.writeFileSync(cachePath, JSON.stringify(index, null, 2), "utf-8");
16764
- } catch {
16765
- }
16766
- }
16767
- } catch {
16768
- }
16769
- }
16770
- if (!index || !index.skills) return { content: [{ type: "text", text: "\u65E0\u6CD5\u83B7\u53D6 skill \u6CE8\u518C\u8868\u7D22\u5F15" }], isError: true };
16771
- const cats = /* @__PURE__ */ new Set();
16772
- for (const s of index.skills) for (const t of s.tags || []) cats.add(t);
16773
- const sorted = Array.from(cats).sort();
16774
- return { content: [{ type: "text", text: sorted.length ? `\u6CE8\u518C\u8868\u5206\u7C7B (${sorted.length}):
16775
- ${sorted.join("\n")}` : "\u6682\u65E0\u5206\u7C7B" }] };
16776
- }
17113
+ skill_search,
17114
+ skill_install,
17115
+ skill_list_installed,
17116
+ skill_uninstall,
17117
+ skill_list_categories
16777
17118
  };
16778
17119
  }
16779
17120
  });
@@ -16864,7 +17205,7 @@ var init_mcp_server_state = __esm({
16864
17205
  progressContext = new AsyncLocalStorage();
16865
17206
  sessionServers = /* @__PURE__ */ new Map();
16866
17207
  sessionTransports = /* @__PURE__ */ new Map();
16867
- mcpHandlers = { tools: tools42, handleToolCall };
17208
+ mcpHandlers = { tools: tools43, handleToolCall };
16868
17209
  _stdioServer = null;
16869
17210
  _pendingResourceUpdated = /* @__PURE__ */ new Set();
16870
17211
  _resourceUpdatedTimer = null;
@@ -16888,11 +17229,12 @@ function mcpError(text) {
16888
17229
  return { content: [{ type: "text", text: String(text) }], isError: true };
16889
17230
  }
16890
17231
  async function ensureCadConnected() {
16891
- const { cad: cad2 } = await Promise.resolve().then(() => (init_cad(), cad_exports));
16892
- if (!cad2.connected) {
16893
- await cad2.connect();
17232
+ const { cad: cad2, resolveCadConnection: resolveCadConnection2 } = await Promise.resolve().then(() => (init_cad(), cad_exports));
17233
+ const conn = resolveCadConnection2();
17234
+ if (!conn.connected) {
17235
+ await conn.connect();
16894
17236
  }
16895
- if (!cad2.connected) {
17237
+ if (!conn.connected) {
16896
17238
  throw createError(ERROR_CODES.CAD_NOT_CONNECTED);
16897
17239
  }
16898
17240
  }
@@ -17077,7 +17419,7 @@ __export(resource_handlers_exports, {
17077
17419
  readResource: () => readResource
17078
17420
  });
17079
17421
  import fs13 from "fs";
17080
- import path16 from "path";
17422
+ import path17 from "path";
17081
17423
  function propertyPairsToObject(pairs) {
17082
17424
  if (!Array.isArray(pairs)) return null;
17083
17425
  const obj = {};
@@ -17849,7 +18191,7 @@ async function getDwgList() {
17849
18191
  }
17850
18192
  }
17851
18193
  function loadStandardsFile(filename) {
17852
- const filePath = path16.join(STANDARDS_DIR, filename);
18194
+ const filePath = path17.join(STANDARDS_DIR, filename);
17853
18195
  try {
17854
18196
  const raw = fs13.readFileSync(filePath, "utf-8");
17855
18197
  return JSON.parse(raw);
@@ -17949,6 +18291,119 @@ async function getDwgContext() {
17949
18291
  };
17950
18292
  }
17951
18293
  }
18294
+ async function getDrawingAnalysis(params = {}) {
18295
+ const snapshot = await getCadSnapshot();
18296
+ if (!snapshot || !snapshot.cadConnected) {
18297
+ return { connected: false, error: "CAD \u672A\u8FDE\u63A5" };
18298
+ }
18299
+ try {
18300
+ const detailedCode = `(progn
18301
+ (vl-load-com)
18302
+ (setq r nil)
18303
+ ;; entity counts by type
18304
+ (if (setq ss (ssget "_X"))
18305
+ (progn
18306
+ (setq total (sslength ss) i 0 cnt nil)
18307
+ (while (< i total)
18308
+ (setq typ (cdr (assoc 0 (entget (ssname ss i)))))
18309
+ (setq cnt (if (assoc typ cnt) (subst (cons typ (1+ (cdr (assoc typ cnt)))) (assoc typ cnt) cnt) (cons (cons typ 1) cnt)))
18310
+ (setq i (1+ i)))
18311
+ (setq r (cons (cons "entityCount" total) r))
18312
+ (setq r (cons (cons "entityByType" cnt) r)))
18313
+ (setq r (cons (cons "entityCount" 0) r)))
18314
+ ;; block usage
18315
+ (setq blocks nil)
18316
+ (vlax-for b (vla-get-blocks (vla-get-activedocument (vlax-get-acad-object)))
18317
+ (if (and (= (vla-get-IsXref b) :vlax-false) (/= (vla-get-Name b) "*Model_Space") (/= (vla-get-Name b) "*Paper_Space"))
18318
+ (setq blocks (cons (list "name" (vla-get-Name b) "count" (vla-get-Count b) "isDynamic" (vla-get-IsDynamicBlock b)) blocks))))
18319
+ (setq r (cons (cons "blockUsage" blocks) r))
18320
+ ;; layer summary
18321
+ (setq layers nil le (tblnext "layer" t))
18322
+ (while le
18323
+ (setq ln (cdr (assoc 2 le)) lc (cdr (assoc 62 le)) ll (cdr (assoc 6 le)))
18324
+ (setq layers (cons (list "name" ln "color" (if (< lc 0) (- lc) lc) "off" (< lc 0) "linetype" ll) layers))
18325
+ (setq le (tblnext "layer" nil)))
18326
+ (setq r (cons (cons "layers" layers) r))
18327
+ ;; text stats
18328
+ (if (setq ts (ssget "_X" '((0 . "TEXT,MTEXT"))))
18329
+ (progn
18330
+ (setq tc (sslength ts) th nil ti 0)
18331
+ (while (< ti tc)
18332
+ (setq h (cdr (assoc 40 (entget (ssname ts ti)))))
18333
+ (if h (setq th (cons h th)))
18334
+ (setq ti (1+ ti)))
18335
+ (setq th (vl-sort th '<))
18336
+ (setq r (cons (cons "textCount" tc) r))
18337
+ (setq r (cons (cons "textHeights" (list "min" (car th) "max" (last th) "median" (nth (fix (/ (length th) 2)) th))) r)))
18338
+ (setq r (cons (cons "textCount" 0) r)))
18339
+ ;; system variables
18340
+ (setq r (cons (cons "sysvars" (list
18341
+ (cons "ltscale" (getvar "ltscale"))
18342
+ (cons "dimscale" (getvar "dimscale"))
18343
+ (cons "insunits" (getvar "insunits"))
18344
+ (cons "measurement" (getvar "measurement"))
18345
+ (cons "celtype" (getvar "celtype"))
18346
+ (cons "clayer" (getvar "clayer"))
18347
+ (cons "cecolor" (getvar "cecolor"))
18348
+ (cons "osmode" (getvar "osmode"))
18349
+ (cons "snapmode" (getvar "snapmode"))
18350
+ (cons "gridmode" (getvar "gridmode"))
18351
+ (cons "orthomode" (getvar "orthomode"))
18352
+ (cons "plinetype" (getvar "plinetype"))
18353
+ (cons "fillmode" (getvar "fillmode"))
18354
+ (cons "pickstyle" (getvar "pickstyle"))
18355
+ )) r))
18356
+ (vl-prin1-to-string (reverse r)))`;
18357
+ const resultStr = await cad.sendCommandWithResult(detailedCode);
18358
+ const parsed = parseLispRecursive(resultStr);
18359
+ if (!parsed || !Array.isArray(parsed)) {
18360
+ return { connected: true, error: "\u5206\u6790\u7ED3\u679C\u89E3\u6790\u5931\u8D25" };
18361
+ }
18362
+ const analysis = {};
18363
+ for (const pair of parsed) {
18364
+ if (Array.isArray(pair) && pair.length >= 2) {
18365
+ analysis[String(pair[0])] = pair[1];
18366
+ }
18367
+ }
18368
+ return { connected: true, ...analysis };
18369
+ } catch (e) {
18370
+ log(`getDrawingAnalysis error: ${e.message}`);
18371
+ return { connected: true, error: e.message };
18372
+ }
18373
+ }
18374
+ function getToolCategories() {
18375
+ const cats = {};
18376
+ for (const t of tools43) {
18377
+ const cat = t.annotations?.category || "\u5176\u4ED6";
18378
+ if (!cats[cat]) cats[cat] = { count: 0, tools: [] };
18379
+ cats[cat].count++;
18380
+ cats[cat].tools.push(t.name);
18381
+ }
18382
+ return Object.entries(cats).map(([category, info]) => ({
18383
+ category,
18384
+ count: info.count,
18385
+ tools: info.tools
18386
+ })).sort((a, b) => b.count - a.count);
18387
+ }
18388
+ async function checkDrawingStandards() {
18389
+ const standards = getDraftingStandards();
18390
+ if (!standards || standards.error) {
18391
+ return { ok: false, error: standards?.error || "\u5236\u56FE\u89C4\u8303\u672A\u52A0\u8F7D" };
18392
+ }
18393
+ try {
18394
+ const issues = [];
18395
+ for (const rule of standards.layers || []) {
18396
+ const code = `(tblsearch "LAYER" "${escapeLispString(rule.name || rule.pattern)}")`;
18397
+ const exists = await cad.sendCommandWithResult(code);
18398
+ if (rule.required && (!exists || exists === "nil" || exists === "")) {
18399
+ issues.push({ type: "missing_layer", message: `\u7F3A\u5C11\u5FC5\u9700\u56FE\u5C42: ${rule.name}` });
18400
+ }
18401
+ }
18402
+ return { ok: issues.length === 0, issues };
18403
+ } catch (e) {
18404
+ return { ok: false, error: e.message };
18405
+ }
18406
+ }
17952
18407
  function getToolRelationships() {
17953
18408
  return {
17954
18409
  selection: {
@@ -18204,6 +18659,12 @@ async function readResource(uri) {
18204
18659
  return await getDwgContext();
18205
18660
  case "atlisp://tools/relationships":
18206
18661
  return getToolRelationships();
18662
+ case "atlisp://dwg/analysis":
18663
+ return await getDrawingAnalysis(params);
18664
+ case "atlisp://tools/categories":
18665
+ return getToolCategories();
18666
+ case "atlisp://dwg/standards/check":
18667
+ return await checkDrawingStandards();
18207
18668
  default:
18208
18669
  throw new Error(`\u8D44\u6E90\u4E0D\u5B58\u5728: ${uri}`);
18209
18670
  }
@@ -18219,6 +18680,7 @@ var init_resource_handlers = __esm({
18219
18680
  init_handler_utils();
18220
18681
  init_errors();
18221
18682
  init_config();
18683
+ init_tools();
18222
18684
  ENTITY_PROPERTIES_LISP = `
18223
18685
  (defun entity:to-list (val)
18224
18686
  (cond
@@ -18298,11 +18760,11 @@ init_subscription_manager();
18298
18760
  init_subscription_manager();
18299
18761
  init_sse_session();
18300
18762
  init_config();
18301
- import path19 from "path";
18763
+ import path20 from "path";
18302
18764
  import fs16 from "fs";
18303
18765
  import http from "http";
18304
18766
  import https from "https";
18305
- import { fileURLToPath as fileURLToPath7 } from "url";
18767
+ import { fileURLToPath as fileURLToPath8 } from "url";
18306
18768
  import { createRequire as createRequire2 } from "module";
18307
18769
  import express2 from "express";
18308
18770
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -18315,12 +18777,13 @@ init_cad_pool();
18315
18777
  init_subscription_manager();
18316
18778
  init_mcp_server_state();
18317
18779
  init_sse_session();
18780
+ import os10 from "os";
18318
18781
  import express from "express";
18319
18782
  import { randomUUID as randomUUID6, createHash } from "crypto";
18320
18783
  import fs14 from "fs";
18321
- import path17 from "path";
18322
- import { fileURLToPath as fileURLToPath6 } from "url";
18323
- import iconv from "iconv-lite";
18784
+ import path18 from "path";
18785
+ import { fileURLToPath as fileURLToPath7 } from "url";
18786
+ import iconv2 from "iconv-lite";
18324
18787
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
18325
18788
 
18326
18789
  // src/cad-event-bus.js
@@ -18432,7 +18895,6 @@ var TOOL_PERMISSIONS = {
18432
18895
  "measure_*": "read",
18433
18896
  "stream_*": "read",
18434
18897
  "watch_list": "read",
18435
- "pipeline_list": "read",
18436
18898
  // Write tools
18437
18899
  "create_*": "write",
18438
18900
  "set_*": "write",
@@ -18452,8 +18914,6 @@ var TOOL_PERMISSIONS = {
18452
18914
  "import_*": "import",
18453
18915
  // Admin
18454
18916
  "config_*": "admin",
18455
- "pipeline_save": "admin",
18456
- "pipeline_delete": "admin",
18457
18917
  "webhook_*": "admin",
18458
18918
  "watch_remove": "admin"
18459
18919
  };
@@ -18487,11 +18947,11 @@ function getApiKeyRole(apiKey2, config2) {
18487
18947
 
18488
18948
  // src/routes.js
18489
18949
  init_constants();
18490
- var __dirname6 = path17.dirname(fileURLToPath6(import.meta.url));
18491
- var htmlDir = path17.join(__dirname6, "html");
18950
+ var __dirname6 = path18.dirname(fileURLToPath7(import.meta.url));
18951
+ var htmlDir = path18.join(__dirname6, "html");
18492
18952
  function loadHtml(name, replacements = {}) {
18493
18953
  if (!htmlCache[name]) {
18494
- htmlCache[name] = fs14.readFileSync(path17.join(htmlDir, `${name}.html`), "utf-8");
18954
+ htmlCache[name] = fs14.readFileSync(path18.join(htmlDir, `${name}.html`), "utf-8");
18495
18955
  }
18496
18956
  let html = htmlCache[name];
18497
18957
  for (const [key, value] of Object.entries(replacements)) {
@@ -18504,7 +18964,7 @@ var cachedToolsJson = null;
18504
18964
  var cachedToolsJsonAt = 0;
18505
18965
  function getToolsJson() {
18506
18966
  if (cachedToolsJson && Date.now() - cachedToolsJsonAt < 3e4) return cachedToolsJson;
18507
- cachedToolsJson = JSON.stringify(tools42.map((t) => ({
18967
+ cachedToolsJson = JSON.stringify(tools43.map((t) => ({
18508
18968
  name: t.name,
18509
18969
  description: t.description,
18510
18970
  inputSchema: t.inputSchema
@@ -18534,7 +18994,7 @@ function createJsonBodyParser() {
18534
18994
  let charset = m ? m[1].toLowerCase() : "utf-8";
18535
18995
  const charsetMap = { "gb2312": "gbk", "gb18030": "gbk", "gbk": "gbk", "936": "gbk" };
18536
18996
  charset = charsetMap[charset] || charset;
18537
- const str = iconv.decode(buf, charset);
18997
+ const str = iconv2.decode(buf, charset);
18538
18998
  req.body = JSON.parse(str);
18539
18999
  next();
18540
19000
  } catch (e) {
@@ -18574,15 +19034,45 @@ function createAuthMiddleware() {
18574
19034
  return res.status(401).json({ error: "Unauthorized" });
18575
19035
  };
18576
19036
  }
19037
+ function originMatches(pattern, origin) {
19038
+ if (pattern === "*") return true;
19039
+ if (!origin) return false;
19040
+ const getHost = (s) => s.replace(/^https?:\/\//, "").replace(/:\d+$/, "").toLowerCase();
19041
+ const oHost = getHost(origin);
19042
+ let pHost = getHost(pattern);
19043
+ if (pHost.startsWith("*.")) {
19044
+ const suffix = pHost.slice(1);
19045
+ return oHost === suffix.slice(1) || oHost.endsWith(suffix);
19046
+ }
19047
+ return oHost === pHost;
19048
+ }
18577
19049
  function createCorsMiddleware() {
19050
+ const allowedOrigins = corsOrigin.split(",").map((s) => s.trim()).filter(Boolean);
19051
+ const allowAll = allowedOrigins.includes("*");
18578
19052
  return (req, res, next) => {
18579
19053
  if (!enableCors) return next();
18580
- res.setHeader("Access-Control-Allow-Origin", corsOrigin);
19054
+ const origin = req.headers.origin;
19055
+ const isPnaPreflight = req.method === "OPTIONS" && req.headers["access-control-request-private-network"] === "true";
19056
+ if (allowAll) {
19057
+ if (isPnaPreflight && origin) {
19058
+ res.setHeader("Access-Control-Allow-Origin", origin);
19059
+ res.setHeader("Vary", "Origin");
19060
+ } else {
19061
+ res.setHeader("Access-Control-Allow-Origin", corsOrigin);
19062
+ }
19063
+ } else if (origin && allowedOrigins.some((p) => originMatches(p, origin))) {
19064
+ res.setHeader("Access-Control-Allow-Origin", origin);
19065
+ res.setHeader("Vary", "Origin");
19066
+ } else if (!origin) {
19067
+ res.setHeader("Access-Control-Allow-Origin", allowedOrigins[0] || "*");
19068
+ } else {
19069
+ return res.status(403).json({ error: "Origin not allowed" });
19070
+ }
18581
19071
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS, CONNECT");
18582
19072
  res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Accept, mcp-session-id, last-event-id");
18583
19073
  res.setHeader("Access-Control-Expose-Headers", "mcp-session-id, content-type, x-accel-buffering");
18584
19074
  res.setHeader("Access-Control-Max-Age", "86400");
18585
- res.setHeader("Vary", "Accept");
19075
+ res.setHeader("Access-Control-Allow-Private-Network", "true");
18586
19076
  if (req.method === "OPTIONS") return res.status(204).end();
18587
19077
  next();
18588
19078
  };
@@ -18673,7 +19163,7 @@ function setupRoutes(app) {
18673
19163
  },
18674
19164
  mcp: {
18675
19165
  sessions: sessionServers.size,
18676
- tools: tools42.length,
19166
+ tools: tools43.length,
18677
19167
  sseSessions: sseCount
18678
19168
  },
18679
19169
  pool: {
@@ -18683,6 +19173,11 @@ function setupRoutes(app) {
18683
19173
  }
18684
19174
  });
18685
19175
  });
19176
+ app.get("/go", (req, res) => {
19177
+ const remoteUrl = "https://atlisp.cn/agent";
19178
+ const cid = config_default.clientId || os10.hostname();
19179
+ res.redirect(302, `${remoteUrl}?clientId=${encodeURIComponent(cid)}`);
19180
+ });
18686
19181
  app.get("/health/full", async (req, res) => {
18687
19182
  const memUsage = process.memoryUsage();
18688
19183
  const poolStats = pool ? pool.getStats() : {};
@@ -18696,7 +19191,7 @@ function setupRoutes(app) {
18696
19191
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
18697
19192
  cad: { connected: cad.connected, platform: cad.getPlatform?.(), version: cad.getVersion?.(), commandQueueSize: cad._getQueueSize ? cad._getQueueSize() : 0, restartCount: cad._getRestartCount ? cad._getRestartCount() : 0 },
18698
19193
  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() },
18699
- mcp: { sessions: sessionServers.size, tools: tools42.length, sseSessions: sseCount },
19194
+ mcp: { sessions: sessionServers.size, tools: tools43.length, sseSessions: sseCount },
18700
19195
  pool: poolStats
18701
19196
  });
18702
19197
  });
@@ -18734,7 +19229,7 @@ function setupRoutes(app) {
18734
19229
  app.get("/api/docs", (req, res) => {
18735
19230
  const html = loadHtml("api-docs", {
18736
19231
  VERSION: SERVER_VERSION,
18737
- TOOLS_COUNT: tools42.length
19232
+ TOOLS_COUNT: tools43.length
18738
19233
  });
18739
19234
  res.setHeader("Content-Type", "text/html; charset=utf-8");
18740
19235
  res.send(html);
@@ -18755,7 +19250,7 @@ function setupRoutes(app) {
18755
19250
  SESSIONS_COUNT: sessionServers.size,
18756
19251
  CAD_STATUS_CLASS: cadConnected ? "status-ok" : "status-error",
18757
19252
  CAD_STATUS_TEXT: cadStatusText,
18758
- TOOLS_COUNT: tools42.length
19253
+ TOOLS_COUNT: tools43.length
18759
19254
  });
18760
19255
  res.setHeader("Content-Type", "text/html; charset=utf-8");
18761
19256
  res.send(html);
@@ -18947,7 +19442,7 @@ function sendWelcome(ws, clientId) {
18947
19442
  server: { name: SERVER_NAME, version: SERVER_VERSION },
18948
19443
  capabilities: SERVER_CAPABILITIES,
18949
19444
  clientId,
18950
- toolCount: tools42.length,
19445
+ toolCount: tools43.length,
18951
19446
  cadConnected: cad.connected,
18952
19447
  cadPlatform: cad.connected ? cad.getPlatform() : null,
18953
19448
  cadVersion: cad.connected ? cad.getVersion() : null
@@ -19199,9 +19694,9 @@ function createWebSocketServer(httpServer) {
19199
19694
  // src/plugin-loader.js
19200
19695
  init_logger();
19201
19696
  import fs15 from "fs";
19202
- import path18 from "path";
19203
- import os10 from "os";
19204
- var PLUGIN_DIR = path18.join(os10.homedir(), ".atlisp", "mcp-plugins");
19697
+ import path19 from "path";
19698
+ import os11 from "os";
19699
+ var PLUGIN_DIR = path19.join(os11.homedir(), ".atlisp", "mcp-plugins");
19205
19700
  var loadedPlugins = [];
19206
19701
  var watchCallbacks = [];
19207
19702
  var watcher = null;
@@ -19226,7 +19721,7 @@ function clearModuleCache(pluginPath) {
19226
19721
  if (key) delete import.meta[key];
19227
19722
  }
19228
19723
  async function loadSinglePlugin(file) {
19229
- const pluginPath = path18.join(PLUGIN_DIR, file);
19724
+ const pluginPath = path19.join(PLUGIN_DIR, file);
19230
19725
  clearModuleCache(pluginPath);
19231
19726
  try {
19232
19727
  const plugin = await import(`${pluginPath}?t=${Date.now()}`);
@@ -19276,7 +19771,7 @@ function startPluginWatcher() {
19276
19771
  if (!filename || !filename.endsWith(".js") || filename.startsWith("_")) return;
19277
19772
  const existingIndex = loadedPlugins.findIndex((p) => p.name === filename.replace(".js", "") || p._file === filename);
19278
19773
  if (eventType === "rename") {
19279
- const filePath = path18.join(PLUGIN_DIR, filename);
19774
+ const filePath = path19.join(PLUGIN_DIR, filename);
19280
19775
  if (!fs15.existsSync(filePath)) {
19281
19776
  if (existingIndex !== -1) {
19282
19777
  loadedPlugins.splice(existingIndex, 1);
@@ -19316,6 +19811,91 @@ function stopPluginWatcher() {
19316
19811
  init_tool_registry();
19317
19812
  init_mcp_server_state();
19318
19813
 
19814
+ // src/health-reporter.js
19815
+ init_config();
19816
+ init_cad();
19817
+ init_cad_pool();
19818
+ init_mcp_server_state();
19819
+ init_subscription_manager();
19820
+ init_logger();
19821
+ import os12 from "os";
19822
+ var REPORT_URL = "https://atlisp.cn/api/mcp/health";
19823
+ var REPORT_INTERVAL = 3e4;
19824
+ var intervalHandle = null;
19825
+ function gatherHealth() {
19826
+ let cadResponseTime = 0;
19827
+ if (cad.connected) {
19828
+ const t0 = Date.now();
19829
+ try {
19830
+ cad.ping();
19831
+ } catch {
19832
+ }
19833
+ cadResponseTime = Date.now() - t0;
19834
+ }
19835
+ const memUsage = process.memoryUsage();
19836
+ let sseCount = 0;
19837
+ try {
19838
+ sseCount = sessionManager?.count?.() || 0;
19839
+ } catch {
19840
+ }
19841
+ return {
19842
+ clientId: config_default.clientId || os12.hostname(),
19843
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
19844
+ status: "ok",
19845
+ cad: {
19846
+ connected: cad.connected,
19847
+ platform: cad.getPlatform ? cad.getPlatform() : null,
19848
+ version: cad.getVersion ? cad.getVersion() : null,
19849
+ responseTime: cadResponseTime,
19850
+ commandQueueSize: cad._getQueueSize ? cad._getQueueSize() : 0,
19851
+ restartCount: cad._getRestartCount ? cad._getRestartCount() : 0
19852
+ },
19853
+ system: {
19854
+ uptime: process.uptime(),
19855
+ memory: {
19856
+ rss: Math.round(memUsage.rss / 1024 / 1024),
19857
+ heapUsed: Math.round(memUsage.heapUsed / 1024 / 1024),
19858
+ heapTotal: Math.round(memUsage.heapTotal / 1024 / 1024)
19859
+ }
19860
+ },
19861
+ mcp: {
19862
+ sessions: sessionServers.size,
19863
+ sseSessions: sseCount
19864
+ },
19865
+ pool: pool ? {
19866
+ total: pool.getStats().total,
19867
+ active: pool.getStats().active,
19868
+ instances: pool.getStats().instances
19869
+ } : null
19870
+ };
19871
+ }
19872
+ async function report() {
19873
+ try {
19874
+ const data = gatherHealth();
19875
+ const res = await fetch(REPORT_URL, {
19876
+ method: "POST",
19877
+ headers: { "Content-Type": "application/json" },
19878
+ body: JSON.stringify(data),
19879
+ signal: AbortSignal.timeout(1e4)
19880
+ });
19881
+ if (!res.ok) {
19882
+ error(`Health report failed: ${res.status}`);
19883
+ }
19884
+ } catch (e) {
19885
+ error(`Health report error: ${e.message}`);
19886
+ }
19887
+ }
19888
+ function startHealthReporter() {
19889
+ report();
19890
+ intervalHandle = setInterval(report, REPORT_INTERVAL);
19891
+ }
19892
+ function stopHealthReporter() {
19893
+ if (intervalHandle) {
19894
+ clearInterval(intervalHandle);
19895
+ intervalHandle = null;
19896
+ }
19897
+ }
19898
+
19319
19899
  // src/streamable-http.js
19320
19900
  init_logger();
19321
19901
  init_mcp_handlers();
@@ -19326,7 +19906,7 @@ var mcpServer = null;
19326
19906
  var streamableTransport = null;
19327
19907
  async function initStreamableHttpServer() {
19328
19908
  if (mcpServer) return;
19329
- mcpServer = createMcpServer({ tools: tools42, handleToolCall });
19909
+ mcpServer = createMcpServer({ tools: tools43, handleToolCall });
19330
19910
  streamableTransport = new StreamableHTTPServerTransport2({
19331
19911
  sessionIdGenerator: () => randomUUID8()
19332
19912
  });
@@ -19377,9 +19957,9 @@ function setupStreamableHttpRoutes(app) {
19377
19957
  init_mcp_tools();
19378
19958
  init_function_handlers();
19379
19959
  init_constants();
19380
- var __dirname7 = path19.dirname(fileURLToPath7(import.meta.url));
19960
+ var __dirname7 = path20.dirname(fileURLToPath8(import.meta.url));
19381
19961
  var require3 = createRequire2(import.meta.url);
19382
- var { version: version2 } = require3(path19.join(__dirname7, "..", "package.json"));
19962
+ var { version: version2 } = require3(path20.join(__dirname7, "..", "package.json"));
19383
19963
  var isMcpScript = process.argv[1]?.includes("atlisp-mcp");
19384
19964
  if (isMcpScript) {
19385
19965
  const cliArgs2 = process.argv.slice(2);
@@ -19413,6 +19993,7 @@ Options:
19413
19993
  --no-lint Disable lint pre-check for eval_lisp
19414
19994
  --lint-off Same as --no-lint
19415
19995
  --api-key <key> API key for authentication
19996
+ --client-id <id> Client identifier for remote health report
19416
19997
  --security-level <type> Security level: strict, standard, relaxed (default: strict)
19417
19998
 
19418
19999
  Environment Variables:
@@ -19433,7 +20014,8 @@ Environment Variables:
19433
20014
  LINT_LEVEL Lint level (error/warn/off)
19434
20015
  NO_LINT Disable lint (true/false)
19435
20016
  REQUEST_LOG_ENABLED Enable request logging (true/false, default: true)
19436
- CORS_ORIGIN CORS origin (default: *)
20017
+ CORS_ORIGIN CORS origin (default: https://atlisp.cn)
20018
+ CLIENT_ID Client identifier for remote health report
19437
20019
  RATE_LIMIT_WINDOW Rate limit window in ms (default: 60000)
19438
20020
  RATE_LIMIT_MAX Max requests per window (default: 100)
19439
20021
 
@@ -19489,6 +20071,8 @@ For more info: https://atlisp.cn
19489
20071
  process.env.SSL_CERT_PATH = cliArgs2[++i];
19490
20072
  } else if (arg === "--api-key" && cliArgs2[i + 1]) {
19491
20073
  process.env.MCP_API_KEY = cliArgs2[++i];
20074
+ } else if (arg === "--client-id" && cliArgs2[i + 1]) {
20075
+ process.env.CLIENT_ID = cliArgs2[++i];
19492
20076
  } else if (arg === "--metrics-enabled" && cliArgs2[i + 1]) {
19493
20077
  process.env.METRICS_ENABLED = cliArgs2[++i];
19494
20078
  }
@@ -19511,7 +20095,7 @@ async function initCadConnection() {
19511
20095
  }
19512
20096
  async function startServer() {
19513
20097
  await loadPlugins();
19514
- initPlugins({ cad, config: config_default, log, registerTool, tools: tools42 });
20098
+ initPlugins({ cad, config: config_default, log, registerTool, tools: tools43 });
19515
20099
  onPluginChange((type, name) => {
19516
20100
  log(`Plugin changed: ${type} ${name}`);
19517
20101
  broadcastToolListChanged();
@@ -19521,7 +20105,7 @@ async function startServer() {
19521
20105
  startPluginWatcher();
19522
20106
  let promptsWatcher = null;
19523
20107
  try {
19524
- const promptsDir = path19.join(__dirname7, "..", "prompts");
20108
+ const promptsDir = path20.join(__dirname7, "..", "prompts");
19525
20109
  if (fs16.existsSync(promptsDir)) {
19526
20110
  promptsWatcher = fs16.watch(promptsDir, (eventType, filename) => {
19527
20111
  if (filename && filename.endsWith(".json")) {
@@ -19535,7 +20119,7 @@ async function startServer() {
19535
20119
  }
19536
20120
  if (config_default.transport === "stdio") {
19537
20121
  await initCadConnection();
19538
- const mcpServer2 = createStdioServer({ tools: tools42, handleToolCall });
20122
+ const mcpServer2 = createStdioServer({ tools: tools43, handleToolCall });
19539
20123
  const transport = new StdioServerTransport();
19540
20124
  const sessionId = "stdio-session";
19541
20125
  sessionManager.createSession(sessionId);
@@ -19583,10 +20167,12 @@ async function startServer() {
19583
20167
  const proto = useSsl ? "https" : "http";
19584
20168
  const mode = config_default.transport === "ws" ? "WebSocket" : config_default.transport === "streamable-http" ? "Streamable HTTP" : "\u591A\u4F1A\u8BDD";
19585
20169
  console.error(`@lisp MCP Server \u542F\u52A8 - ${proto}://${config_default.host}:${config_default.port} (${mode}\u6A21\u5F0F)`);
20170
+ startHealthReporter();
19586
20171
  });
19587
20172
  async function shutdown(signal) {
19588
20173
  console.error(`
19589
20174
  \u6536\u5230 ${signal}\uFF0C\u6B63\u5728\u4F18\u96C5\u5173\u95ED...`);
20175
+ stopHealthReporter();
19590
20176
  stopPluginWatcher();
19591
20177
  if (promptsWatcher) {
19592
20178
  promptsWatcher.close();
@@ -19636,5 +20222,5 @@ export {
19636
20222
  handleToolCall,
19637
20223
  loadAtlibFunctionLib,
19638
20224
  startServer,
19639
- tools42 as tools
20225
+ tools43 as tools
19640
20226
  };