@atlisp/mcp 1.8.23 → 1.8.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,10 +7,10 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
7
7
  if (typeof require !== "undefined") return require.apply(this, arguments);
8
8
  throw Error('Dynamic require of "' + x + '" is not supported');
9
9
  });
10
- var __glob = (map) => (path20) => {
11
- var fn = map[path20];
10
+ var __glob = (map) => (path21) => {
11
+ var fn = map[path21];
12
12
  if (fn) return fn();
13
- throw new Error("Module not found in bundle: " + path20);
13
+ throw new Error("Module not found in bundle: " + path21);
14
14
  };
15
15
  var __esm = (fn, res) => function __init() {
16
16
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
@@ -37,7 +37,7 @@ function parseArgs() {
37
37
  } else if (args[i] === "--ws") {
38
38
  result.transport = "ws";
39
39
  } else if (args[i] === "--port" && args[i + 1]) {
40
- result.port = args[i + 1];
40
+ result.port = parseInt(args[i + 1], 10);
41
41
  i++;
42
42
  } else if (args[i] === "--host" && args[i + 1]) {
43
43
  result.host = args[i + 1];
@@ -111,41 +111,42 @@ var envSchema, configSchema, cliArgs, envResult, env, configFilePath, fileConfig
111
111
  var init_config = __esm({
112
112
  "src/config.js"() {
113
113
  envSchema = z.object({
114
- PORT: z.string().optional().default("8110"),
114
+ PORT: z.coerce.number().optional().default(8110),
115
115
  HOST: z.string().optional().default("0.0.0.0"),
116
116
  TRANSPORT: z.enum(["http", "stdio", "ws"]).optional().default("http"),
117
117
  MCP_API_KEY: z.string().optional().default(""),
118
- ENABLE_SSE: z.string().optional().default("true"),
119
- MESSAGE_TIMEOUT: z.string().optional().default("60000"),
120
- HEARTBEAT_INTERVAL: z.string().optional().default("30000"),
121
- BUSY_RETRIES: z.string().optional().default("10"),
122
- BUSY_DELAY: z.string().optional().default("500"),
123
- ENABLE_CORS: z.string().optional().default("true"),
124
- CORS_ORIGIN: z.string().optional().default("*"),
125
- RATE_LIMIT_WINDOW: z.string().optional().default("60000"),
126
- RATE_LIMIT_MAX: z.string().optional().default("100"),
127
- RESOURCE_CACHE_TTL: z.string().optional().default("5000"),
128
- FUNCTION_LIB_CACHE_TTL: z.string().optional().default("300000"),
129
- DEBUG: z.string().optional().default("false"),
118
+ ENABLE_SSE: z.coerce.boolean().optional().default(true),
119
+ MESSAGE_TIMEOUT: z.coerce.number().optional().default(6e4),
120
+ HEARTBEAT_INTERVAL: z.coerce.number().optional().default(3e4),
121
+ BUSY_RETRIES: z.coerce.number().optional().default(10),
122
+ BUSY_DELAY: z.coerce.number().optional().default(500),
123
+ ENABLE_CORS: z.coerce.boolean().optional().default(true),
124
+ CORS_ORIGIN: z.string().optional().default("https://atlisp.cn"),
125
+ RATE_LIMIT_WINDOW: z.coerce.number().optional().default(6e4),
126
+ RATE_LIMIT_MAX: z.coerce.number().optional().default(100),
127
+ RESOURCE_CACHE_TTL: z.coerce.number().optional().default(5e3),
128
+ FUNCTION_LIB_CACHE_TTL: z.coerce.number().optional().default(3e5),
129
+ DEBUG: z.coerce.boolean().optional().default(false),
130
130
  DEBUG_FILE: z.string().optional().default(""),
131
131
  FUNCTION_LIB_URL: z.string().optional().default("http://s3.atlisp.cn/json/functions.json"),
132
132
  CONFIG_FILE: z.string().optional().default(""),
133
- API_KEY_RATE_LIMIT: z.string().optional().default("1000"),
134
- METRICS_ENABLED: z.string().optional().default("true"),
135
- REQUEST_LOG_ENABLED: z.string().optional().default("true"),
133
+ API_KEY_RATE_LIMIT: z.coerce.number().optional().default(1e3),
134
+ METRICS_ENABLED: z.coerce.boolean().optional().default(true),
135
+ REQUEST_LOG_ENABLED: z.coerce.boolean().optional().default(true),
136
136
  SSL_KEY_PATH: z.string().optional().default(""),
137
137
  SSL_CERT_PATH: z.string().optional().default(""),
138
138
  LLM_API_KEY: z.string().optional().default(""),
139
139
  LLM_API_URL: z.string().optional().default("https://api.openai.com/v1/chat/completions"),
140
140
  LLM_MODEL: z.string().optional().default("gpt-4o-mini"),
141
- LLM_MAX_TOKENS: z.string().optional().default("4096"),
142
- LLM_TEMPERATURE: z.string().optional().default("0.7"),
143
- WORKER_POOL_SIZE: z.string().optional().default("2"),
141
+ LLM_MAX_TOKENS: z.coerce.number().optional().default(4096),
142
+ LLM_TEMPERATURE: z.coerce.number().optional().default(0.7),
143
+ WORKER_POOL_SIZE: z.coerce.number().optional().default(2),
144
144
  LINT_PRESET: z.string().optional().default(""),
145
145
  LINT_CONFIG_PATH: z.string().optional().default(""),
146
146
  LINT_LEVEL: z.string().optional().default(""),
147
- NO_LINT: z.string().optional().default(""),
148
- SECURITY_LEVEL: z.string().optional().default("strict")
147
+ NO_LINT: z.coerce.boolean().optional().default(false),
148
+ SECURITY_LEVEL: z.string().optional().default("strict"),
149
+ CLIENT_ID: z.string().optional().default("")
149
150
  });
150
151
  configSchema = z.object({
151
152
  port: z.number().optional(),
@@ -188,7 +189,8 @@ var init_config = __esm({
188
189
  lintConfigPath: z.string().optional(),
189
190
  lintLevel: z.string().optional(),
190
191
  noLint: z.boolean().optional(),
191
- securityLevel: z.enum(["strict", "standard", "relaxed"]).optional()
192
+ securityLevel: z.enum(["strict", "standard", "relaxed"]).optional(),
193
+ clientId: z.string().optional()
192
194
  });
193
195
  cliArgs = parseArgs();
194
196
  envResult = envSchema.safeParse(process.env);
@@ -200,43 +202,44 @@ var init_config = __esm({
200
202
  configFilePath = env.CONFIG_FILE || cliArgs.configFile || path.join(os.homedir(), ".atlisp", "atlisp.config.json");
201
203
  fileConfig = loadConfigFile(configFilePath);
202
204
  config = {
203
- port: fileConfig?.port || parseInt(cliArgs.port || env.PORT, 10),
204
- host: fileConfig?.host || cliArgs.host || env.HOST,
205
+ port: fileConfig?.port ?? cliArgs.port ?? env.PORT,
206
+ host: fileConfig?.host ?? cliArgs.host ?? env.HOST,
205
207
  transport: fileConfig?.transport ?? cliArgs.transport ?? env.TRANSPORT,
206
- apiKey: fileConfig?.apiKey || env.MCP_API_KEY,
207
- enableSse: fileConfig?.enableSse ?? env.ENABLE_SSE === "true",
208
- messageTimeout: fileConfig?.messageTimeout || parseInt(env.MESSAGE_TIMEOUT, 10),
209
- heartbeatInterval: fileConfig?.heartbeatInterval || parseInt(env.HEARTBEAT_INTERVAL, 10),
210
- busyRetries: fileConfig?.busyRetries || parseInt(env.BUSY_RETRIES, 10),
211
- busyDelay: fileConfig?.busyDelay || parseInt(env.BUSY_DELAY, 10),
212
- enableCors: fileConfig?.enableCors ?? env.ENABLE_CORS === "true",
213
- corsOrigin: fileConfig?.corsOrigin || env.CORS_ORIGIN,
214
- rateLimitWindow: fileConfig?.rateLimitWindow || parseInt(env.RATE_LIMIT_WINDOW, 10),
215
- rateLimitMax: fileConfig?.rateLimitMax || parseInt(env.RATE_LIMIT_MAX, 10),
216
- resourceCacheTtl: fileConfig?.resourceCacheTtl || parseInt(env.RESOURCE_CACHE_TTL, 10),
217
- functionLibCacheTtl: fileConfig?.functionLibCacheTtl || parseInt(env.FUNCTION_LIB_CACHE_TTL, 10),
218
- debug: fileConfig?.debug ?? env.DEBUG === "true",
219
- debugFile: fileConfig?.debugFile || env.DEBUG_FILE,
220
- functionLibUrl: fileConfig?.functionLibUrl || env.FUNCTION_LIB_URL,
208
+ apiKey: fileConfig?.apiKey ?? env.MCP_API_KEY,
209
+ enableSse: fileConfig?.enableSse ?? env.ENABLE_SSE,
210
+ messageTimeout: fileConfig?.messageTimeout ?? env.MESSAGE_TIMEOUT,
211
+ heartbeatInterval: fileConfig?.heartbeatInterval ?? env.HEARTBEAT_INTERVAL,
212
+ busyRetries: fileConfig?.busyRetries ?? env.BUSY_RETRIES,
213
+ busyDelay: fileConfig?.busyDelay ?? env.BUSY_DELAY,
214
+ enableCors: fileConfig?.enableCors ?? env.ENABLE_CORS,
215
+ corsOrigin: fileConfig?.corsOrigin ?? env.CORS_ORIGIN,
216
+ rateLimitWindow: fileConfig?.rateLimitWindow ?? env.RATE_LIMIT_WINDOW,
217
+ rateLimitMax: fileConfig?.rateLimitMax ?? env.RATE_LIMIT_MAX,
218
+ resourceCacheTtl: fileConfig?.resourceCacheTtl ?? env.RESOURCE_CACHE_TTL,
219
+ functionLibCacheTtl: fileConfig?.functionLibCacheTtl ?? env.FUNCTION_LIB_CACHE_TTL,
220
+ debug: fileConfig?.debug ?? env.DEBUG,
221
+ debugFile: fileConfig?.debugFile ?? env.DEBUG_FILE,
222
+ functionLibUrl: fileConfig?.functionLibUrl ?? env.FUNCTION_LIB_URL,
221
223
  configFile: configFilePath,
222
- apiKeyRateLimit: fileConfig?.apiKeyRateLimit || parseInt(env.API_KEY_RATE_LIMIT, 10),
223
- apiKeys: fileConfig?.apiKeys || [],
224
- metricsEnabled: fileConfig?.metricsEnabled ?? env.METRICS_ENABLED === "true",
225
- requestLogEnabled: fileConfig?.requestLogEnabled ?? env.REQUEST_LOG_ENABLED !== "false",
226
- sslKey: fileConfig?.sslKey || env.SSL_KEY_PATH || cliArgs.sslKey,
227
- sslCert: fileConfig?.sslCert || env.SSL_CERT_PATH || cliArgs.sslCert,
228
- llmApiKey: fileConfig?.llmApiKey || env.LLM_API_KEY,
229
- llmApiUrl: fileConfig?.llmApiUrl || env.LLM_API_URL,
230
- llmModel: fileConfig?.llmModel || env.LLM_MODEL,
231
- llmMaxTokens: fileConfig?.llmMaxTokens || parseInt(env.LLM_MAX_TOKENS, 10),
232
- llmTemperature: fileConfig?.llmTemperature || parseFloat(env.LLM_TEMPERATURE),
233
- maxPoolSize: fileConfig?.maxPoolSize || 3,
234
- workerPoolSize: fileConfig?.workerPoolSize || parseInt(env.WORKER_POOL_SIZE, 10),
235
- lintPreset: fileConfig?.lintPreset || cliArgs.lintPreset || env.LINT_PRESET || void 0,
236
- lintConfigPath: fileConfig?.lintConfigPath || cliArgs.lintConfigPath || env.LINT_CONFIG_PATH || void 0,
237
- lintLevel: fileConfig?.lintLevel || cliArgs.lintLevel || env.LINT_LEVEL || void 0,
238
- noLint: fileConfig?.noLint ?? cliArgs.noLint ?? env.NO_LINT === "true",
239
- securityLevel: fileConfig?.securityLevel ?? cliArgs.securityLevel ?? (env.SECURITY_LEVEL || "strict")
224
+ apiKeyRateLimit: fileConfig?.apiKeyRateLimit ?? env.API_KEY_RATE_LIMIT,
225
+ apiKeys: fileConfig?.apiKeys ?? [],
226
+ metricsEnabled: fileConfig?.metricsEnabled ?? env.METRICS_ENABLED,
227
+ requestLogEnabled: fileConfig?.requestLogEnabled ?? env.REQUEST_LOG_ENABLED,
228
+ sslKey: fileConfig?.sslKey ?? env.SSL_KEY_PATH ?? cliArgs.sslKey,
229
+ sslCert: fileConfig?.sslCert ?? env.SSL_CERT_PATH ?? cliArgs.sslCert,
230
+ llmApiKey: fileConfig?.llmApiKey ?? env.LLM_API_KEY,
231
+ llmApiUrl: fileConfig?.llmApiUrl ?? env.LLM_API_URL,
232
+ llmModel: fileConfig?.llmModel ?? env.LLM_MODEL,
233
+ llmMaxTokens: fileConfig?.llmMaxTokens ?? env.LLM_MAX_TOKENS,
234
+ llmTemperature: fileConfig?.llmTemperature ?? env.LLM_TEMPERATURE,
235
+ maxPoolSize: fileConfig?.maxPoolSize ?? 3,
236
+ workerPoolSize: fileConfig?.workerPoolSize ?? env.WORKER_POOL_SIZE,
237
+ lintPreset: fileConfig?.lintPreset ?? cliArgs.lintPreset ?? (env.LINT_PRESET || void 0),
238
+ lintConfigPath: fileConfig?.lintConfigPath ?? cliArgs.lintConfigPath ?? (env.LINT_CONFIG_PATH || void 0),
239
+ lintLevel: fileConfig?.lintLevel ?? cliArgs.lintLevel ?? (env.LINT_LEVEL || void 0),
240
+ noLint: fileConfig?.noLint ?? cliArgs.noLint ?? env.NO_LINT,
241
+ securityLevel: fileConfig?.securityLevel ?? cliArgs.securityLevel ?? (env.SECURITY_LEVEL || "strict"),
242
+ clientId: fileConfig?.clientId ?? (env.CLIENT_ID || "")
240
243
  };
241
244
  if (config.lintConfigPath) process.env.LINT_CONFIG_PATH = config.lintConfigPath;
242
245
  if (config.lintPreset) process.env.LINT_PRESET = config.lintPreset;
@@ -453,15 +456,89 @@ var init_metrics = __esm({
453
456
  }
454
457
  });
455
458
 
459
+ // src/cad-circuit-breaker.js
460
+ var CB_CLOSED, CB_OPEN, CB_HALF_OPEN, CB_FAILURE_THRESHOLD, CB_RESET_TIMEOUT, CircuitBreaker;
461
+ var init_cad_circuit_breaker = __esm({
462
+ "src/cad-circuit-breaker.js"() {
463
+ CB_CLOSED = 0;
464
+ CB_OPEN = 1;
465
+ CB_HALF_OPEN = 2;
466
+ CB_FAILURE_THRESHOLD = 5;
467
+ CB_RESET_TIMEOUT = 1e4;
468
+ CircuitBreaker = class {
469
+ constructor(connectionId, onOpen, onHalfOpen) {
470
+ this._connId = connectionId;
471
+ this._state = CB_CLOSED;
472
+ this._failureCount = 0;
473
+ this._lastFailureTime = 0;
474
+ this._timer = null;
475
+ this._onOpen = onOpen;
476
+ this._onHalfOpen = onHalfOpen;
477
+ }
478
+ get state() {
479
+ return this._state;
480
+ }
481
+ get failureCount() {
482
+ return this._failureCount;
483
+ }
484
+ allowRequest() {
485
+ if (this._state === CB_OPEN) {
486
+ const elapsed = Date.now() - this._lastFailureTime;
487
+ if (elapsed >= CB_RESET_TIMEOUT) {
488
+ this._state = CB_HALF_OPEN;
489
+ if (this._onHalfOpen) this._onHalfOpen(this._connId);
490
+ } else {
491
+ return false;
492
+ }
493
+ }
494
+ return true;
495
+ }
496
+ recordSuccess() {
497
+ this._state = CB_CLOSED;
498
+ this._failureCount = 0;
499
+ if (this._timer) {
500
+ clearTimeout(this._timer);
501
+ this._timer = null;
502
+ }
503
+ }
504
+ recordFailure() {
505
+ this._failureCount++;
506
+ this._lastFailureTime = Date.now();
507
+ if (this._failureCount >= CB_FAILURE_THRESHOLD && this._state !== CB_OPEN) {
508
+ this._state = CB_OPEN;
509
+ if (this._onOpen) this._onOpen(this._connId);
510
+ this._timer = setTimeout(() => {
511
+ this._state = CB_HALF_OPEN;
512
+ if (this._onHalfOpen) this._onHalfOpen(this._connId);
513
+ }, CB_RESET_TIMEOUT);
514
+ }
515
+ }
516
+ reset() {
517
+ this._state = CB_CLOSED;
518
+ this._failureCount = 0;
519
+ this._lastFailureTime = 0;
520
+ if (this._timer) {
521
+ clearTimeout(this._timer);
522
+ this._timer = null;
523
+ }
524
+ }
525
+ };
526
+ }
527
+ });
528
+
456
529
  // src/cad.js
457
530
  var cad_exports = {};
458
531
  __export(cad_exports, {
532
+ CommandQueue: () => CommandQueue,
533
+ ReadBatcher: () => ReadBatcher,
459
534
  _setInstanceResolver: () => _setInstanceResolver,
460
535
  cad: () => cad,
461
536
  createCadConnection: () => createCadConnection,
462
537
  default: () => cad_default,
463
538
  getActiveConnection: () => getActiveConnection,
539
+ isReadCommand: () => isReadCommand,
464
540
  onDocumentChanged: () => onDocumentChanged,
541
+ resolveCadConnection: () => resolveCadConnection,
465
542
  setActiveConnection: () => setActiveConnection
466
543
  });
467
544
  import { spawn } from "child_process";
@@ -506,22 +583,21 @@ function _resolveConn() {
506
583
  }
507
584
  return _activeConnection;
508
585
  }
509
- var __dirname, workerPath, MESSAGE_TIMEOUT, HEARTBEAT_INTERVAL, MAX_RESTART_ATTEMPTS, RESTART_BACKOFF_BASE, CB_CLOSED, CB_OPEN, CB_HALF_OPEN, CB_FAILURE_THRESHOLD, CB_RESET_TIMEOUT, MAX_BUFFER_SIZE, PRIORITY_HIGH, PRIORITY_NORMAL, MAX_QUEUE_SIZE, READ_PREFIXES, _docChangeCallbacks, ReadBatcher, CommandQueue, CadConnection, _activeConnection, _resolveInstanceConn, cad, cad_default;
586
+ function resolveCadConnection() {
587
+ return _resolveConn();
588
+ }
589
+ var __dirname, workerPath, MESSAGE_TIMEOUT, HEARTBEAT_INTERVAL, MAX_RESTART_ATTEMPTS, RESTART_BACKOFF_BASE, MAX_BUFFER_SIZE, PRIORITY_HIGH, PRIORITY_NORMAL, MAX_QUEUE_SIZE, READ_PREFIXES, _docChangeCallbacks, ReadBatcher, CommandQueue, CadConnection, _activeConnection, _resolveInstanceConn, cad, cad_default;
510
590
  var init_cad = __esm({
511
591
  "src/cad.js"() {
512
592
  init_config();
513
593
  init_metrics();
594
+ init_cad_circuit_breaker();
514
595
  __dirname = path2.dirname(fileURLToPath(import.meta.url));
515
596
  workerPath = path2.join(__dirname, "cad-worker.js");
516
597
  MESSAGE_TIMEOUT = config_default.messageTimeout;
517
598
  HEARTBEAT_INTERVAL = config_default.heartbeatInterval;
518
599
  MAX_RESTART_ATTEMPTS = 5;
519
600
  RESTART_BACKOFF_BASE = 1e3;
520
- CB_CLOSED = 0;
521
- CB_OPEN = 1;
522
- CB_HALF_OPEN = 2;
523
- CB_FAILURE_THRESHOLD = 5;
524
- CB_RESET_TIMEOUT = 1e4;
525
601
  MAX_BUFFER_SIZE = 1024 * 1024;
526
602
  PRIORITY_HIGH = 0;
527
603
  PRIORITY_NORMAL = 1;
@@ -708,10 +784,6 @@ var init_cad = __esm({
708
784
  _heartbeatTimer = null;
709
785
  _pendingRequests = /* @__PURE__ */ new Map();
710
786
  _commandQueue = null;
711
- _cbState = CB_CLOSED;
712
- _cbFailureCount = 0;
713
- _cbLastFailureTime = 0;
714
- _cbTimer = null;
715
787
  _restartCount = 0;
716
788
  _restartTimer = null;
717
789
  _activeDocName = null;
@@ -721,6 +793,11 @@ var init_cad = __esm({
721
793
  constructor() {
722
794
  this._connectionId = randomUUID().slice(0, 8);
723
795
  this._commandQueue = new CommandQueue(this);
796
+ this._cb = new CircuitBreaker(
797
+ this._connectionId,
798
+ (id) => console.error(`[${id}] Circuit breaker OPEN`),
799
+ (id) => console.error(`[${id}] Circuit breaker HALF_OPEN, allowing trial request`)
800
+ );
724
801
  }
725
802
  get _workerPoolSize() {
726
803
  return config_default.workerPoolSize || 2;
@@ -728,11 +805,15 @@ var init_cad = __esm({
728
805
  isAvailable() {
729
806
  return process.platform === "win32";
730
807
  }
731
- async connect(platform = null) {
808
+ async connect(platform = null, options = {}) {
732
809
  if (!this.isAvailable()) return false;
733
- this._cbSuccess();
810
+ this._cb.recordSuccess();
734
811
  try {
735
812
  const msg = platform ? { type: "connect", platform } : { type: "connect" };
813
+ if (options.moniker) {
814
+ msg.moniker = options.moniker;
815
+ msg.monikerKey = options.key || "moniker";
816
+ }
736
817
  const result = await this._sendMessage(msg);
737
818
  if (result && result.success) {
738
819
  this._version = result.version;
@@ -845,21 +926,8 @@ var init_cad = __esm({
845
926
  return `Platform: ${this._product}, Version: ${this._version}, Status: Connected`;
846
927
  }
847
928
  async disconnect() {
848
- this.connected = false;
849
- setCadConnected(false);
850
- this._version = null;
851
- this._product = null;
852
- this._platform = null;
853
- this._activeDocName = null;
854
- this._monitorStarted = false;
855
- this._commandQueue.clear();
856
- this._cbState = CB_CLOSED;
857
- this._cbFailureCount = 0;
858
- if (this._cbTimer) {
859
- clearTimeout(this._cbTimer);
860
- this._cbTimer = null;
861
- }
862
- this._resetWorker();
929
+ this._reset();
930
+ this._cb.reset();
863
931
  }
864
932
  async ping() {
865
933
  try {
@@ -878,12 +946,6 @@ var init_cad = __esm({
878
946
  this._activeDocName = null;
879
947
  this._monitorStarted = false;
880
948
  this._commandQueue.clear();
881
- this._cbState = CB_CLOSED;
882
- this._cbFailureCount = 0;
883
- if (this._cbTimer) {
884
- clearTimeout(this._cbTimer);
885
- this._cbTimer = null;
886
- }
887
949
  this._resetWorker();
888
950
  }
889
951
  _getQueueSize() {
@@ -892,44 +954,6 @@ var init_cad = __esm({
892
954
  _getRestartCount() {
893
955
  return this._restartCount;
894
956
  }
895
- // --- Circuit breaker ---
896
- _cbSuccess() {
897
- this._cbState = CB_CLOSED;
898
- this._cbFailureCount = 0;
899
- if (this._cbTimer) {
900
- clearTimeout(this._cbTimer);
901
- this._cbTimer = null;
902
- }
903
- }
904
- _cbFailure() {
905
- this._cbFailureCount++;
906
- this._cbLastFailureTime = Date.now();
907
- if (this._cbFailureCount >= CB_FAILURE_THRESHOLD && this._cbState !== CB_OPEN) {
908
- this._cbState = CB_OPEN;
909
- console.error(`[${this._connectionId}] Circuit breaker OPEN after ${this._cbFailureCount} failures`);
910
- this._cbTimer = setTimeout(() => {
911
- this._cbState = CB_HALF_OPEN;
912
- console.error(`[${this._connectionId}] Circuit breaker HALF_OPEN, allowing trial request`);
913
- }, CB_RESET_TIMEOUT);
914
- }
915
- }
916
- _cbAllowRequest() {
917
- if (this._cbState === CB_OPEN) {
918
- const elapsed = Date.now() - this._cbLastFailureTime;
919
- if (elapsed >= CB_RESET_TIMEOUT) {
920
- this._cbState = CB_HALF_OPEN;
921
- console.error(`[${this._connectionId}] Circuit breaker auto-transition to HALF_OPEN`);
922
- } else {
923
- return false;
924
- }
925
- }
926
- return true;
927
- }
928
- // --- Worker management ---
929
- _isReadOnlyMessage(msg) {
930
- if (msg.code) return isReadCommand(msg.code);
931
- return false;
932
- }
933
957
  _spawnWorker(type) {
934
958
  const w = spawn("node", [workerPath], { stdio: ["pipe", "pipe", "pipe"] });
935
959
  w.connected = true;
@@ -940,7 +964,7 @@ var init_cad = __esm({
940
964
  w.connected = false;
941
965
  if (type === "primary") {
942
966
  if (w !== this._primaryWorker) return;
943
- this._cbFailure();
967
+ this._cb.recordFailure();
944
968
  const exitError = new Error(`Primary worker exited with code ${code}`);
945
969
  for (const [id, entry] of this._pendingRequests) {
946
970
  clearTimeout(entry.timeout);
@@ -977,7 +1001,7 @@ var init_cad = __esm({
977
1001
  this._primaryWorker = null;
978
1002
  }
979
1003
  this._primaryWorker = this._spawnWorker("primary");
980
- this._cbSuccess();
1004
+ this._cb.recordSuccess();
981
1005
  if (!this._heartbeatTimer) this._startHeartbeat();
982
1006
  this._restartCount = 0;
983
1007
  }
@@ -1118,14 +1142,9 @@ var init_cad = __esm({
1118
1142
  }, delay);
1119
1143
  }
1120
1144
  async _sendMessage(msg) {
1121
- if (!this._cbAllowRequest()) {
1145
+ if (!this._cb.allowRequest()) {
1122
1146
  console.error(`[${this._connectionId}] Circuit breaker OPEN, auto-recovering...`);
1123
- this._cbState = CB_CLOSED;
1124
- this._cbFailureCount = 0;
1125
- if (this._cbTimer) {
1126
- clearTimeout(this._cbTimer);
1127
- this._cbTimer = null;
1128
- }
1147
+ this._cb.reset();
1129
1148
  await this._resetWorker();
1130
1149
  await this._getWorker(false);
1131
1150
  }
@@ -1141,17 +1160,17 @@ var init_cad = __esm({
1141
1160
  const p = self._pendingRequests.get(requestId);
1142
1161
  if (p) {
1143
1162
  self._pendingRequests.delete(requestId);
1144
- if (!noCircuitBreaker) self._cbFailure();
1163
+ if (!noCircuitBreaker) self._cb.recordFailure();
1145
1164
  reject(new Error("Timeout waiting for worker"));
1146
1165
  }
1147
1166
  }, MESSAGE_TIMEOUT);
1148
1167
  const entry = {
1149
1168
  resolve: (v) => {
1150
- if (!noCircuitBreaker && !entry._skipCb) self._cbSuccess();
1169
+ if (!noCircuitBreaker && !entry._skipCb) self._cb.recordSuccess();
1151
1170
  resolve(v);
1152
1171
  },
1153
1172
  reject: (e) => {
1154
- if (!noCircuitBreaker && !entry._skipCb) self._cbFailure();
1173
+ if (!noCircuitBreaker && !entry._skipCb) self._cb.recordFailure();
1155
1174
  reject(e);
1156
1175
  },
1157
1176
  timeout
@@ -1162,11 +1181,15 @@ var init_cad = __esm({
1162
1181
  } catch (e) {
1163
1182
  clearTimeout(timeout);
1164
1183
  self._pendingRequests.delete(requestId);
1165
- if (!noCircuitBreaker) self._cbFailure();
1184
+ if (!noCircuitBreaker) self._cb.recordFailure();
1166
1185
  reject(new Error(`Failed to write to worker: ${e.message}`));
1167
1186
  }
1168
1187
  });
1169
1188
  }
1189
+ _isReadOnlyMessage(msg) {
1190
+ if (msg.code) return isReadCommand(msg.code);
1191
+ return false;
1192
+ }
1170
1193
  };
1171
1194
  _activeConnection = new CadConnection();
1172
1195
  _resolveInstanceConn = null;
@@ -1175,28 +1198,28 @@ var init_cad = __esm({
1175
1198
  return _resolveConn().connected;
1176
1199
  },
1177
1200
  set connected(v) {
1178
- _activeConnection.connected = v;
1201
+ _resolveConn().connected = v;
1179
1202
  },
1180
1203
  get version() {
1181
- return _activeConnection._version;
1204
+ return _resolveConn()._version;
1182
1205
  },
1183
1206
  set version(v) {
1184
- _activeConnection._version = v;
1207
+ _resolveConn()._version = v;
1185
1208
  },
1186
1209
  get product() {
1187
- return _activeConnection._product;
1210
+ return _resolveConn()._product;
1188
1211
  },
1189
1212
  set product(v) {
1190
- _activeConnection._product = v;
1213
+ _resolveConn()._product = v;
1191
1214
  },
1192
1215
  get _platform() {
1193
- return _activeConnection._platform;
1216
+ return _resolveConn()._platform;
1194
1217
  },
1195
1218
  set _platform(v) {
1196
- _activeConnection._platform = v;
1219
+ _resolveConn()._platform = v;
1197
1220
  },
1198
1221
  async connect(platform) {
1199
- return _activeConnection.connect(platform);
1222
+ return _resolveConn().connect(platform);
1200
1223
  },
1201
1224
  async sendCommand(code) {
1202
1225
  return _resolveConn().sendCommand(code);
@@ -1205,49 +1228,49 @@ var init_cad = __esm({
1205
1228
  return _resolveConn().sendCommandWithResult(code, encoding);
1206
1229
  },
1207
1230
  getVersion() {
1208
- return _activeConnection.getVersion();
1231
+ return _resolveConn().getVersion();
1209
1232
  },
1210
1233
  getPlatform() {
1211
- return _activeConnection.getPlatform();
1234
+ return _resolveConn().getPlatform();
1212
1235
  },
1213
1236
  getConnectionId() {
1214
- return _activeConnection.getConnectionId();
1237
+ return _resolveConn().getConnectionId();
1215
1238
  },
1216
1239
  isAvailable() {
1217
- return _activeConnection.isAvailable();
1240
+ return _resolveConn().isAvailable();
1218
1241
  },
1219
1242
  async isBusy() {
1220
- return _activeConnection.isBusy();
1243
+ return _resolveConn().isBusy();
1221
1244
  },
1222
1245
  async hasDoc() {
1223
- return _activeConnection.hasDoc();
1246
+ return _resolveConn().hasDoc();
1224
1247
  },
1225
1248
  async newDoc() {
1226
- return _activeConnection.newDoc();
1249
+ return _resolveConn().newDoc();
1227
1250
  },
1228
1251
  async bringToFront() {
1229
- return _activeConnection.bringToFront();
1252
+ return _resolveConn().bringToFront();
1230
1253
  },
1231
1254
  async getActiveDocInfo() {
1232
- return _activeConnection.getActiveDocInfo();
1255
+ return _resolveConn().getActiveDocInfo();
1233
1256
  },
1234
1257
  async getInfo() {
1235
- return _activeConnection.getInfo();
1258
+ return _resolveConn().getInfo();
1236
1259
  },
1237
1260
  async disconnect() {
1238
- return _activeConnection.disconnect();
1261
+ return _resolveConn().disconnect();
1239
1262
  },
1240
1263
  async ping() {
1241
- return _activeConnection.ping();
1264
+ return _resolveConn().ping();
1242
1265
  },
1243
1266
  _reset() {
1244
- return _activeConnection._reset();
1267
+ return _resolveConn()._reset();
1245
1268
  },
1246
1269
  _getQueueSize() {
1247
- return _activeConnection._getQueueSize();
1270
+ return _resolveConn()._getQueueSize();
1248
1271
  },
1249
1272
  _getRestartCount() {
1250
- return _activeConnection._getRestartCount();
1273
+ return _resolveConn()._getRestartCount();
1251
1274
  }
1252
1275
  };
1253
1276
  cad_default = CadConnection;
@@ -1258,7 +1281,7 @@ var init_cad = __esm({
1258
1281
  import path3 from "path";
1259
1282
  import { fileURLToPath as fileURLToPath2 } from "url";
1260
1283
  import { createRequire } from "module";
1261
- var CAD_PLATFORMS, FILE_EXTENSIONS, __dirname2, require2, version, SERVER_NAME, SERVER_VERSION, SERVER_CAPABILITIES, MOCK_PACKAGES;
1284
+ var CAD_PLATFORMS, FILE_EXTENSIONS, __dirname2, require2, version, SERVER_NAME, SERVER_VERSION, SERVER_CAPABILITIES, MOCK_PACKAGES, CAD_RESOURCE_URIS;
1262
1285
  var init_constants = __esm({
1263
1286
  "src/constants.js"() {
1264
1287
  CAD_PLATFORMS = ["AutoCAD", "ZWCAD", "GStarCAD", "BricsCAD"];
@@ -1281,6 +1304,17 @@ var init_constants = __esm({
1281
1304
  completions: {}
1282
1305
  };
1283
1306
  MOCK_PACKAGES = [];
1307
+ CAD_RESOURCE_URIS = [
1308
+ "atlisp://cad/info",
1309
+ "atlisp://dwg/name",
1310
+ "atlisp://dwg/path",
1311
+ "atlisp://dwg/entities",
1312
+ "atlisp://dwg/tbl",
1313
+ "atlisp://dwg/block-refs",
1314
+ "atlisp://dwg/text-content",
1315
+ "atlisp://cad/dwgs",
1316
+ "atlisp://packages"
1317
+ ];
1284
1318
  }
1285
1319
  });
1286
1320
 
@@ -1481,7 +1515,10 @@ var init_resource_defs = __esm({
1481
1515
  { uri: "atlisp://standards/drafting", name: "Drafting Standards", description: "CAD \u5236\u56FE\u89C4\u8303", mimeType: "application/json", subscribable: false },
1482
1516
  { uri: "atlisp://standards/coding", name: "Coding Standards", description: "@lisp \u7F16\u7801\u89C4\u8303", mimeType: "application/json", subscribable: false },
1483
1517
  { uri: "atlisp://dwg/context", name: "DWG Context", description: "\u5F53\u524D\u56FE\u7EB8\u4E0A\u4E0B\u6587\u6458\u8981\u3002\u8FD4\u56DE\u6587\u6863\u540D/\u8DEF\u5F84/\u5E03\u5C40/UCS/\u56FE\u5C42\u5217\u8868/\u5B9E\u4F53\u7EDF\u8BA1/\u7CFB\u7EDF\u53D8\u91CF\u7EC4\u5408\u4FE1\u606F\uFF0C\u5E2E\u52A9\u5FEB\u901F\u4E86\u89E3\u5F53\u524D CAD \u72B6\u6001", mimeType: "application/json", subscribable: true },
1484
- { uri: "atlisp://tools/relationships", name: "Tool Relationships", description: "\u5DE5\u5177\u4F9D\u8D56\u5173\u7CFB\u56FE\u3002\u63CF\u8FF0\u5DE5\u5177\u95F4\u7684\u8C03\u7528\u94FE\u5173\u7CFB\uFF08\u5982 select_entities \u2192 batch_delete\uFF09\uFF0C\u5E2E\u52A9\u7406\u89E3\u5DE5\u5177\u7EC4\u5408\u4F7F\u7528\u65B9\u5F0F", mimeType: "application/json", subscribable: false }
1518
+ { uri: "atlisp://tools/relationships", name: "Tool Relationships", description: "\u5DE5\u5177\u4F9D\u8D56\u5173\u7CFB\u56FE\u3002\u63CF\u8FF0\u5DE5\u5177\u95F4\u7684\u8C03\u7528\u94FE\u5173\u7CFB\uFF08\u5982 select_entities \u2192 batch_delete\uFF09\uFF0C\u5E2E\u52A9\u7406\u89E3\u5DE5\u5177\u7EC4\u5408\u4F7F\u7528\u65B9\u5F0F", mimeType: "application/json", subscribable: false },
1519
+ { uri: "atlisp://dwg/analysis", name: "Drawing Analysis", description: "\u5F53\u524D\u56FE\u7EB8\u7EFC\u5408\u5206\u6790\uFF1A\u5B9E\u4F53\u7C7B\u578B\u7EDF\u8BA1\u3001\u5757\u4F7F\u7528\u7EDF\u8BA1\u3001\u56FE\u5C42\u6458\u8981\u3001\u6587\u5B57\u7EDF\u8BA1\u3001\u7CFB\u7EDF\u53D8\u91CF\u5FEB\u7167\u3002\u652F\u6301 ?type=summary\uFF08\u9ED8\u8BA4\uFF09\u6216 ?type=detailed", mimeType: "application/json", subscribable: true },
1520
+ { uri: "atlisp://tools/categories", name: "Tool Categories", description: "\u5DE5\u5177\u5206\u7C7B\u6982\u89C8\u3002\u5217\u51FA\u6240\u6709\u5206\u7C7B\u53CA\u5176\u5305\u542B\u7684\u5DE5\u5177\u6570\u91CF", mimeType: "application/json", subscribable: false },
1521
+ { uri: "atlisp://dwg/standards/check", name: "Drawing Standards Check", description: "\u68C0\u67E5\u5F53\u524D\u56FE\u7EB8\u662F\u5426\u7B26\u5408\u52A0\u8F7D\u7684\u5236\u56FE\u89C4\u8303\uFF08\u56FE\u5C42\u547D\u540D\u3001\u989C\u8272\u3001\u7EBF\u578B\u7B49\u89C4\u5219\uFF09", mimeType: "application/json", subscribable: false }
1485
1522
  ];
1486
1523
  TBL_QUERIES = {
1487
1524
  layer: { name: "layer", lisp: `(progn
@@ -1528,49 +1565,70 @@ var init_resource_defs = __esm({
1528
1565
  });
1529
1566
 
1530
1567
  // src/handlers/resource-cache.js
1568
+ function _getCache() {
1569
+ const key = _instanceKey || _defaultKey;
1570
+ if (!_instanceCaches.has(key)) {
1571
+ _instanceCaches.set(key, /* @__PURE__ */ new Map());
1572
+ }
1573
+ return _instanceCaches.get(key);
1574
+ }
1575
+ function _ttl() {
1576
+ return config_default.resourceCacheTtl;
1577
+ }
1531
1578
  function ensureCleanup() {
1532
1579
  if (cleanupTimer) return;
1533
1580
  cleanupTimer = setInterval(() => {
1534
1581
  const now = Date.now();
1535
- for (const [key, entry] of resourceCache) {
1536
- if (now - entry.timestamp >= CACHE_TTL) {
1537
- resourceCache.delete(key);
1582
+ const ttl = _ttl();
1583
+ for (const cache of _instanceCaches.values()) {
1584
+ for (const [key, entry] of cache) {
1585
+ if (now - entry.timestamp >= ttl) {
1586
+ cache.delete(key);
1587
+ }
1538
1588
  }
1539
1589
  }
1540
1590
  }, CLEANUP_INTERVAL);
1541
1591
  cleanupTimer.unref();
1542
1592
  }
1593
+ function setInstanceKey(key) {
1594
+ _instanceKey = key || null;
1595
+ }
1543
1596
  function getCached(key) {
1544
- const entry = resourceCache.get(key);
1545
- if (entry && Date.now() - entry.timestamp < CACHE_TTL) {
1597
+ const cache = _getCache();
1598
+ const entry = cache.get(key);
1599
+ if (entry && Date.now() - entry.timestamp < _ttl()) {
1546
1600
  return entry.data;
1547
1601
  }
1548
- resourceCache.delete(key);
1602
+ cache.delete(key);
1549
1603
  return null;
1550
1604
  }
1551
1605
  function setCache(key, data) {
1552
- resourceCache.set(key, { data, timestamp: Date.now() });
1606
+ _getCache().set(key, { data, timestamp: Date.now() });
1553
1607
  }
1554
1608
  function clearCache(uri) {
1555
- const prefix = URI_CACHE_PREFIX[uri];
1556
- if (prefix) {
1557
- for (const key of resourceCache.keys()) {
1558
- if (key === prefix || key.startsWith(prefix + ":")) {
1559
- resourceCache.delete(key);
1609
+ const basePrefix = URI_CACHE_PREFIX[uri];
1610
+ if (basePrefix) {
1611
+ const cache = _getCache();
1612
+ for (const key of cache.keys()) {
1613
+ if (key === basePrefix || key.startsWith(basePrefix + ":")) {
1614
+ cache.delete(key);
1560
1615
  }
1561
1616
  }
1562
1617
  } else if (!uri) {
1563
- resourceCache.clear();
1618
+ const key = _instanceKey || _defaultKey;
1619
+ _instanceCaches.delete(key);
1620
+ _instanceCaches.set(key, /* @__PURE__ */ new Map());
1564
1621
  }
1565
1622
  }
1566
- var CACHE_TTL, resourceCache, CLEANUP_INTERVAL, cleanupTimer, URI_CACHE_PREFIX;
1623
+ var CLEANUP_INTERVAL, cleanupTimer, _instanceKey, _instanceCaches, _defaultKey, URI_CACHE_PREFIX;
1567
1624
  var init_resource_cache = __esm({
1568
1625
  "src/handlers/resource-cache.js"() {
1569
1626
  init_config();
1570
- CACHE_TTL = config_default.resourceCacheTtl;
1571
- resourceCache = /* @__PURE__ */ new Map();
1572
1627
  CLEANUP_INTERVAL = 6e4;
1573
1628
  cleanupTimer = null;
1629
+ _instanceKey = null;
1630
+ _instanceCaches = /* @__PURE__ */ new Map();
1631
+ _defaultKey = "__default__";
1574
1632
  ensureCleanup();
1575
1633
  URI_CACHE_PREFIX = {
1576
1634
  "atlisp://cad/info": "cad:info",
@@ -1591,6 +1649,23 @@ function createError(code, details = {}) {
1591
1649
  const def = ERROR_DEFS[code] || ERROR_DEFS[ERROR_CODES.INTERNAL_ERROR];
1592
1650
  return new MCPError(code, def.message, details);
1593
1651
  }
1652
+ function toMcpError(error2) {
1653
+ if (error2 instanceof MCPError) return error2;
1654
+ if (error2 instanceof Error) {
1655
+ return new MCPError(ERROR_CODES.INTERNAL_ERROR, error2.message, { stack: error2.stack });
1656
+ }
1657
+ return new MCPError(ERROR_CODES.INTERNAL_ERROR, String(error2));
1658
+ }
1659
+ function mcpErrorResponse(error2) {
1660
+ const mcpErr = toMcpError(error2);
1661
+ const json = mcpErr.toJSON();
1662
+ return {
1663
+ content: [{ type: "text", text: `[${json.code}] ${json.message}
1664
+ \u63D0\u793A: ${json.hint}
1665
+ \u5EFA\u8BAE\u5DE5\u5177: ${json.suggestedTools.join(", ") || "\u65E0"}` }],
1666
+ isError: true
1667
+ };
1668
+ }
1594
1669
  function mcpSuccessResponse(text) {
1595
1670
  return { content: [{ type: "text", text: String(text) }] };
1596
1671
  }
@@ -1604,9 +1679,14 @@ var init_errors = __esm({
1604
1679
  LISP_PAREN_MISMATCH: "LISP_PAREN_MISMATCH",
1605
1680
  TIMEOUT: "TIMEOUT",
1606
1681
  INVALID_PARAMS: "INVALID_PARAMS",
1682
+ INVALID_ARGS: "INVALID_ARGS",
1607
1683
  METHOD_NOT_FOUND: "METHOD_NOT_FOUND",
1684
+ HANDLER_NOT_FOUND: "HANDLER_NOT_FOUND",
1608
1685
  RESOURCE_NOT_FOUND: "RESOURCE_NOT_FOUND",
1609
1686
  PACKAGE_NOT_FOUND: "PACKAGE_NOT_FOUND",
1687
+ OPERATION_FAILED: "OPERATION_FAILED",
1688
+ RATE_LIMITED: "RATE_LIMITED",
1689
+ SESSION_NOT_FOUND: "SESSION_NOT_FOUND",
1610
1690
  INTERNAL_ERROR: "INTERNAL_ERROR"
1611
1691
  };
1612
1692
  ERROR_DEFS = {
@@ -1640,6 +1720,31 @@ var init_errors = __esm({
1640
1720
  hint: "\u8BF7\u68C0\u67E5\u5DE5\u5177\u53C2\u6570\u683C\u5F0F\u662F\u5426\u6B63\u786E\u3002\u4F7F\u7528 search_tools \u6216 list_tools_by_category \u67E5\u770B\u5DE5\u5177\u53C2\u6570\u8981\u6C42",
1641
1721
  tools: ["search_tools", "list_tools_by_category"]
1642
1722
  },
1723
+ [ERROR_CODES.INVALID_ARGS]: {
1724
+ message: "\u53C2\u6570\u6821\u9A8C\u5931\u8D25",
1725
+ hint: "\u5DE5\u5177\u6240\u9700\u53C2\u6570\u7F3A\u5931\u6216\u683C\u5F0F\u9519\u8BEF\u3002\u8BF7\u68C0\u67E5\u53C2\u6570\u7C7B\u578B\u548C\u5FC5\u586B\u5B57\u6BB5\uFF0C\u4F7F\u7528 search_tools \u67E5\u770B\u5DE5\u5177\u53C2\u6570\u8981\u6C42",
1726
+ tools: ["search_tools", "list_tools_by_category"]
1727
+ },
1728
+ [ERROR_CODES.HANDLER_NOT_FOUND]: {
1729
+ message: "\u5DE5\u5177\u672A\u627E\u5230",
1730
+ hint: "\u5DE5\u5177\u540D\u79F0\u4E0D\u5B58\u5728\u3002\u4F7F\u7528 search_tools \u641C\u7D22\u53EF\u7528\u5DE5\u5177\uFF0C\u6216 list_tools_by_category \u6D4F\u89C8\u5168\u90E8\u5206\u7C7B",
1731
+ tools: ["search_tools", "list_tools_by_category"]
1732
+ },
1733
+ [ERROR_CODES.OPERATION_FAILED]: {
1734
+ message: "\u64CD\u4F5C\u6267\u884C\u5931\u8D25",
1735
+ hint: "\u5DE5\u5177\u6267\u884C\u8FC7\u7A0B\u4E2D\u53D1\u751F\u9519\u8BEF\u3002\u53EF\u91CD\u8BD5\u64CD\u4F5C\uFF0C\u6216\u68C0\u67E5\u65E5\u5FD7\u83B7\u53D6\u8BE6\u7EC6\u4FE1\u606F",
1736
+ tools: ["get_system_status", "get_cad_info"]
1737
+ },
1738
+ [ERROR_CODES.RATE_LIMITED]: {
1739
+ message: "\u8C03\u7528\u9891\u7387\u8D85\u9650",
1740
+ hint: "\u5DE5\u5177\u8C03\u7528\u8FC7\u4E8E\u9891\u7E41\u3002\u8BF7\u7B49\u5F85\u4E00\u6BB5\u65F6\u95F4\u540E\u518D\u8BD5",
1741
+ tools: []
1742
+ },
1743
+ [ERROR_CODES.SESSION_NOT_FOUND]: {
1744
+ message: "\u4F1A\u8BDD\u672A\u627E\u5230",
1745
+ hint: "\u6307\u5B9A ID \u7684\u4F1A\u8BDD\u4E0D\u5B58\u5728\u3002\u8BF7\u68C0\u67E5 sessionId \u53C2\u6570\u662F\u5426\u6B63\u786E",
1746
+ tools: ["lisp_repl_list"]
1747
+ },
1643
1748
  [ERROR_CODES.METHOD_NOT_FOUND]: {
1644
1749
  message: "\u65B9\u6CD5\u4E0D\u5B58\u5728",
1645
1750
  hint: "\u5DE5\u5177\u540D\u79F0\u8F93\u5165\u9519\u8BEF\u3002\u4F7F\u7528 search_tools \u641C\u7D22\u53EF\u7528\u5DE5\u5177\uFF0C\u6216 list_tools_by_category \u6D4F\u89C8\u5168\u90E8\u5206\u7C7B",
@@ -1784,8 +1889,8 @@ __export(renderer_exports, {
1784
1889
  renderPrompt: () => renderPrompt,
1785
1890
  renderTemplate: () => renderTemplate
1786
1891
  });
1787
- function resolvePath(obj, path20) {
1788
- const parts = path20.split(/[.\[\]]/g).filter(Boolean);
1892
+ function resolvePath(obj, path21) {
1893
+ const parts = path21.split(/[.\[\]]/g).filter(Boolean);
1789
1894
  let val = obj;
1790
1895
  for (const p of parts) {
1791
1896
  if (val == null) return void 0;
@@ -1795,8 +1900,8 @@ function resolvePath(obj, path20) {
1795
1900
  }
1796
1901
  function renderTemplate(template, args) {
1797
1902
  return template.replace(PARAM_RE, (match, rawExpr, expr) => {
1798
- const path20 = rawExpr || expr;
1799
- const val = resolvePath(args, path20);
1903
+ const path21 = rawExpr || expr;
1904
+ const val = resolvePath(args, path21);
1800
1905
  if (val == null) return "";
1801
1906
  const strVal = String(val);
1802
1907
  if (rawExpr) return strVal;
@@ -2228,8 +2333,8 @@ var init_sse_session = __esm({
2228
2333
  sendMessage(data) {
2229
2334
  return this.sendEvent(SSE_EVENTS.MESSAGE, data);
2230
2335
  }
2231
- sendEndpoint(path20) {
2232
- return this.sendEvent(SSE_EVENTS.ENDPOINT, path20);
2336
+ sendEndpoint(path21) {
2337
+ return this.sendEvent(SSE_EVENTS.ENDPOINT, path21);
2233
2338
  }
2234
2339
  sendError(code, message, id = null) {
2235
2340
  return this.sendEvent(SSE_EVENTS.ERROR, { code, message }, id);
@@ -2951,7 +3056,8 @@ var init_info_tools = __esm({
2951
3056
  }
2952
3057
  },
2953
3058
  { name: "search_tools", description: "\u6309\u5173\u952E\u8BCD\u641C\u7D22\u53EF\u7528\u5DE5\u5177", inputSchema: { type: "object", properties: { query: { type: "string", description: "\u641C\u7D22\u5173\u952E\u8BCD(\u5339\u914D\u540D\u79F0\u548C\u63CF\u8FF0)" } }, required: ["query"] } },
2954
- { name: "list_tools_by_category", description: "\u6309\u5206\u7C7B\u5217\u51FA\u5DE5\u5177", inputSchema: { type: "object", properties: { category: { type: "string", description: "\u5206\u7C7B\u540D\u79F0(\u53EF\u9009,\u4E0D\u4F20\u5219\u5217\u51FA\u6240\u6709\u5206\u7C7B)" } } } }
3059
+ { name: "list_tools_by_category", description: "\u6309\u5206\u7C7B\u5217\u51FA\u5DE5\u5177", inputSchema: { type: "object", properties: { category: { type: "string", description: "\u5206\u7C7B\u540D\u79F0(\u53EF\u9009,\u4E0D\u4F20\u5219\u5217\u51FA\u6240\u6709\u5206\u7C7B)" } } } },
3060
+ { name: "get_tool_info", description: "\u83B7\u53D6\u5DE5\u5177\u7684\u8BE6\u7EC6\u4FE1\u606F\uFF08\u53C2\u6570\u3001\u5206\u7C7B\u3001\u53EA\u8BFB/\u7834\u574F\u6027\u6807\u8BB0\u7B49\uFF09", inputSchema: { type: "object", properties: { name: { type: "string", description: "\u5DE5\u5177\u540D\u79F0" } }, required: ["name"] } }
2955
3061
  ];
2956
3062
  }
2957
3063
  });
@@ -5492,12 +5598,12 @@ var init_pool_tools = __esm({
5492
5598
  inputSchema: {
5493
5599
  type: "object",
5494
5600
  properties: {
5495
- key: { type: "string", description: "\u5B9E\u4F8B\u6807\u8BC6\u952E\u540D\uFF08\u5982 autocad, zwcad, mycad\uFF09" },
5601
+ key: { type: "string", description: "\u5B9E\u4F8B\u6807\u8BC6\u952E\u540D\uFF08\u5982 autocad, zwcad, mycad\uFF1B\u4E0D\u4F20\u65F6\u7531 pid \u81EA\u52A8\u6D3E\u751F\uFF09" },
5496
5602
  platform: { type: "string", description: "CAD \u5E73\u53F0: AutoCAD, ZWCAD, GStarCAD, BricsCAD\uFF08\u53EF\u9009\uFF0C\u81EA\u52A8\u68C0\u6D4B\uFF09" },
5603
+ pid: { type: "number", description: "\u76EE\u6807\u8FDB\u7A0B PID\uFF08\u53EF\u9009\uFF0C\u901A\u8FC7 ROT \u67E5\u627E\u540E\u8FDE\u63A5\uFF0C\u53EF\u4E0E pid \u81EA\u52A8\u6D3E\u751F key\uFF09" },
5497
5604
  label: { type: "string", description: "\u5B9E\u4F8B\u663E\u793A\u6807\u7B7E\uFF08\u53EF\u9009\uFF09" },
5498
5605
  makeActive: { type: "boolean", description: "\u8FDE\u63A5\u540E\u8BBE\u4E3A\u6D3B\u52A8\u5B9E\u4F8B\uFF08\u9ED8\u8BA4 true\uFF09" }
5499
- },
5500
- required: ["key"]
5606
+ }
5501
5607
  }
5502
5608
  },
5503
5609
  {
@@ -6457,7 +6563,66 @@ var init_debug_tools = __esm({
6457
6563
  }
6458
6564
  });
6459
6565
 
6566
+ // src/tools/concurrent-tools.js
6567
+ var tools42;
6568
+ var init_concurrent_tools = __esm({
6569
+ "src/tools/concurrent-tools.js"() {
6570
+ tools42 = [
6571
+ {
6572
+ name: "batch_concurrent_tasks",
6573
+ description: "\u5E76\u53D1\u6267\u884C\u591A\u5B9E\u4F8B\u5DE5\u5177 \u2014 \u5C06\u591A\u4E2A\u5DE5\u5177\u8C03\u7528\u540C\u65F6\u8DEF\u7531\u5230\u4E0D\u540C CAD \u5B9E\u4F8B\u5E76\u884C\u6267\u884C\u3002\u5185\u90E8\u4F7F\u7528 Promise.allSettled\uFF0C\u4E00\u4E2A\u5931\u8D25\u4E0D\u5F71\u54CD\u5176\u4ED6\u3002",
6574
+ inputSchema: {
6575
+ type: "object",
6576
+ properties: {
6577
+ tasks: {
6578
+ type: "array",
6579
+ items: {
6580
+ type: "object",
6581
+ properties: {
6582
+ tool: { type: "string", description: "\u5DE5\u5177\u540D\u79F0" },
6583
+ args: { type: "object", description: "\u5DE5\u5177\u53C2\u6570\uFF08\u4E0D\u542B instance\uFF09" },
6584
+ instance: { type: "string", description: "\u76EE\u6807\u5B9E\u4F8B key" }
6585
+ },
6586
+ required: ["tool", "instance"]
6587
+ },
6588
+ description: "\u8981\u5E76\u53D1\u6267\u884C\u7684\u4EFB\u52A1\u5217\u8868"
6589
+ }
6590
+ },
6591
+ required: ["tasks"]
6592
+ }
6593
+ }
6594
+ ];
6595
+ }
6596
+ });
6597
+
6460
6598
  // src/tools/index.js
6599
+ function getToolCategory(name) {
6600
+ const n = name;
6601
+ if (/^(draw_|create_3d_|create_box|create_cone|create_cylinder|create_sphere|create_torus|create_wedge|extrude_|revolve_|boolean_|slice_solid|thicken_|offset_surface|create_loft_|create_region)/.test(n)) return "\u7ED8\u56FE\u4E0E3D";
6602
+ if (/^(move_|copy_|rotate_|scale_|mirror_|offset_|array_|batch_)/.test(n)) return "\u4FEE\u6539";
6603
+ if (/^(trim_|extend_|fillet|chamfer|break_|join_|stretch)/.test(n)) return "\u7F16\u8F91";
6604
+ if (/^(create_layer|delete_layer|set_layer|layer_)/.test(n)) return "\u56FE\u5C42";
6605
+ if (/^(create_block|insert_block|explode_block|get_block|set_block|batch_get_block|batch_set_block)/.test(n)) return "\u5757";
6606
+ if (/^(create_dim|list_dim|set_dim|create_hatch|set_hatch)/.test(n)) return "\u6807\u6CE8\u4E0E\u586B\u5145";
6607
+ if (/^(create_ucs|set_ucs|delete_ucs|list_ucs|get_current_ucs|create_layout|delete_layout|rename_layout|set_layout|list_layouts|get_current_layout)/.test(n)) return "\u5750\u6807\u7CFB\u4E0E\u5E03\u5C40";
6608
+ if (/^(create_viewport|set_viewport|lock_viewport|unlock_viewport|maximize_viewport)/.test(n)) return "\u89C6\u53E3";
6609
+ if (/^find_/.test(n)) return "\u7A7A\u95F4\u67E5\u8BE2";
6610
+ if (/^(select_|get_entity|set_entity|delete_entity|get_bounding_box)/.test(n)) return "\u9009\u62E9\u4E0E\u5C5E\u6027";
6611
+ if (/^(open_dwg|save_dwg|export_|import_|close_dwg|publish_)/.test(n)) return "\u6587\u4EF6";
6612
+ if (/^(plot_|save_pdf|batch_plot|publish_layouts)/.test(n)) return "\u6253\u5370";
6613
+ if (/^(get_xdata|set_xdata|delete_xdata|list_xdata|copxy_xdata|search_by_xdata|export_xdata|import_xdata)/.test(n)) return "\u6269\u5C55\u6570\u636E";
6614
+ if (/^(create_sheet|open_sheet|list_sheet|add_sheet|delete_sheet|publish_sheet|sheet_|reorder_sheet|create_subset|list_subset|set_sheet)/.test(n)) return "\u56FE\u7EB8\u96C6";
6615
+ if (/^(attach_|detach_|reload_|list_attached|get_pdf|create_dgn|create_dwf|import_dxf|import_dgn|export_dgn|create_hyper|remove_hyper|xref|bind_xref|clip_xref|path_xref)/.test(n)) return "\u53C2\u7167\u4E0E\u5E95\u56FE";
6616
+ if (/^(create_text|create_mleader|create_linetype|list_text|list_linetype|list_mleader|load_linetype|create_dimension_style|set_dimstyle|list_dimension_styles|set_mleader_style)/.test(n)) return "\u6837\u5F0F";
6617
+ if (/^(purge_|drawing_audit|drawing_recover|overkill)/.test(n)) return "\u6E05\u7406";
6618
+ if (/^(find_text|replace_text|set_text_|justify_|convert_text|explode_text)/.test(n)) return "\u6587\u5B57";
6619
+ if (/^(eval_lisp|connect_cad|get_cad|get_platform|get_system|at_command|new_document|bring_to_front|install_atlisp|init_atlisp|get_system_status)/.test(n)) return "\u7CFB\u7EDF";
6620
+ if (/^(list_packages|search_packages|install_package|get_function_usage|list_symbols|import_funlib)/.test(n)) return "\u5305\u4E0E\u51FD\u6570";
6621
+ if (/^(watch_|lisp_repl|webhook_|sandbox_|audit_|cad_pool_|external_|list_data|export_to|import_external)/.test(n)) return "\u9AD8\u7EA7";
6622
+ if (/^(list_prompts|get_prompt|search_tools|list_tools_by_category|get_tool_info)/.test(n)) return "\u5143\u5DE5\u5177";
6623
+ if (/^(agent_)/.test(n)) return "Agent\u4E0A\u4E0B\u6587";
6624
+ return "\u5176\u4ED6";
6625
+ }
6461
6626
  function addAnnotations(tool) {
6462
6627
  const isDeprecated = DEPRECATED_TOOLS.has(tool.name);
6463
6628
  const isPoolTool = tool.name.startsWith("cad_pool_") || tool.name === "session_pin_instance" || tool.name === "session_unpin_instance";
@@ -6477,11 +6642,12 @@ function addAnnotations(tool) {
6477
6642
  destructiveHint: tool.name.startsWith("delete_") || tool.name.startsWith("purge_") || tool.name.startsWith("boolean_") || EXTRA_DESTRUCTIVE.has(tool.name),
6478
6643
  idempotentHint: READ_ONLY_TOOLS.has(tool.name) || IDEMPOTENT_WRITE_TOOLS.has(tool.name),
6479
6644
  openWorldHint: OPEN_WORLD_TOOLS.has(tool.name),
6645
+ category: getToolCategory(tool.name),
6480
6646
  ...isDeprecated ? { deprecatedReason: "This tool has been superseded. Check documentation for alternatives." } : {}
6481
6647
  }
6482
6648
  };
6483
6649
  }
6484
- var READ_ONLY_TOOLS, DEPRECATED_TOOLS, EXTRA_DESTRUCTIVE, IDEMPOTENT_WRITE_TOOLS, OPEN_WORLD_TOOLS, tools42, toolsByName;
6650
+ var READ_ONLY_TOOLS, DEPRECATED_TOOLS, EXTRA_DESTRUCTIVE, IDEMPOTENT_WRITE_TOOLS, OPEN_WORLD_TOOLS, tools43, toolsByName;
6485
6651
  var init_tools = __esm({
6486
6652
  "src/tools/index.js"() {
6487
6653
  init_cad_tools();
@@ -6525,6 +6691,7 @@ var init_tools = __esm({
6525
6691
  init_batch_transaction_tools();
6526
6692
  init_lint_tools();
6527
6693
  init_debug_tools();
6694
+ init_concurrent_tools();
6528
6695
  READ_ONLY_TOOLS = /* @__PURE__ */ new Set([
6529
6696
  "get_cad_info",
6530
6697
  "get_platform_info",
@@ -6664,7 +6831,7 @@ var init_tools = __esm({
6664
6831
  "import_page_setup",
6665
6832
  "load_linetype"
6666
6833
  ]);
6667
- tools42 = [
6834
+ tools43 = [
6668
6835
  ...tools,
6669
6836
  ...tools2,
6670
6837
  ...tools3,
@@ -6705,9 +6872,10 @@ var init_tools = __esm({
6705
6872
  ...tools39,
6706
6873
  ...tools37,
6707
6874
  ...tools40,
6708
- ...tools41
6875
+ ...tools41,
6876
+ ...tools42
6709
6877
  ].map(addAnnotations);
6710
- toolsByName = new Map(tools42.map((t) => [t.name, t]));
6878
+ toolsByName = new Map(tools43.map((t) => [t.name, t]));
6711
6879
  }
6712
6880
  });
6713
6881
 
@@ -6760,7 +6928,7 @@ function parseLispList(str) {
6760
6928
  });
6761
6929
  }
6762
6930
  function createMcpServer(handlers = {}) {
6763
- const { tools: tools43 = [], handleToolCall: handleToolCall2 = () => ({ content: [] }) } = handlers;
6931
+ const { tools: tools44 = [], handleToolCall: handleToolCall2 = () => ({ content: [] }) } = handlers;
6764
6932
  const server = new Server(
6765
6933
  { name: SERVER_NAME, version: SERVER_VERSION },
6766
6934
  {
@@ -6783,8 +6951,8 @@ function createMcpServer(handlers = {}) {
6783
6951
  const params = request.params || {};
6784
6952
  const cursor = decodeCursor(params.cursor);
6785
6953
  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;
6954
+ const page = tools44.slice(cursor, cursor + PAGE_SIZE);
6955
+ const nextCursor = cursor + PAGE_SIZE < tools44.length ? encodeCursor(cursor + PAGE_SIZE) : void 0;
6788
6956
  return { tools: page, nextCursor };
6789
6957
  });
6790
6958
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
@@ -7133,11 +7301,11 @@ function createMcpServer(handlers = {}) {
7133
7301
  if (p.toLowerCase().startsWith(prefix)) values.push(p);
7134
7302
  }
7135
7303
  } else if (ref?.type === "tool") {
7136
- for (const t of tools43) {
7304
+ for (const t of tools44) {
7137
7305
  if (t.name.toLowerCase().startsWith(prefix)) values.push(t.name);
7138
7306
  }
7139
7307
  } else if (ref?.type === "ref/parameter") {
7140
- const toolDef = tools43.find((t) => t.name === ref.name);
7308
+ const toolDef = tools44.find((t) => t.name === ref.name);
7141
7309
  if (toolDef?.inputSchema?.properties) {
7142
7310
  const paramName = argument.name;
7143
7311
  const paramDef = toolDef.inputSchema.properties[paramName];
@@ -10670,6 +10838,87 @@ var init_lint_handlers = __esm({
10670
10838
  }
10671
10839
  });
10672
10840
 
10841
+ // src/handlers/meta-handlers.js
10842
+ var meta_handlers_exports = {};
10843
+ __export(meta_handlers_exports, {
10844
+ get_prompt_handler: () => get_prompt_handler,
10845
+ get_tool_info: () => get_tool_info,
10846
+ list_prompts_handler: () => list_prompts_handler,
10847
+ list_tools_by_category: () => list_tools_by_category,
10848
+ search_tools: () => search_tools
10849
+ });
10850
+ async function search_tools(a) {
10851
+ const q = (a.query || "").toLowerCase();
10852
+ const results = tools43.filter((t) => t.name.toLowerCase().includes(q) || (t.description || "").toLowerCase().includes(q));
10853
+ const text = results.length ? `\u627E\u5230 ${results.length} \u4E2A\u5DE5\u5177:
10854
+ ${results.map((t) => `${t.name}: ${t.description}`).join("\n")}` : `\u672A\u627E\u5230\u5305\u542B "${a.query}" \u7684\u5DE5\u5177`;
10855
+ return { content: [{ type: "text", text }] };
10856
+ }
10857
+ async function list_tools_by_category(a) {
10858
+ const categories = {};
10859
+ for (const t of tools43) {
10860
+ const cat = t.annotations?.category || "\u5176\u4ED6";
10861
+ if (!categories[cat]) categories[cat] = [];
10862
+ categories[cat].push(t.name);
10863
+ }
10864
+ if (a.category) {
10865
+ const names = categories[a.category];
10866
+ return { content: [{ type: "text", text: names ? `${a.category} (${names.length}):
10867
+ ${names.join("\n")}` : `\u672A\u627E\u5230\u5206\u7C7B: ${a.category}` }] };
10868
+ }
10869
+ const lines = [];
10870
+ for (const [cat, names] of Object.entries(categories)) {
10871
+ lines.push(`## ${cat} (${names.length})`);
10872
+ }
10873
+ lines.push(`
10874
+ \u5171 ${Object.keys(categories).length} \u4E2A\u5206\u7C7B`);
10875
+ return { content: [{ type: "text", text: lines.join("\n") }] };
10876
+ }
10877
+ async function get_tool_info(a) {
10878
+ if (!a.name) return { content: [{ type: "text", text: "\u9700\u8981 name \u53C2\u6570\uFF08\u5DE5\u5177\u540D\u79F0\uFF09" }], isError: true };
10879
+ const tool = tools43.find((t) => t.name === a.name);
10880
+ if (!tool) return { content: [{ type: "text", text: `\u672A\u627E\u5230\u5DE5\u5177: ${a.name}` }], isError: true };
10881
+ const ann = tool.annotations || {};
10882
+ const required = (tool.inputSchema?.required || []).join(", ") || "\u65E0";
10883
+ const props = tool.inputSchema?.properties || {};
10884
+ const params = Object.entries(props).map(([k, v]) => {
10885
+ const req = required.includes(k) ? "\u5FC5\u586B" : "\u53EF\u9009";
10886
+ const desc = v.description || "";
10887
+ return ` ${k} (${req}): ${desc}`;
10888
+ }).join("\n");
10889
+ const lines = [
10890
+ `## ${tool.name}`,
10891
+ `\u63CF\u8FF0: ${tool.description || "\u65E0"}`,
10892
+ `\u5206\u7C7B: ${ann.category || "\u5176\u4ED6"}`,
10893
+ `\u53EA\u8BFB: ${ann.readOnlyHint ? "\u662F" : "\u5426"}`,
10894
+ `\u7834\u574F\u6027: ${ann.destructiveHint ? "\u662F" : "\u5426"}`,
10895
+ `\u5E42\u7B49: ${ann.idempotentHint ? "\u662F" : "\u5426"}`,
10896
+ `\u5F00\u653E\u4E16\u754C: ${ann.openWorldHint ? "\u662F" : "\u5426"}`,
10897
+ ``,
10898
+ `\u53C2\u6570:`,
10899
+ params || " \u65E0\u53C2\u6570"
10900
+ ];
10901
+ if (ann.deprecatedReason) {
10902
+ lines.push(`
10903
+ \u5DF2\u5F03\u7528: ${ann.deprecatedReason}`);
10904
+ }
10905
+ return { content: [{ type: "text", text: lines.join("\n") }] };
10906
+ }
10907
+ async function list_prompts_handler() {
10908
+ const prompts = await listPrompts2();
10909
+ return { content: [{ type: "text", text: JSON.stringify(prompts) }] };
10910
+ }
10911
+ async function get_prompt_handler(a) {
10912
+ const prompt = await getPrompt2(a.name, a.args || {});
10913
+ return { content: prompt.messages.map((m) => ({ type: "text", text: m.content.text || m.content })) };
10914
+ }
10915
+ var init_meta_handlers = __esm({
10916
+ "src/handlers/meta-handlers.js"() {
10917
+ init_tools();
10918
+ init_prompt_handlers();
10919
+ }
10920
+ });
10921
+
10673
10922
  // src/handlers/package-handlers.js
10674
10923
  var package_handlers_exports = {};
10675
10924
  __export(package_handlers_exports, {
@@ -10679,7 +10928,7 @@ __export(package_handlers_exports, {
10679
10928
  });
10680
10929
  async function fetchPackages() {
10681
10930
  const now = Date.now();
10682
- if (cachedPackages && cachedAt2 && now - cachedAt2 < CACHE_TTL2) {
10931
+ if (cachedPackages && cachedAt2 && now - cachedAt2 < CACHE_TTL) {
10683
10932
  return cachedPackages;
10684
10933
  }
10685
10934
  try {
@@ -10756,7 +11005,7 @@ async function installPackage(packageName) {
10756
11005
  }
10757
11006
  return { content: [{ type: "text", text: `\u5305 "${packageName}" \u5B89\u88C5\u547D\u4EE4\u5DF2\u53D1\u9001\uFF01` }] };
10758
11007
  }
10759
- var PACKAGES_LIST_URL, cachedPackages, cachedAt2, CACHE_TTL2;
11008
+ var PACKAGES_LIST_URL, cachedPackages, cachedAt2, CACHE_TTL;
10760
11009
  var init_package_handlers = __esm({
10761
11010
  "src/handlers/package-handlers.js"() {
10762
11011
  init_logger();
@@ -10765,7 +11014,7 @@ var init_package_handlers = __esm({
10765
11014
  PACKAGES_LIST_URL = process.env.PACKAGES_LIST_URL || "http://s3.atlisp.cn/packages-list?fmt=json";
10766
11015
  cachedPackages = null;
10767
11016
  cachedAt2 = null;
10768
- CACHE_TTL2 = 10 * 60 * 1e3;
11017
+ CACHE_TTL = 10 * 60 * 1e3;
10769
11018
  }
10770
11019
  });
10771
11020
 
@@ -12589,6 +12838,192 @@ var init_sheetset_handlers = __esm({
12589
12838
  }
12590
12839
  });
12591
12840
 
12841
+ // src/handlers/skill-handlers.js
12842
+ var skill_handlers_exports = {};
12843
+ __export(skill_handlers_exports, {
12844
+ skill_install: () => skill_install,
12845
+ skill_list_categories: () => skill_list_categories,
12846
+ skill_list_installed: () => skill_list_installed,
12847
+ skill_search: () => skill_search,
12848
+ skill_uninstall: () => skill_uninstall
12849
+ });
12850
+ import fs8 from "fs";
12851
+ import path12 from "path";
12852
+ import os7 from "os";
12853
+ function readCache() {
12854
+ try {
12855
+ if (fs8.existsSync(CACHE_PATH)) return JSON.parse(fs8.readFileSync(CACHE_PATH, "utf-8"));
12856
+ } catch {
12857
+ }
12858
+ return null;
12859
+ }
12860
+ function writeCache(data) {
12861
+ try {
12862
+ fs8.writeFileSync(CACHE_PATH, JSON.stringify(data, null, 2), "utf-8");
12863
+ } catch {
12864
+ }
12865
+ }
12866
+ async function fetchIndex() {
12867
+ let index = readCache();
12868
+ if (index && Date.now() - index.fetchedAt < 36e5) return index;
12869
+ try {
12870
+ const resp = await fetch(REGISTRY_URL, { headers: { "User-Agent": "atlisp-mcp" } });
12871
+ if (resp.ok) {
12872
+ index = await resp.json();
12873
+ index.fetchedAt = Date.now();
12874
+ writeCache(index);
12875
+ }
12876
+ } catch {
12877
+ }
12878
+ return index;
12879
+ }
12880
+ async function skill_search(a) {
12881
+ const index = await fetchIndex();
12882
+ if (!index?.skills) return { content: [{ type: "text", text: "\u65E0\u6CD5\u83B7\u53D6 skill \u6CE8\u518C\u8868\u7D22\u5F15" }], isError: true };
12883
+ const q = (a.query || "").toLowerCase();
12884
+ const results = index.skills.filter(
12885
+ (s) => s.name.toLowerCase().includes(q) || (s.description || "").toLowerCase().includes(q) || (s.tags || []).some((t) => t.toLowerCase().includes(q))
12886
+ );
12887
+ if (!results.length) return { content: [{ type: "text", text: `\u672A\u627E\u5230\u5339\u914D "${a.query}" \u7684 skill` }] };
12888
+ const lines = [`\u627E\u5230 ${results.length} \u4E2A skill:
12889
+ `];
12890
+ for (const s of results) {
12891
+ lines.push(`**${s.name}**`);
12892
+ if (s.description) lines.push(` \u63CF\u8FF0: ${s.description}`);
12893
+ if (s.tags?.length) lines.push(` \u6807\u7B7E: ${s.tags.join(", ")}`);
12894
+ if (s.version) lines.push(` \u7248\u672C: ${s.version}`);
12895
+ lines.push("");
12896
+ }
12897
+ return { content: [{ type: "text", text: lines.join("\n") }] };
12898
+ }
12899
+ async function skill_install(a) {
12900
+ if (!a.name) return { content: [{ type: "text", text: "\u9700\u8981 skill \u540D\u79F0" }], isError: true };
12901
+ const index = await fetchIndex();
12902
+ if (!index?.skills) return { content: [{ type: "text", text: "\u65E0\u6CD5\u83B7\u53D6 skill \u6CE8\u518C\u8868\u7D22\u5F15" }], isError: true };
12903
+ const skill = index.skills.find((s) => s.name === a.name);
12904
+ if (!skill) return { content: [{ type: "text", text: `\u672A\u627E\u5230 skill: ${a.name}` }], isError: true };
12905
+ const downloadUrl = skill.download_url || skill.url;
12906
+ if (!downloadUrl) return { content: [{ type: "text", text: `Skill "${a.name}" \u6CA1\u6709\u4E0B\u8F7D\u5730\u5740` }], isError: true };
12907
+ let content;
12908
+ try {
12909
+ const resp = await fetch(downloadUrl, { headers: { "User-Agent": "atlisp-mcp" } });
12910
+ if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
12911
+ content = await resp.text();
12912
+ } catch (e) {
12913
+ return { content: [{ type: "text", text: `\u4E0B\u8F7D\u5931\u8D25: ${e.message}` }], isError: true };
12914
+ }
12915
+ try {
12916
+ fs8.mkdirSync(SKILLS_DIR, { recursive: true });
12917
+ } catch {
12918
+ }
12919
+ const safeName = a.name.replace(/[^a-zA-Z0-9\u4e00-\u9fff_-]/g, "-").substring(0, 40);
12920
+ const skillFile = path12.join(SKILLS_DIR, `skill-${safeName}.md`);
12921
+ if (fs8.existsSync(skillFile)) {
12922
+ return { content: [{ type: "text", text: `Skill "${a.name}" \u5DF2\u5B89\u88C5 (${skillFile})` }], isError: true };
12923
+ }
12924
+ let finalContent = content;
12925
+ if (!content.match(/^---\n/)) {
12926
+ const tags = (skill.tags || []).join(", ");
12927
+ finalContent = `---
12928
+ name: ${a.name}
12929
+ description: ${skill.description || ""}
12930
+ tags: ${tags}
12931
+ ---
12932
+
12933
+ ${content}`;
12934
+ }
12935
+ fs8.writeFileSync(skillFile, finalContent, "utf-8");
12936
+ return { content: [{ type: "text", text: `${skill.name} \u5B89\u88C5\u6210\u529F
12937
+ \u6587\u4EF6: ${skillFile}${skill.version ? `
12938
+ \u7248\u672C: ${skill.version}` : ""}` }] };
12939
+ }
12940
+ async function skill_list_installed() {
12941
+ if (!fs8.existsSync(SKILLS_DIR)) return { content: [{ type: "text", text: "\u6682\u65E0\u5DF2\u5B89\u88C5\u7684 skill" }] };
12942
+ const results = [];
12943
+ try {
12944
+ const entries = fs8.readdirSync(SKILLS_DIR, { withFileTypes: true });
12945
+ for (const entry of entries) {
12946
+ if (entry.isFile() && entry.name.endsWith(".md")) {
12947
+ const fullPath = path12.join(SKILLS_DIR, entry.name);
12948
+ try {
12949
+ const raw = fs8.readFileSync(fullPath, "utf-8");
12950
+ const match = raw.match(/^---\n([\s\S]*?)\n---/);
12951
+ if (match) {
12952
+ const fm = {};
12953
+ for (const line of match[1].split("\n")) {
12954
+ const sep = line.indexOf(":");
12955
+ if (sep > 0) fm[line.slice(0, sep).trim()] = line.slice(sep + 1).trim();
12956
+ }
12957
+ results.push({ name: fm.name || entry.name.replace(".md", ""), description: fm.description || "", tags: fm.tags || "" });
12958
+ } else {
12959
+ results.push({ name: entry.name.replace(".md", ""), description: "", tags: "" });
12960
+ }
12961
+ } catch {
12962
+ }
12963
+ }
12964
+ }
12965
+ } catch {
12966
+ }
12967
+ if (!results.length) return { content: [{ type: "text", text: "\u6682\u65E0\u5DF2\u5B89\u88C5\u7684 skill" }] };
12968
+ const lines = [`\u5DF2\u5B89\u88C5 ${results.length} \u4E2A skill:
12969
+ `];
12970
+ for (const s of results) {
12971
+ lines.push(`**${s.name}**${s.description ? ` \u2014 ${s.description}` : ""}`);
12972
+ if (s.tags) lines.push(` \u6807\u7B7E: ${s.tags}`);
12973
+ }
12974
+ return { content: [{ type: "text", text: lines.join("\n") }] };
12975
+ }
12976
+ async function skill_uninstall(a) {
12977
+ if (!a.name) return { content: [{ type: "text", text: "\u9700\u8981 skill \u540D\u79F0" }], isError: true };
12978
+ if (!fs8.existsSync(SKILLS_DIR)) return { content: [{ type: "text", text: `\u672A\u5B89\u88C5 skill: ${a.name}` }], isError: true };
12979
+ let found = null;
12980
+ try {
12981
+ const entries = fs8.readdirSync(SKILLS_DIR, { withFileTypes: true });
12982
+ for (const entry of entries) {
12983
+ if (entry.isFile() && entry.name.endsWith(".md")) {
12984
+ const fullPath = path12.join(SKILLS_DIR, entry.name);
12985
+ try {
12986
+ const raw = fs8.readFileSync(fullPath, "utf-8");
12987
+ const match = raw.match(/^---\n([\s\S]*?)\n---/);
12988
+ if (match) {
12989
+ for (const line of match[1].split("\n")) {
12990
+ if (line.startsWith("name:") && line.slice(5).trim() === a.name) {
12991
+ found = fullPath;
12992
+ break;
12993
+ }
12994
+ }
12995
+ } else if (entry.name.replace(".md", "") === a.name) {
12996
+ found = fullPath;
12997
+ }
12998
+ } catch {
12999
+ }
13000
+ if (found) break;
13001
+ }
13002
+ }
13003
+ } catch {
13004
+ }
13005
+ if (!found) return { content: [{ type: "text", text: `\u672A\u5B89\u88C5 skill: ${a.name}` }], isError: true };
13006
+ fs8.unlinkSync(found);
13007
+ return { content: [{ type: "text", text: `${a.name} \u5DF2\u5378\u8F7D` }] };
13008
+ }
13009
+ async function skill_list_categories() {
13010
+ const index = await fetchIndex();
13011
+ if (!index?.skills) return { content: [{ type: "text", text: "\u65E0\u6CD5\u83B7\u53D6 skill \u6CE8\u518C\u8868\u7D22\u5F15" }], isError: true };
13012
+ const cats = /* @__PURE__ */ new Set();
13013
+ for (const s of index.skills) for (const t of s.tags || []) cats.add(t);
13014
+ const sorted = Array.from(cats).sort();
13015
+ return { content: [{ type: "text", text: sorted.length ? `\u6CE8\u518C\u8868\u5206\u7C7B (${sorted.length}):
13016
+ ${sorted.join("\n")}` : "\u6682\u65E0\u5206\u7C7B" }] };
13017
+ }
13018
+ var SKILLS_DIR, REGISTRY_URL, CACHE_PATH;
13019
+ var init_skill_handlers = __esm({
13020
+ "src/handlers/skill-handlers.js"() {
13021
+ SKILLS_DIR = path12.join(os7.homedir(), ".atlisp", "skills");
13022
+ REGISTRY_URL = process.env.SKILL_REGISTRY_URL || "https://raw.githubusercontent.com/atlisp/skill-registry/main/index.json";
13023
+ CACHE_PATH = path12.join(os7.homedir(), ".atlisp", "skill-registry-cache.json");
13024
+ }
13025
+ });
13026
+
12592
13027
  // src/handlers/spatial-handlers.js
12593
13028
  var spatial_handlers_exports = {};
12594
13029
  __export(spatial_handlers_exports, {
@@ -13960,13 +14395,13 @@ function lispPoint(pt) {
13960
14395
  }
13961
14396
  async function attachXref(args) {
13962
14397
  try {
13963
- const path20 = esc3(args.path);
14398
+ const path21 = esc3(args.path);
13964
14399
  const point = lispPoint(args.point);
13965
14400
  const scale = args.scale || 1;
13966
14401
  const rotation = args.rotation || 0;
13967
14402
  const overlay = args.overlay ? "_O" : "_A";
13968
14403
  const result = await cad.sendCommandWithResult(
13969
- `(command "_.XATTACH" "${path20}" "${overlay}" "${point}" "${scale}" "${rotation}") (princ)`
14404
+ `(command "_.XATTACH" "${path21}" "${overlay}" "${point}" "${scale}" "${rotation}") (princ)`
13970
14405
  );
13971
14406
  return mcpSuccess(`\u5916\u90E8\u53C2\u7167\u5DF2\u9644\u7740: ${args.path}`);
13972
14407
  } catch (e) {
@@ -14294,6 +14729,7 @@ var init_ = __esm({
14294
14729
  "./handlers/function-handlers.js": () => Promise.resolve().then(() => (init_function_handlers(), function_handlers_exports)),
14295
14730
  "./handlers/layer-handlers.js": () => Promise.resolve().then(() => (init_layer_handlers(), layer_handlers_exports)),
14296
14731
  "./handlers/lint-handlers.js": () => Promise.resolve().then(() => (init_lint_handlers(), lint_handlers_exports)),
14732
+ "./handlers/meta-handlers.js": () => Promise.resolve().then(() => (init_meta_handlers(), meta_handlers_exports)),
14297
14733
  "./handlers/package-handlers.js": () => Promise.resolve().then(() => (init_package_handlers(), package_handlers_exports)),
14298
14734
  "./handlers/parametric-handlers.js": () => Promise.resolve().then(() => (init_parametric_handlers(), parametric_handlers_exports)),
14299
14735
  "./handlers/pdf-annotation-handlers.js": () => Promise.resolve().then(() => (init_pdf_annotation_handlers(), pdf_annotation_handlers_exports)),
@@ -14302,6 +14738,7 @@ var init_ = __esm({
14302
14738
  "./handlers/query-print-handlers.js": () => Promise.resolve().then(() => (init_query_print_handlers(), query_print_handlers_exports)),
14303
14739
  "./handlers/resource-handlers.js": () => Promise.resolve().then(() => (init_resource_handlers(), resource_handlers_exports)),
14304
14740
  "./handlers/sheetset-handlers.js": () => Promise.resolve().then(() => (init_sheetset_handlers(), sheetset_handlers_exports)),
14741
+ "./handlers/skill-handlers.js": () => Promise.resolve().then(() => (init_skill_handlers(), skill_handlers_exports)),
14305
14742
  "./handlers/spatial-handlers.js": () => Promise.resolve().then(() => (init_spatial_handlers(), spatial_handlers_exports)),
14306
14743
  "./handlers/style-handlers.js": () => Promise.resolve().then(() => (init_style_handlers(), style_handlers_exports)),
14307
14744
  "./handlers/text-handlers.js": () => Promise.resolve().then(() => (init_text_handlers(), text_handlers_exports)),
@@ -14314,8 +14751,8 @@ var init_ = __esm({
14314
14751
  });
14315
14752
 
14316
14753
  // src/handler-loader.js
14317
- import fs8 from "fs";
14318
- import path12 from "path";
14754
+ import fs9 from "fs";
14755
+ import path13 from "path";
14319
14756
  import { fileURLToPath as fileURLToPath5 } from "url";
14320
14757
  function toolNameToFuncName(toolName) {
14321
14758
  if (FUNCTION_OVERRIDES[toolName]) return FUNCTION_OVERRIDES[toolName];
@@ -14365,10 +14802,10 @@ async function getHandlerModule(name) {
14365
14802
  async function initHandlerModules() {
14366
14803
  if (_handlerModulesInit) return;
14367
14804
  _handlerModulesInit = true;
14368
- const handlerDir = path12.join(__dirname5, "handlers");
14805
+ const handlerDir = path13.join(__dirname5, "handlers");
14369
14806
  let names;
14370
14807
  try {
14371
- names = await fs8.promises.readdir(handlerDir);
14808
+ names = await fs9.promises.readdir(handlerDir);
14372
14809
  } catch {
14373
14810
  log("Handler directory not found, using static manifest");
14374
14811
  return;
@@ -14379,6 +14816,7 @@ async function initHandlerModules() {
14379
14816
  handlerModules[name] = () => globImport_handlers_handlers_js(`./handlers/${name}-handlers.js`);
14380
14817
  }
14381
14818
  }
14819
+ watchHandlers();
14382
14820
  }
14383
14821
  async function getHandler(toolName) {
14384
14822
  const resolved = await resolveHandler(toolName);
@@ -14387,12 +14825,40 @@ async function getHandler(toolName) {
14387
14825
  if (!mod || typeof mod[resolved.funcName] !== "function") return null;
14388
14826
  return mod[resolved.funcName];
14389
14827
  }
14390
- var __dirname5, _handlerModulesInit, _handlerNameMapInit, handlerModules, moduleCache, resolvedCache, FUNCTION_OVERRIDES;
14828
+ function watchHandlers() {
14829
+ if (_watcherInitialized) return;
14830
+ _watcherInitialized = true;
14831
+ const handlerDir = path13.join(__dirname5, "handlers");
14832
+ let debounceTimer = null;
14833
+ try {
14834
+ if (!fs9.existsSync(handlerDir)) return;
14835
+ fs9.watch(handlerDir, (eventType, filename) => {
14836
+ if (!filename || !filename.endsWith("-handlers.js")) return;
14837
+ if (debounceTimer) clearTimeout(debounceTimer);
14838
+ debounceTimer = setTimeout(async () => {
14839
+ const name = filename.replace("-handlers.js", "");
14840
+ moduleCache.delete(name);
14841
+ resolvedCache.clear();
14842
+ _handlerNameMapInit = false;
14843
+ _handlerModulesInit = false;
14844
+ try {
14845
+ await initHandlerModules();
14846
+ log(`Handler hot-reloaded: ${filename}`);
14847
+ } catch (e) {
14848
+ log(`Handler reload failed for ${filename}: ${e.message}`);
14849
+ }
14850
+ }, 300);
14851
+ });
14852
+ } catch (e) {
14853
+ log(`Handler watch error: ${e.message}`);
14854
+ }
14855
+ }
14856
+ var __dirname5, _handlerModulesInit, _handlerNameMapInit, handlerModules, moduleCache, resolvedCache, FUNCTION_OVERRIDES, _watcherInitialized;
14391
14857
  var init_handler_loader = __esm({
14392
14858
  "src/handler-loader.js"() {
14393
14859
  init_logger();
14394
14860
  init_();
14395
- __dirname5 = path12.dirname(fileURLToPath5(import.meta.url));
14861
+ __dirname5 = path13.dirname(fileURLToPath5(import.meta.url));
14396
14862
  _handlerModulesInit = false;
14397
14863
  _handlerNameMapInit = false;
14398
14864
  handlerModules = {};
@@ -14404,16 +14870,18 @@ var init_handler_loader = __esm({
14404
14870
  get_dimension: "getDimensionValue",
14405
14871
  eval_lisp_with_result: "evalLisp"
14406
14872
  };
14873
+ _watcherInitialized = false;
14407
14874
  }
14408
14875
  });
14409
14876
 
14410
14877
  // 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")`;
14878
+ function escapeLispStr(str) {
14879
+ if (typeof str !== "string") return "";
14880
+ return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
14881
+ }
14882
+ async function streamEntities({ filterExpr, extractFields, itemMapper, offset, limit, hasFilter, warnThreshold, overLimitMsg, resultProp }) {
14883
+ const ssgetExpr = filterExpr ? `(ssget "_X" ${filterExpr})` : `(ssget "_X")`;
14884
+ const extractCode = extractFields.length === 1 ? `(list ${extractFields.join(" ")})` : `(list ${extractFields.join(" ")})`;
14417
14885
  const lisp = `(progn
14418
14886
  (setq ss ${ssgetExpr})
14419
14887
  (setq total (if ss (sslength ss) 0))
@@ -14425,10 +14893,10 @@ async function streamEntitiesByType({ type = null, layer = null, chunk = STREAM_
14425
14893
  (setq result (list total))
14426
14894
  (setq i ${offset})
14427
14895
  (setq count 0)
14428
- (while (and (< i total) (< count ${chunk}))
14896
+ (while (and (< i total) (< count ${limit}))
14429
14897
  (setq e (ssname ss i))
14430
14898
  (setq elist (entget e))
14431
- (setq result (cons (list (cdr (assoc 0 elist)) (cdr (assoc 5 elist)) (cdr (assoc 8 elist))) result))
14899
+ (setq result (cons ${extractCode} result))
14432
14900
  (setq i (1+ i))
14433
14901
  (setq count (1+ count)))
14434
14902
  (reverse result)))))`;
@@ -14442,108 +14910,68 @@ async function streamEntitiesByType({ type = null, layer = null, chunk = STREAM_
14442
14910
  offset: 0,
14443
14911
  count: 0,
14444
14912
  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`
14913
+ [resultProp]: [],
14914
+ error: `\u5B9E\u4F53\u6570\u91CF (${parsed[1]}) \u8D85\u8FC7\u6700\u5927\u9650\u5236 (${MAX_ENTITY_LIMIT})\uFF0C${overLimitMsg}`
14447
14915
  };
14448
14916
  }
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`;
14917
+ const items = parsed.slice(1).map(itemMapper);
14918
+ const result = { total, offset, count: items.length, hasMore: offset + items.length < total, [resultProp]: items };
14919
+ if (!hasFilter && total > warnThreshold) {
14920
+ result.warning = `\u672A\u63D0\u4F9B\u8FC7\u6EE4\u6761\u4EF6\uFF0C\u5168\u56FE\u626B\u63CF\u4E86 ${total} \u4E2A\u5B9E\u4F53\u3002\u5EFA\u8BAE\u6DFB\u52A0\u8FC7\u6EE4\u53C2\u6570\u4EE5\u63D0\u5347\u6027\u80FD\u3002`;
14459
14921
  }
14460
14922
  return result;
14461
14923
  } catch {
14462
- return { total: 0, offset: 0, count: 0, hasMore: false, entities: [] };
14924
+ return { total: 0, offset: 0, count: 0, hasMore: false, [resultProp]: [] };
14463
14925
  }
14464
14926
  }
14927
+ async function streamEntitiesByType({ type = null, layer = null, chunk = STREAM_CHUNK_SIZE, offset = 0 }) {
14928
+ const filters = [];
14929
+ if (type) filters.push(`(cons 0 "${escapeLispStr(type)}")`);
14930
+ if (layer) filters.push(`(cons 8 "${escapeLispStr(layer)}")`);
14931
+ const filterExpr = filters.length > 0 ? `(list ${filters.join(" ")})` : null;
14932
+ return streamEntities({
14933
+ filterExpr,
14934
+ extractFields: ["(list (cdr (assoc 0 elist)) (cdr (assoc 5 elist)) (cdr (assoc 8 elist)))"],
14935
+ itemMapper: (item) => ({ type: item[0], handle: item[1], layer: item[2] }),
14936
+ offset,
14937
+ limit: chunk,
14938
+ hasFilter: !!(type || layer),
14939
+ warnThreshold: UNFILTERED_WARN_THRESHOLD,
14940
+ overLimitMsg: "\u8BF7\u4F7F\u7528 type/layer \u8FC7\u6EE4",
14941
+ resultProp: "entities"
14942
+ });
14943
+ }
14465
14944
  async function streamTextContent({ layer = null, bbox = null, offset = 0, limit = 100 }) {
14466
14945
  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
- }
14946
+ if (layer) textFilters.push(`(cons 8 "${escapeLispStr(layer)}")`);
14947
+ const filterExpr = `(list '(-4 . "<OR") ${textFilters.join(" ")} '(-4 . "OR>"))`;
14948
+ return streamEntities({
14949
+ filterExpr,
14950
+ extractFields: ["(list (cdr (assoc 5 elist)) (cdr (assoc 1 elist)) (cdr (assoc 10 elist)))"],
14951
+ itemMapper: (item) => ({ handle: item[0], text: item[1], point: item[2] }),
14952
+ offset,
14953
+ limit,
14954
+ hasFilter: !!layer,
14955
+ warnThreshold: UNFILTERED_WARN_THRESHOLD,
14956
+ overLimitMsg: "\u8BF7\u4F7F\u7528 layer \u8FC7\u6EE4",
14957
+ resultProp: "texts"
14958
+ });
14506
14959
  }
14507
14960
  async function streamBlockRefs({ blockName = null, offset = 0, limit = 100 }) {
14508
14961
  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
- }
14962
+ if (blockName) filters.push(`(cons 2 "${escapeLispStr(blockName)}")`);
14963
+ const filterExpr = filters.length > 0 ? `(list ${filters.join(" ")})` : null;
14964
+ return streamEntities({
14965
+ filterExpr,
14966
+ extractFields: ["(list (cdr (assoc 2 elist)) (cdr (assoc 5 elist)) (cdr (assoc 10 elist)))"],
14967
+ itemMapper: (item) => ({ blockName: item[0], handle: item[1], insertionPoint: item[2] }),
14968
+ offset,
14969
+ limit,
14970
+ hasFilter: !!blockName,
14971
+ warnThreshold: UNFILTERED_WARN_THRESHOLD,
14972
+ overLimitMsg: "\u8BF7\u4F7F\u7528 blockName \u8FC7\u6EE4",
14973
+ resultProp: "refs"
14974
+ });
14547
14975
  }
14548
14976
  var STREAM_CHUNK_SIZE, MAX_ENTITY_LIMIT, UNFILTERED_WARN_THRESHOLD;
14549
14977
  var init_streaming = __esm({
@@ -14555,6 +14983,74 @@ var init_streaming = __esm({
14555
14983
  }
14556
14984
  });
14557
14985
 
14986
+ // src/cad-rot-helper.js
14987
+ import edge from "edge-js";
14988
+ import path14 from "path";
14989
+ import { fileURLToPath as fileURLToPath6 } from "url";
14990
+ function getRotHelper() {
14991
+ if (_rotHelper) return _rotHelper;
14992
+ const dllPath = path14.join(projectRoot, "dist", "cad-rothelper.dll");
14993
+ try {
14994
+ _rotHelper = edge.func({
14995
+ assemblyFile: dllPath,
14996
+ typeName: "CadRotHelper.RotEnumerator",
14997
+ methodName: "EnumerateAll"
14998
+ });
14999
+ return _rotHelper;
15000
+ } catch (e) {
15001
+ error(`ROT \u5E2E\u52A9\u5668\u52A0\u8F7D\u5931\u8D25: ${e.message}`);
15002
+ return null;
15003
+ }
15004
+ }
15005
+ async function discoverCadInstances() {
15006
+ const helper = getRotHelper();
15007
+ if (!helper) {
15008
+ log("ROT \u5E2E\u52A9\u5668\u4E0D\u53EF\u7528\uFF0C\u8DF3\u8FC7 CAD \u5B9E\u4F8B\u53D1\u73B0");
15009
+ return [];
15010
+ }
15011
+ return new Promise((resolve) => {
15012
+ helper(null, (err, result) => {
15013
+ if (err) {
15014
+ error(`ROT \u679A\u4E3E\u5931\u8D25: ${err.message}`);
15015
+ resolve([]);
15016
+ return;
15017
+ }
15018
+ if (!Array.isArray(result)) {
15019
+ resolve([]);
15020
+ return;
15021
+ }
15022
+ const seen = /* @__PURE__ */ new Set();
15023
+ const instances = [];
15024
+ for (const entry of result) {
15025
+ if (!entry.appName) continue;
15026
+ const pidKey = `${entry.appName}-${entry.pid}`;
15027
+ if (seen.has(pidKey)) continue;
15028
+ seen.add(pidKey);
15029
+ const key = `${entry.appName.toLowerCase()}-${entry.pid}`;
15030
+ instances.push({
15031
+ key,
15032
+ platform: entry.appName,
15033
+ pid: entry.pid,
15034
+ hwnd: entry.hwnd,
15035
+ version: entry.version,
15036
+ moniker: entry.name
15037
+ });
15038
+ }
15039
+ log(`ROT \u53D1\u73B0: ${instances.length} \u4E2A CAD \u5B9E\u4F8B (${instances.map((i) => `${i.platform}#${i.pid}`).join(", ")})`);
15040
+ resolve(instances);
15041
+ });
15042
+ });
15043
+ }
15044
+ var __filename, projectRoot, _rotHelper;
15045
+ var init_cad_rot_helper = __esm({
15046
+ "src/cad-rot-helper.js"() {
15047
+ init_logger();
15048
+ __filename = fileURLToPath6(import.meta.url);
15049
+ projectRoot = path14.dirname(path14.dirname(__filename));
15050
+ _rotHelper = null;
15051
+ }
15052
+ });
15053
+
14558
15054
  // src/cad-pool.js
14559
15055
  var POOL_STRATEGIES, CadConnectionPool, pool;
14560
15056
  var init_cad_pool = __esm({
@@ -14562,6 +15058,8 @@ var init_cad_pool = __esm({
14562
15058
  init_cad();
14563
15059
  init_logger();
14564
15060
  init_config();
15061
+ init_cad_rot_helper();
15062
+ init_resource_cache();
14565
15063
  POOL_STRATEGIES = {
14566
15064
  ROUND_ROBIN: "round-robin",
14567
15065
  LEAST_BUSY: "least-busy",
@@ -14570,9 +15068,8 @@ var init_cad_pool = __esm({
14570
15068
  CadConnectionPool = class {
14571
15069
  _instances = /* @__PURE__ */ new Map();
14572
15070
  _maxPoolSize = 3;
14573
- _activeKey = "default";
15071
+ _activeKey = null;
14574
15072
  _strategy = POOL_STRATEGIES.MANUAL;
14575
- _rrIndex = 0;
14576
15073
  _reconnectTimers = /* @__PURE__ */ new Map();
14577
15074
  _reconnectCounts = /* @__PURE__ */ new Map();
14578
15075
  _maxReconnectAttempts = 10;
@@ -14582,30 +15079,8 @@ var init_cad_pool = __esm({
14582
15079
  this._maxPoolSize = config_default.maxPoolSize || 3;
14583
15080
  }
14584
15081
  getConnection(key) {
14585
- this._ensureDefault();
14586
15082
  return this._instances.get(key)?.connection || null;
14587
15083
  }
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
15084
  /**
14610
15085
  * Connect to a CAD instance and add it to the pool.
14611
15086
  * @param {string} key - Instance identifier
@@ -14613,14 +15088,30 @@ var init_cad_pool = __esm({
14613
15088
  * @param {object} [options] - Additional options
14614
15089
  * @param {string} [options.label] - Human-readable label
14615
15090
  * @param {boolean} [options.autoReconnect=true] - Auto-reconnect on failure
15091
+ * @param {number} [options.pid] - Target process PID (find via ROT)
14616
15092
  * @param {boolean} [options.makeActive=true] - Set as active after connecting
14617
15093
  */
14618
15094
  async connect(key = "default", platform = null, options = {}) {
14619
- this._ensureDefault();
14620
- const { label, autoReconnect = true, makeActive = true } = options || {};
15095
+ const { label, autoReconnect = true, makeActive = true, pid } = options || {};
14621
15096
  if (this._instances.size >= this._maxPoolSize && !this._instances.has(key)) {
14622
15097
  throw new Error(`\u8FDE\u63A5\u6C60\u5DF2\u6EE1 (${this._maxPoolSize})\uFF0C\u65E0\u6CD5\u6DFB\u52A0\u65B0\u5B9E\u4F8B "${key}"`);
14623
15098
  }
15099
+ if (pid != null) {
15100
+ const instances = await discoverCadInstances();
15101
+ const match = instances.find((i) => i.pid === pid);
15102
+ if (!match) {
15103
+ error(`\u8FDE\u63A5\u6C60: \u672A\u627E\u5230 PID ${pid} \u7684 CAD \u5B9E\u4F8B`);
15104
+ return false;
15105
+ }
15106
+ platform = platform || match.platform;
15107
+ if (key === "default" || key === pid.toString()) {
15108
+ key = match.key;
15109
+ }
15110
+ if (match.moniker) {
15111
+ if (!options) options = {};
15112
+ options._moniker = match.moniker;
15113
+ }
15114
+ }
14624
15115
  let entry = this._instances.get(key);
14625
15116
  if (entry && entry.connected) {
14626
15117
  return true;
@@ -14642,7 +15133,9 @@ var init_cad_pool = __esm({
14642
15133
  }
14643
15134
  const conn = entry.connection;
14644
15135
  try {
14645
- const ok = await conn.connect(platform);
15136
+ const moniker = options._moniker || entry.moniker;
15137
+ const connectOpts = moniker ? { moniker, key } : {};
15138
+ const ok = await conn.connect(platform, connectOpts);
14646
15139
  if (ok) {
14647
15140
  entry.connected = true;
14648
15141
  entry.platform = conn.getPlatform();
@@ -14666,17 +15159,6 @@ var init_cad_pool = __esm({
14666
15159
  * Disconnect and remove an instance from the pool.
14667
15160
  */
14668
15161
  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
15162
  const entry = this._instances.get(key);
14681
15163
  if (!entry) return false;
14682
15164
  this._clearReconnectTimer(key);
@@ -14687,7 +15169,12 @@ var init_cad_pool = __esm({
14687
15169
  }
14688
15170
  this._instances.delete(key);
14689
15171
  if (this._activeKey === key) {
14690
- this.setActive("default");
15172
+ const remaining = Array.from(this._instances.keys());
15173
+ if (remaining.length > 0) {
15174
+ this.setActive(remaining[0]);
15175
+ } else {
15176
+ this._activeKey = null;
15177
+ }
14691
15178
  }
14692
15179
  log(`\u8FDE\u63A5\u6C60: \u5B9E\u4F8B "${key}" \u5DF2\u65AD\u5F00\u5E76\u79FB\u9664`);
14693
15180
  return true;
@@ -14696,7 +15183,6 @@ var init_cad_pool = __esm({
14696
15183
  * Set the active instance. All cad.* calls will be routed here.
14697
15184
  */
14698
15185
  setActive(key) {
14699
- this._ensureDefault();
14700
15186
  const entry = this._instances.get(key);
14701
15187
  if (!entry) {
14702
15188
  error(`\u8FDE\u63A5\u6C60: \u5B9E\u4F8B "${key}" \u4E0D\u5B58\u5728`);
@@ -14704,47 +15190,88 @@ var init_cad_pool = __esm({
14704
15190
  }
14705
15191
  this._activeKey = key;
14706
15192
  setActiveConnection(entry.connection);
15193
+ setInstanceKey(key);
14707
15194
  cad.connected = entry.connected;
14708
15195
  log(`\u8FDE\u63A5\u6C60: \u5DF2\u5207\u6362\u5230\u5B9E\u4F8B "${key}"`);
15196
+ if (this._onInstanceSwitch) {
15197
+ try {
15198
+ this._onInstanceSwitch(key, entry);
15199
+ } catch (e) {
15200
+ error(`\u5207\u6362\u56DE\u8C03\u7528\u9519\u8BEF: ${e.message}`);
15201
+ }
15202
+ }
14709
15203
  return true;
14710
15204
  }
15205
+ /**
15206
+ * Register a callback invoked after every instance switch.
15207
+ * @param {function} fn - (key, entry) => void
15208
+ */
15209
+ onInstanceSwitch(fn) {
15210
+ this._onInstanceSwitch = fn;
15211
+ }
14711
15212
  /**
14712
15213
  * Discover and add all running CAD instances.
14713
- * Tries each platform to find running CAD processes.
15214
+ * Uses ROT enumeration to find all instances (including multiple of the same platform).
15215
+ * Falls back to GetActiveObject per platform when ROT is unavailable.
14714
15216
  */
14715
15217
  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();
15218
+ const resultEntries = [];
15219
+ const rotInstances = await discoverCadInstances();
15220
+ const rotHadResults = rotInstances.length > 0;
15221
+ for (const inst of rotInstances) {
15222
+ const key = inst.key;
14721
15223
  if (this._instances.has(key) && this._instances.get(key).connected) {
14722
- foundCount++;
15224
+ resultEntries.push(this._instances.get(key));
14723
15225
  continue;
14724
15226
  }
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}`);
15227
+ this._instances.set(key, {
15228
+ key,
15229
+ connection: null,
15230
+ platform: inst.platform,
15231
+ pid: inst.pid,
15232
+ version: inst.version,
15233
+ moniker: inst.moniker,
15234
+ connected: false,
15235
+ lastPing: Date.now(),
15236
+ createdAt: Date.now(),
15237
+ label: `${inst.platform} #${inst.pid} ${inst.version || ""}`.trim(),
15238
+ autoReconnect: false
15239
+ });
15240
+ log(`\u8FDE\u63A5\u6C60: \u53D1\u73B0 ${inst.platform} \u5B9E\u4F8B PID=${inst.pid}`);
15241
+ resultEntries.push(this._instances.get(key));
15242
+ }
15243
+ if (!rotHadResults) {
15244
+ const connectWithTimeout = (conn, platform, ms = 4e3) => Promise.race([
15245
+ conn.connect(platform),
15246
+ new Promise((_, reject) => setTimeout(() => reject(new Error("timeout")), ms))
15247
+ ]);
15248
+ for (const platform of ["AutoCAD", "ZWCAD", "GStarCAD", "BricsCAD"]) {
15249
+ const key = platform.toLowerCase();
15250
+ if (this._instances.has(key) && this._instances.get(key).connected) continue;
15251
+ try {
15252
+ const tempConn = createCadConnection();
15253
+ const ok = await connectWithTimeout(tempConn, platform);
15254
+ if (ok) {
15255
+ const label = `${platform} ${tempConn.getVersion() || ""}`.trim();
15256
+ this._instances.set(key, {
15257
+ key,
15258
+ connection: tempConn,
15259
+ platform: tempConn.getPlatform(),
15260
+ version: tempConn.getVersion(),
15261
+ connected: true,
15262
+ lastPing: Date.now(),
15263
+ createdAt: Date.now(),
15264
+ label,
15265
+ autoReconnect: true
15266
+ });
15267
+ log(`\u8FDE\u63A5\u6C60: \u53D1\u73B0 ${label}`);
15268
+ resultEntries.push(this._instances.get(key));
15269
+ }
15270
+ } catch (e) {
14743
15271
  }
14744
- } catch (e) {
14745
15272
  }
14746
15273
  }
14747
- return Array.from(this._instances.values()).map((e) => ({
15274
+ return resultEntries.map((e) => ({
14748
15275
  key: e.key,
14749
15276
  connected: e.connected,
14750
15277
  platform: e.platform,
@@ -14757,7 +15284,6 @@ var init_cad_pool = __esm({
14757
15284
  * Health check for all instances.
14758
15285
  */
14759
15286
  async healthCheck() {
14760
- this._ensureDefault();
14761
15287
  const results = [];
14762
15288
  for (const [key, entry] of this._instances) {
14763
15289
  try {
@@ -14803,14 +15329,21 @@ var init_cad_pool = __esm({
14803
15329
  * Get the active instance.
14804
15330
  */
14805
15331
  getActive() {
14806
- this._ensureDefault();
14807
15332
  return this._instances.get(this._activeKey) || null;
14808
15333
  }
14809
15334
  /**
14810
15335
  * Get stats for all instances.
14811
15336
  */
14812
15337
  getStats() {
14813
- this._ensureDefault();
15338
+ if (this._instances.size === 0) {
15339
+ return {
15340
+ total: 0,
15341
+ maxPoolSize: this._maxPoolSize,
15342
+ active: null,
15343
+ strategy: this._strategy,
15344
+ instances: []
15345
+ };
15346
+ }
14814
15347
  return {
14815
15348
  total: this._instances.size,
14816
15349
  maxPoolSize: this._maxPoolSize,
@@ -14822,7 +15355,7 @@ var init_cad_pool = __esm({
14822
15355
  platform: i.platform,
14823
15356
  version: i.version,
14824
15357
  label: i.label,
14825
- queueSize: i.connection._getQueueSize ? i.connection._getQueueSize() : 0,
15358
+ queueSize: i.connection?._getQueueSize ? i.connection._getQueueSize() : 0,
14826
15359
  lastPingAgo: Date.now() - (i.lastPing || 0),
14827
15360
  uptime: i.connected ? Math.round((Date.now() - i.createdAt) / 1e3) : 0
14828
15361
  }))
@@ -14832,7 +15365,7 @@ var init_cad_pool = __esm({
14832
15365
  * List all instances.
14833
15366
  */
14834
15367
  listInstances() {
14835
- this._ensureDefault();
15368
+ if (this._instances.size === 0) return [];
14836
15369
  return Array.from(this._instances.values()).map((i) => ({
14837
15370
  key: i.key,
14838
15371
  connected: i.connected,
@@ -14844,7 +15377,7 @@ var init_cad_pool = __esm({
14844
15377
  }));
14845
15378
  }
14846
15379
  /**
14847
- * Disconnect all instances.
15380
+ * Disconnect all instances in parallel.
14848
15381
  */
14849
15382
  async disconnectAll() {
14850
15383
  this.stopHealthCheck();
@@ -14853,27 +15386,16 @@ var init_cad_pool = __esm({
14853
15386
  }
14854
15387
  this._reconnectTimers.clear();
14855
15388
  this._reconnectCounts.clear();
14856
- for (const [key, entry] of this._instances) {
15389
+ const entries = Array.from(this._instances.entries());
15390
+ this._instances.clear();
15391
+ this._activeKey = null;
15392
+ await Promise.allSettled(entries.map(async ([key, entry]) => {
14857
15393
  try {
14858
15394
  await entry.connection.disconnect();
14859
15395
  } catch (e) {
14860
15396
  error(`\u8FDE\u63A5\u6C60: \u65AD\u5F00 "${key}" \u51FA\u9519: ${e.message}`);
14861
15397
  }
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
- });
15398
+ }));
14877
15399
  log("\u8FDE\u63A5\u6C60: \u6240\u6709\u5B9E\u4F8B\u5DF2\u65AD\u5F00");
14878
15400
  }
14879
15401
  // --- Internal ---
@@ -14933,7 +15455,7 @@ var init_cad_pool = __esm({
14933
15455
  });
14934
15456
 
14935
15457
  // src/trigger-engine.js
14936
- function escapeLispStr(v) {
15458
+ function escapeLispStr2(v) {
14937
15459
  if (typeof v !== "string") return v;
14938
15460
  return v.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
14939
15461
  }
@@ -14941,10 +15463,10 @@ function buildFilterExpr(filter) {
14941
15463
  if (!filter || typeof filter !== "object") return "nil";
14942
15464
  const parts = [];
14943
15465
  if (filter.type) {
14944
- parts.push(`(cons 0 "${escapeLispStr(filter.type)}")`);
15466
+ parts.push(`(cons 0 "${escapeLispStr2(filter.type)}")`);
14945
15467
  }
14946
15468
  if (filter.layer) {
14947
- parts.push(`(cons 8 "${escapeLispStr(filter.layer)}")`);
15469
+ parts.push(`(cons 8 "${escapeLispStr2(filter.layer)}")`);
14948
15470
  }
14949
15471
  if (parts.length === 0) return "nil";
14950
15472
  return `(list ${parts.join(" ")})`;
@@ -14993,7 +15515,7 @@ async function evaluateWatch(watch) {
14993
15515
  return { matched, data: { count } };
14994
15516
  }
14995
15517
  case "layer_exists": {
14996
- const lName = escapeLispStr(watch.layerName || "");
15518
+ const lName = escapeLispStr2(watch.layerName || "");
14997
15519
  result = await cad.sendCommandWithResult(
14998
15520
  `(progn (if (tblsearch "LAYER" "${lName}") (list 1) (list 0)))`
14999
15521
  );
@@ -15001,7 +15523,7 @@ async function evaluateWatch(watch) {
15001
15523
  return { matched: exists, data: { exists, layerName: watch.layerName } };
15002
15524
  }
15003
15525
  case "doc_name": {
15004
- const pattern = escapeLispStr(watch.pattern || "*");
15526
+ const pattern = escapeLispStr2(watch.pattern || "*");
15005
15527
  result = await cad.sendCommandWithResult(
15006
15528
  `(progn (setq _dn (getvar "dwgname")) (setq _dm (if (wcmatch _dn "${pattern}") 1 0)) (list _dm _dn))`
15007
15529
  );
@@ -15011,14 +15533,14 @@ async function evaluateWatch(watch) {
15011
15533
  return { matched, data: { matched: Boolean(matched), name } };
15012
15534
  }
15013
15535
  case "doc_path": {
15014
- const pattern = escapeLispStr(watch.pattern || "*");
15536
+ const pattern = escapeLispStr2(watch.pattern || "*");
15015
15537
  result = await cad.sendCommandWithResult(
15016
15538
  `(progn (setq _dp (getvar "dwgprefix")) (setq _dm (if (wcmatch _dp "${pattern}") 1 0)) (list _dm _dp))`
15017
15539
  );
15018
15540
  const arr = parseLispList2(result) || [];
15019
15541
  const matched = Number(arr[0]) !== 0;
15020
- const path20 = String(arr[1] || "");
15021
- return { matched, data: { matched: Boolean(matched), path: path20 } };
15542
+ const path21 = String(arr[1] || "");
15543
+ return { matched, data: { matched: Boolean(matched), path: path21 } };
15022
15544
  }
15023
15545
  case "entity_selected": {
15024
15546
  result = await cad.sendCommandWithResult(
@@ -15188,53 +15710,6 @@ function stripStringsAndComments(code) {
15188
15710
  const withoutStrings = code.replace(/"([^"\\]*(?:\\.[^"\\]*)*)"/g, "");
15189
15711
  return withoutStrings.replace(/;.*$/gm, "");
15190
15712
  }
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
15713
  function getSessionAllowlist(sessionId) {
15239
15714
  if (!sessionAllowlist.has(sessionId)) {
15240
15715
  sessionAllowlist.set(sessionId, /* @__PURE__ */ new Set());
@@ -15278,12 +15753,49 @@ function validateLispCode(code, sessionId = null) {
15278
15753
  summary: blocked ? `\u5B89\u5168\u9650\u5236: ${results.filter((r) => r.blocked).map((r) => r.message).join("; ")}` : "\u901A\u8FC7"
15279
15754
  };
15280
15755
  }
15281
- var RULES2, sessionAllowlist;
15282
- var init_lisp_sandbox = __esm({
15283
- "src/lisp-sandbox.js"() {
15756
+ var THREAT_BLOCK, THREAT_WARN, THREAT_ALLOW, CVT, SECURITY_RULES, _cachedLevel, RULES2, sessionAllowlist;
15757
+ var init_lisp_security = __esm({
15758
+ "src/lisp-security.js"() {
15284
15759
  init_logger();
15285
15760
  init_config();
15286
- init_lisp_security();
15761
+ THREAT_BLOCK = "block";
15762
+ THREAT_WARN = "warn";
15763
+ THREAT_ALLOW = "allow";
15764
+ CVT = {
15765
+ strict: THREAT_BLOCK,
15766
+ standard: THREAT_WARN,
15767
+ relaxed: THREAT_ALLOW
15768
+ };
15769
+ SECURITY_RULES = [
15770
+ { re: /\bstartapp\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 startapp \u51FD\u6570" },
15771
+ { re: /\bvl-bt\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 vl-bt \u51FD\u6570" },
15772
+ { re: /\bvlax-import-type-library\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u5BFC\u5165\u7C7B\u578B\u5E93" },
15773
+ { re: /\bdos_command_string\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 dos_command_string" },
15774
+ { re: /\beval\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 eval \u51FD\u6570" },
15775
+ { re: /\bcommand\s*["\s]*[_(]?\s*["']?\.?\s*_?(?:shell|quit|exit|close|open|new|recover|audit|purge|wblock|export)/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528\u5371\u9669 CAD \u547D\u4EE4" },
15776
+ { re: /\bload\s*[(\s"]/i, baseSeverity: THREAT_BLOCK, msg: "load \u51FD\u6570\u53EF\u80FD\u52A0\u8F7D\u5916\u90E8\u4EE3\u7801" },
15777
+ { re: /\b(?:read-line|read-char|write-line|write-char)\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u6587\u4EF6 I/O \u64CD\u4F5C\u53EF\u80FD\u5F71\u54CD\u7CFB\u7EDF" },
15778
+ { re: /\bvl-file-copy\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u6587\u4EF6\u590D\u5236" },
15779
+ { re: /\bvl-file-delete\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u6587\u4EF6\u5220\u9664" },
15780
+ { re: /\bvl-file-rename\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u6587\u4EF6\u91CD\u547D\u540D" },
15781
+ { re: /\bvl-registry-write[\s\)]/i, baseSeverity: THREAT_BLOCK, msg: "vl-registry-write \u4F1A\u4FEE\u6539\u6CE8\u518C\u8868" },
15782
+ { re: /\bvl-exit-with-value\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 vl-exit-with-value" },
15783
+ { re: /\bvl-exit-with-error\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 vl-exit-with-error" },
15784
+ { re: /\bvlax-add-cmd\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u6CE8\u518C\u65B0\u547D\u4EE4" },
15785
+ { re: /\bvla-Delete\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u76F4\u63A5\u5220\u9664 vla \u5BF9\u8C61" },
15786
+ { re: /\bvla-Save\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u76F4\u63A5\u4FDD\u5B58\u6587\u6863" },
15787
+ { re: /\bdirectory\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 directory \u51FD\u6570" },
15788
+ { re: /\bvla-deletefile\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u5220\u9664\u6587\u4EF6" },
15789
+ { re: /\bvla-copyfile\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u590D\u5236\u6587\u4EF6" },
15790
+ { re: /\bvla-movefile\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u79FB\u52A8\u6587\u4EF6" }
15791
+ ];
15792
+ _cachedLevel = null;
15793
+ try {
15794
+ onConfigChange(() => {
15795
+ _cachedLevel = null;
15796
+ });
15797
+ } catch (_) {
15798
+ }
15287
15799
  RULES2 = SECURITY_RULES;
15288
15800
  sessionAllowlist = /* @__PURE__ */ new Map();
15289
15801
  }
@@ -15404,13 +15916,13 @@ ${analysis.errors.map((e) => ` L${e.line}: [${e.rule}] ${e.message}`).join("\n"
15404
15916
  });
15405
15917
 
15406
15918
  // src/webhook-engine.js
15407
- import fs9 from "fs";
15408
- import path13 from "path";
15409
- import os7 from "os";
15919
+ import fs10 from "fs";
15920
+ import path15 from "path";
15921
+ import os8 from "os";
15410
15922
  function loadWebhooks() {
15411
15923
  try {
15412
- if (fs9.existsSync(WEBHOOKS_FILE)) {
15413
- webhooks = JSON.parse(fs9.readFileSync(WEBHOOKS_FILE, "utf-8"));
15924
+ if (fs10.existsSync(WEBHOOKS_FILE)) {
15925
+ webhooks = JSON.parse(fs10.readFileSync(WEBHOOKS_FILE, "utf-8"));
15414
15926
  }
15415
15927
  } catch (e) {
15416
15928
  error(`Webhook load error: ${e.message}`);
@@ -15418,8 +15930,8 @@ function loadWebhooks() {
15418
15930
  }
15419
15931
  function saveWebhooks() {
15420
15932
  try {
15421
- ensureDir(path13.dirname(WEBHOOKS_FILE));
15422
- fs9.writeFileSync(WEBHOOKS_FILE, JSON.stringify(webhooks, null, 2), "utf-8");
15933
+ ensureDir(path15.dirname(WEBHOOKS_FILE));
15934
+ fs10.writeFileSync(WEBHOOKS_FILE, JSON.stringify(webhooks, null, 2), "utf-8");
15423
15935
  } catch (e) {
15424
15936
  error(`Webhook save error: ${e.message}`);
15425
15937
  }
@@ -15476,10 +15988,10 @@ function listWebhooks() {
15476
15988
  }
15477
15989
  function getWebhookLog(id) {
15478
15990
  const sanitized = String(id).replace(/[^a-zA-Z0-9_-]/g, "_");
15479
- const logPath = path13.join(os7.homedir(), ".atlisp", `webhook-${sanitized}.log`);
15991
+ const logPath = path15.join(os8.homedir(), ".atlisp", `webhook-${sanitized}.log`);
15480
15992
  try {
15481
- if (!fs9.existsSync(logPath)) return [];
15482
- return fs9.readFileSync(logPath, "utf-8").split("\n").filter(Boolean).map((l) => JSON.parse(l));
15993
+ if (!fs10.existsSync(logPath)) return [];
15994
+ return fs10.readFileSync(logPath, "utf-8").split("\n").filter(Boolean).map((l) => JSON.parse(l));
15483
15995
  } catch (e) {
15484
15996
  error(`Webhook log read error [${sanitized}]: ${e.message}`);
15485
15997
  return [];
@@ -15490,30 +16002,30 @@ var init_webhook_engine = __esm({
15490
16002
  "src/webhook-engine.js"() {
15491
16003
  init_logger();
15492
16004
  init_handler_utils();
15493
- WEBHOOKS_FILE = path13.join(os7.homedir(), ".atlisp", "webhooks.json");
16005
+ WEBHOOKS_FILE = path15.join(os8.homedir(), ".atlisp", "webhooks.json");
15494
16006
  webhooks = [];
15495
16007
  loadWebhooks();
15496
16008
  }
15497
16009
  });
15498
16010
 
15499
16011
  // src/audit-log.js
15500
- import fs10 from "fs";
15501
- import path14 from "path";
15502
- import os8 from "os";
16012
+ import fs11 from "fs";
16013
+ import path16 from "path";
16014
+ import os9 from "os";
15503
16015
  import zlib from "zlib";
15504
16016
  function rotateIfNeeded() {
15505
16017
  try {
15506
- if (fs10.existsSync(AUDIT_FILE) && fs10.statSync(AUDIT_FILE).size > MAX_LOG_SIZE2) {
16018
+ if (fs11.existsSync(AUDIT_FILE) && fs11.statSync(AUDIT_FILE).size > MAX_LOG_SIZE2) {
15507
16019
  const rotated = AUDIT_FILE.replace(".log", `.${Date.now()}.log`);
15508
- fs10.renameSync(AUDIT_FILE, rotated);
16020
+ fs11.renameSync(AUDIT_FILE, rotated);
15509
16021
  try {
15510
- const compressed = zlib.gzipSync(fs10.readFileSync(rotated));
15511
- fs10.writeFileSync(rotated + ".gz", compressed);
15512
- fs10.unlinkSync(rotated);
16022
+ const compressed = zlib.gzipSync(fs11.readFileSync(rotated));
16023
+ fs11.writeFileSync(rotated + ".gz", compressed);
16024
+ fs11.unlinkSync(rotated);
15513
16025
  } catch (e) {
15514
16026
  error(`Audit log compression failed, keeping raw file: ${e.message}`);
15515
16027
  try {
15516
- fs10.unlinkSync(rotated + ".gz");
16028
+ fs11.unlinkSync(rotated + ".gz");
15517
16029
  } catch {
15518
16030
  }
15519
16031
  }
@@ -15537,15 +16049,15 @@ function writeAuditEntry(entry) {
15537
16049
  user: entry.user || "",
15538
16050
  role: entry.role || ""
15539
16051
  }) + "\n";
15540
- fs10.appendFileSync(AUDIT_FILE, line, "utf-8");
16052
+ fs11.appendFileSync(AUDIT_FILE, line, "utf-8");
15541
16053
  } catch (e) {
15542
16054
  error(`Audit log write error: ${e.message}`);
15543
16055
  }
15544
16056
  }
15545
16057
  function queryAuditLog(filters = {}) {
15546
16058
  try {
15547
- if (!fs10.existsSync(AUDIT_FILE)) return { total: 0, offset: 0, count: 0, entries: [] };
15548
- const content = fs10.readFileSync(AUDIT_FILE, "utf-8");
16059
+ if (!fs11.existsSync(AUDIT_FILE)) return { total: 0, offset: 0, count: 0, entries: [] };
16060
+ const content = fs11.readFileSync(AUDIT_FILE, "utf-8");
15549
16061
  const lines = content.trim().split("\n").filter(Boolean);
15550
16062
  let entries = lines.map((l) => {
15551
16063
  try {
@@ -15577,8 +16089,8 @@ function queryAuditLog(filters = {}) {
15577
16089
  }
15578
16090
  function getAuditStats() {
15579
16091
  try {
15580
- if (!fs10.existsSync(AUDIT_FILE)) return { totalEntries: 0, tools: {}, errors: 0 };
15581
- const content = fs10.readFileSync(AUDIT_FILE, "utf-8");
16092
+ if (!fs11.existsSync(AUDIT_FILE)) return { totalEntries: 0, tools: {}, errors: 0 };
16093
+ const content = fs11.readFileSync(AUDIT_FILE, "utf-8");
15582
16094
  const lines = content.trim().split("\n").filter(Boolean);
15583
16095
  const toolCounts = {};
15584
16096
  let errors = 0;
@@ -15594,7 +16106,7 @@ function getAuditStats() {
15594
16106
  totalEntries: lines.length,
15595
16107
  tools: toolCounts,
15596
16108
  errors,
15597
- fileSize: fs10.existsSync(AUDIT_FILE) ? fs10.statSync(AUDIT_FILE).size : 0,
16109
+ fileSize: fs11.existsSync(AUDIT_FILE) ? fs11.statSync(AUDIT_FILE).size : 0,
15598
16110
  filePath: AUDIT_FILE
15599
16111
  };
15600
16112
  } catch (e) {
@@ -15606,8 +16118,8 @@ var init_audit_log = __esm({
15606
16118
  "src/audit-log.js"() {
15607
16119
  init_logger();
15608
16120
  init_handler_utils();
15609
- AUDIT_DIR = path14.join(os8.homedir(), "@lisp", "logs");
15610
- AUDIT_FILE = path14.join(AUDIT_DIR, "audit.log");
16121
+ AUDIT_DIR = path16.join(os9.homedir(), "@lisp", "logs");
16122
+ AUDIT_FILE = path16.join(AUDIT_DIR, "audit.log");
15611
16123
  MAX_LOG_SIZE2 = 10 * 1024 * 1024;
15612
16124
  MAX_LOG_AGE = 90 * 24 * 60 * 60 * 1e3;
15613
16125
  }
@@ -15641,14 +16153,17 @@ var init_agent_context = __esm({
15641
16153
  });
15642
16154
 
15643
16155
  // src/tool-validators.js
15644
- function validateProps(args, props, required, path20 = "") {
16156
+ function validationError(msg) {
16157
+ return new MCPError(ERROR_CODES.INVALID_ARGS, msg);
16158
+ }
16159
+ function validateProps(args, props, required, path21 = "") {
15645
16160
  for (const [key, prop] of Object.entries(props)) {
15646
- const fullKey = path20 ? `${path20}.${key}` : key;
16161
+ const fullKey = path21 ? `${path21}.${key}` : key;
15647
16162
  const isOptional = !required.includes(key);
15648
16163
  const val = args[key];
15649
16164
  if (val === void 0 || val === null) {
15650
16165
  if (isOptional) continue;
15651
- throw new Error(`Missing required argument: ${fullKey}`);
16166
+ throw validationError(`Missing required argument: ${fullKey}`);
15652
16167
  }
15653
16168
  if (prop.type === "object" && prop.properties && typeof val === "object" && !Array.isArray(val)) {
15654
16169
  const subRequired = prop.required || [];
@@ -15663,46 +16178,46 @@ function validateProps(args, props, required, path20 = "") {
15663
16178
  const actualType = typeof val;
15664
16179
  const expectedType = prop.type || "string";
15665
16180
  if (expectedType === "string" && actualType !== "string" && actualType !== "number") {
15666
- throw new Error(`Invalid type for argument ${fullKey}: expected string, got ${actualType}`);
16181
+ throw validationError(`Invalid type for argument ${fullKey}: expected string, got ${actualType}`);
15667
16182
  }
15668
16183
  if ((expectedType === "object" || expectedType === "array") && (actualType !== "object" || val === null)) {
15669
- throw new Error(`Invalid type for argument ${fullKey}: expected ${expectedType}, got ${actualType}`);
16184
+ throw validationError(`Invalid type for argument ${fullKey}: expected ${expectedType}, got ${actualType}`);
15670
16185
  }
15671
16186
  if (expectedType === "array" && !Array.isArray(val)) {
15672
- throw new Error(`Invalid type for argument ${fullKey}: expected array, got ${actualType}`);
16187
+ throw validationError(`Invalid type for argument ${fullKey}: expected array, got ${actualType}`);
15673
16188
  }
15674
16189
  if (expectedType === "object" && Array.isArray(val)) {
15675
- throw new Error(`Invalid type for argument ${fullKey}: expected object, got array`);
16190
+ throw validationError(`Invalid type for argument ${fullKey}: expected object, got array`);
15676
16191
  }
15677
16192
  if ((expectedType === "number" || expectedType === "integer") && actualType !== "number") {
15678
16193
  if (expectedType === "integer" && !Number.isInteger(val)) {
15679
- throw new Error(`Invalid type for argument ${fullKey}: expected integer, got ${actualType}`);
16194
+ throw validationError(`Invalid type for argument ${fullKey}: expected integer, got ${actualType}`);
15680
16195
  }
15681
16196
  if (expectedType === "number") {
15682
- throw new Error(`Invalid type for argument ${fullKey}: expected number, got ${actualType}`);
16197
+ throw validationError(`Invalid type for argument ${fullKey}: expected number, got ${actualType}`);
15683
16198
  }
15684
16199
  }
15685
16200
  if (expectedType === "boolean" && actualType !== "boolean") {
15686
- throw new Error(`Invalid type for argument ${fullKey}: expected boolean, got ${actualType}`);
16201
+ throw validationError(`Invalid type for argument ${fullKey}: expected boolean, got ${actualType}`);
15687
16202
  }
15688
16203
  if (prop.enum && !prop.enum.includes(val)) {
15689
- throw new Error(`Invalid value for argument ${fullKey}: must be one of ${prop.enum.join(", ")}`);
16204
+ throw validationError(`Invalid value for argument ${fullKey}: must be one of ${prop.enum.join(", ")}`);
15690
16205
  }
15691
16206
  if (expectedType === "string" && typeof prop.maxLength === "number" && String(val).length > prop.maxLength) {
15692
- throw new Error(`Argument ${fullKey} exceeds max length of ${prop.maxLength}`);
16207
+ throw validationError(`Argument ${fullKey} exceeds max length of ${prop.maxLength}`);
15693
16208
  }
15694
16209
  if ((expectedType === "number" || expectedType === "integer") && prop.minimum != null && val < prop.minimum) {
15695
- throw new Error(`Argument ${fullKey} must be >= ${prop.minimum}`);
16210
+ throw validationError(`Argument ${fullKey} must be >= ${prop.minimum}`);
15696
16211
  }
15697
16212
  if ((expectedType === "number" || expectedType === "integer") && prop.maximum != null && val > prop.maximum) {
15698
- throw new Error(`Argument ${fullKey} must be <= ${prop.maximum}`);
16213
+ throw validationError(`Argument ${fullKey} must be <= ${prop.maximum}`);
15699
16214
  }
15700
16215
  }
15701
16216
  }
15702
16217
  function validateToolArgs(name, args) {
15703
16218
  const toolDef = toolsByName.get(name);
15704
16219
  if (!toolDef) {
15705
- throw new Error(`Unknown tool: ${name}`);
16220
+ throw validationError(`Unknown tool: ${name}`);
15706
16221
  }
15707
16222
  const schema = toolDef.inputSchema;
15708
16223
  if (!schema || !schema.properties) return true;
@@ -15710,7 +16225,7 @@ function validateToolArgs(name, args) {
15710
16225
  const required = schema.required || [];
15711
16226
  for (const req of required) {
15712
16227
  if (!(req in (args || {}))) {
15713
- throw new Error(`Missing required argument: ${req}`);
16228
+ throw validationError(`Missing required argument: ${req}`);
15714
16229
  }
15715
16230
  }
15716
16231
  validateProps(args || {}, props, required);
@@ -15719,6 +16234,7 @@ function validateToolArgs(name, args) {
15719
16234
  var init_tool_validators = __esm({
15720
16235
  "src/tool-validators.js"() {
15721
16236
  init_tools();
16237
+ init_errors();
15722
16238
  }
15723
16239
  });
15724
16240
 
@@ -15829,7 +16345,7 @@ function registerTool(toolDef, handler) {
15829
16345
  }
15830
16346
  customTools.push(toolDef);
15831
16347
  customHandlers.set(toolDef.name, handler);
15832
- tools42.push(toolDef);
16348
+ tools43.push(toolDef);
15833
16349
  return true;
15834
16350
  }
15835
16351
  function callCustomTool(name, args) {
@@ -15849,73 +16365,92 @@ var init_tool_registry = __esm({
15849
16365
  });
15850
16366
 
15851
16367
  // src/tool-executor.js
16368
+ function resolveConn(instance) {
16369
+ if (instance) return pool.getConnection(instance) || resolveCadConnection();
16370
+ return cad;
16371
+ }
16372
+ async function writeAuditSuccess(name, args, options) {
16373
+ writeAuditEntry({
16374
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
16375
+ tool: name,
16376
+ args: args || {},
16377
+ sessionId: options.sessionId || "",
16378
+ result: "success"
16379
+ });
16380
+ }
16381
+ async function writeAuditError(name, args, options, error2) {
16382
+ writeAuditEntry({
16383
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
16384
+ tool: name,
16385
+ args: args || {},
16386
+ sessionId: options.sessionId || "",
16387
+ result: "error",
16388
+ error: error2.message
16389
+ });
16390
+ }
16391
+ async function beginUndo(instance) {
16392
+ const conn = resolveConn(instance);
16393
+ if (conn?.connected) {
16394
+ try {
16395
+ await conn.sendCommand('(command "_.UNDO" "_BEGIN")');
16396
+ return true;
16397
+ } catch (e) {
16398
+ error(`UNDO_BEGIN failed: ${e.message}`);
16399
+ }
16400
+ }
16401
+ return false;
16402
+ }
16403
+ async function endUndo(instance) {
16404
+ const conn = resolveConn(instance);
16405
+ try {
16406
+ await conn.sendCommand('(command "_.UNDO" "_END")');
16407
+ } catch (_) {
16408
+ }
16409
+ }
15852
16410
  async function handleToolCall(name, args, options = {}) {
15853
16411
  const handler = TOOL_HANDLERS[name];
15854
16412
  if (!handler) {
15855
16413
  const customResult = callCustomTool(name, args);
15856
16414
  if (customResult !== null) return customResult;
15857
- return { content: [{ type: "text", text: `\u672A\u77E5\u5DE5\u5177: ${name}` }], isError: true };
16415
+ return mcpErrorResponse(new MCPError(ERROR_CODES.HANDLER_NOT_FOUND, `\u672A\u77E5\u5DE5\u5177: ${name}`));
15858
16416
  }
15859
16417
  const isReadOnly = READ_ONLY_TOOLS.has(name);
15860
16418
  const notifyProgress = options.notifyProgress || null;
15861
16419
  const rateCheck = checkToolRateLimit(name, isReadOnly, options.sessionId);
15862
16420
  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 };
16421
+ return mcpErrorResponse(new MCPError(ERROR_CODES.RATE_LIMITED, `\u5DE5\u5177\u8C03\u7528\u9891\u7387\u8D85\u9650\uFF0C\u8BF7 ${rateCheck.retryAfter} \u79D2\u540E\u91CD\u8BD5`));
15864
16422
  }
15865
16423
  return progressContext.run({ notifyProgress }, async () => {
15866
16424
  setNotifyProgress(notifyProgress);
16425
+ const instance = args?.instance || null;
15867
16426
  let undoBegun = false;
15868
16427
  try {
15869
16428
  validateToolArgs(name, args || {});
15870
- const instance = args?.instance || null;
15871
16429
  if (instance) {
15872
16430
  const ctx = mcpContext.getStore();
15873
16431
  ctx._toolInstance = instance;
15874
16432
  }
15875
16433
  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
- }
16434
+ undoBegun = await beginUndo(instance);
15885
16435
  }
15886
16436
  const np = getNotifyProgress();
15887
16437
  if (np) np(1, 1, `\u6B63\u5728\u6267\u884C ${name}...`);
15888
16438
  const result = await handler(args || {});
15889
16439
  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
- });
16440
+ await writeAuditSuccess(name, args, options);
15897
16441
  notifyResourceChanges(name);
15898
16442
  return result;
15899
16443
  } catch (e) {
15900
16444
  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 };
16445
+ await writeAuditError(name, args, options, e);
16446
+ if (e.message.startsWith("Missing required") || e.message.startsWith("Invalid type")) {
16447
+ return mcpErrorResponse(new MCPError(ERROR_CODES.INVALID_ARGS, e.message));
16448
+ }
16449
+ return mcpErrorResponse(e);
15910
16450
  } finally {
15911
16451
  const ctx = mcpContext.getStore();
15912
16452
  if (ctx) ctx._toolInstance = null;
15913
- if (undoBegun) {
15914
- try {
15915
- await cad.sendCommand('(command "_.UNDO" "_END")');
15916
- } catch (_) {
15917
- }
15918
- }
16453
+ if (undoBegun) await endUndo(instance);
15919
16454
  }
15920
16455
  });
15921
16456
  }
@@ -15932,14 +16467,12 @@ var init_tool_executor = __esm({
15932
16467
  init_cad();
15933
16468
  init_cad_pool();
15934
16469
  init_audit_log();
16470
+ init_errors();
15935
16471
  init_tool_registry();
15936
16472
  }
15937
16473
  });
15938
16474
 
15939
16475
  // src/mcp-tools.js
15940
- import fs11 from "fs";
15941
- import path15 from "path";
15942
- import os9 from "os";
15943
16476
  import { formatCode as formatCode2 } from "@atlisp/lint/dist/formatter.js";
15944
16477
  import { lintProject as lintProject2 } from "@atlisp/lint/dist/project.js";
15945
16478
  import { RULES as RULES3 } from "@atlisp/lint/dist/rules.js";
@@ -15956,7 +16489,6 @@ var TOOL_HANDLERS;
15956
16489
  var init_mcp_tools = __esm({
15957
16490
  "src/mcp-tools.js"() {
15958
16491
  init_handler_loader();
15959
- init_prompt_handlers();
15960
16492
  init_streaming();
15961
16493
  init_constants();
15962
16494
  init_cad();
@@ -15964,13 +16496,24 @@ var init_mcp_tools = __esm({
15964
16496
  init_tools();
15965
16497
  init_mcp_server_state();
15966
16498
  init_trigger_engine();
15967
- init_lisp_sandbox();
16499
+ init_lisp_security();
15968
16500
  init_lint_analyzer();
15969
16501
  init_repl_engine();
15970
16502
  init_webhook_engine();
15971
16503
  init_audit_log();
15972
16504
  init_agent_context();
16505
+ init_resource_handlers();
16506
+ init_subscription_manager();
16507
+ init_skill_handlers();
16508
+ init_meta_handlers();
15973
16509
  init_tool_executor();
16510
+ pool.onInstanceSwitch(() => {
16511
+ for (const uri of CAD_RESOURCE_URIS) {
16512
+ if (isSubscribed(uri)) {
16513
+ notify2(uri, null);
16514
+ }
16515
+ }
16516
+ });
15974
16517
  TOOL_HANDLERS = {
15975
16518
  // === CAD Connection ===
15976
16519
  connect_cad: lazy("connect_cad", (a) => a?.platform),
@@ -16007,14 +16550,8 @@ ${CAD_PLATFORMS.join("\n")}
16007
16550
  return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
16008
16551
  },
16009
16552
  // === 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
- },
16553
+ list_prompts: list_prompts_handler,
16554
+ get_prompt: get_prompt_handler,
16018
16555
  // === Entity Operations ===
16019
16556
  get_entity: lazy("get_entity", (a) => a?.handle),
16020
16557
  set_entity: lazy("set_entity", (a) => [a?.handle, a?.property, a?.value]),
@@ -16301,6 +16838,35 @@ ${CAD_PLATFORMS.join("\n")}
16301
16838
  draw_ray: lazy("draw_ray", (a) => [a.point, a.direction]),
16302
16839
  draw_leader: lazy("draw_leader", (a) => [a.points, a.annotation]),
16303
16840
  draw_table: lazy("draw_table", (a) => [a.insertionPoint, a.rows, a.columns, a.rowHeight, a.colWidth]),
16841
+ // === Batch Concurrent ===
16842
+ batch_concurrent_tasks: async (a) => {
16843
+ if (!a.tasks || !Array.isArray(a.tasks) || a.tasks.length === 0) {
16844
+ return { content: [{ type: "text", text: "\u9700\u8981 tasks \u6570\u7EC4" }], isError: true };
16845
+ }
16846
+ if (a.tasks.length > 50) {
16847
+ return { content: [{ type: "text", text: "\u6700\u591A\u652F\u6301 50 \u4E2A\u5E76\u53D1\u4EFB\u52A1" }], isError: true };
16848
+ }
16849
+ const results = await Promise.allSettled(a.tasks.map(async (task, idx) => {
16850
+ const handler = TOOL_HANDLERS[task.tool];
16851
+ if (!handler) return { index: idx, tool: task.tool, instance: task.instance, ok: false, error: `\u672A\u77E5\u5DE5\u5177: ${task.tool}` };
16852
+ const ctx = mcpContext.getStore();
16853
+ const prevInstance = ctx?._toolInstance || null;
16854
+ if (ctx && task.instance) ctx._toolInstance = task.instance;
16855
+ try {
16856
+ const result = await handler(task.args || {});
16857
+ return { index: idx, tool: task.tool, instance: task.instance, ok: !result.isError, data: result.content?.[0]?.text || result };
16858
+ } catch (e) {
16859
+ return { index: idx, tool: task.tool, instance: task.instance, ok: false, error: e.message };
16860
+ } finally {
16861
+ if (ctx) ctx._toolInstance = prevInstance;
16862
+ }
16863
+ }));
16864
+ const output = results.map((r, idx) => {
16865
+ if (r.status === "fulfilled") return r.value;
16866
+ return { index: idx, tool: a.tasks[idx].tool, instance: a.tasks[idx].instance, ok: false, error: r.reason?.message || "Unknown error" };
16867
+ });
16868
+ return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }] };
16869
+ },
16304
16870
  // === Batch Transaction ===
16305
16871
  batch_transaction: lazy("batch_transaction"),
16306
16872
  // === Watch / Trigger ===
@@ -16457,6 +17023,14 @@ ${CAD_PLATFORMS.join("\n")}
16457
17023
  },
16458
17024
  cad_pool_switch: (a) => {
16459
17025
  const ok = pool.setActive(a.key);
17026
+ if (ok) {
17027
+ clearCache();
17028
+ for (const uri of CAD_RESOURCE_URIS) {
17029
+ if (isSubscribed(uri)) {
17030
+ notify2(uri, null);
17031
+ }
17032
+ }
17033
+ }
16460
17034
  return { content: [{ type: "text", text: ok ? `\u5DF2\u5207\u6362\u5230\u5B9E\u4F8B ${a.key}` : `\u5B9E\u4F8B ${a.key} \u4E0D\u5B58\u5728` }] };
16461
17035
  },
16462
17036
  cad_pool_health: async () => {
@@ -16468,8 +17042,9 @@ ${CAD_PLATFORMS.join("\n")}
16468
17042
  return { content: [{ type: "text", text: JSON.stringify(stats, null, 2) }] };
16469
17043
  },
16470
17044
  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` }] };
17045
+ const ok = await pool.connect(a.key, a.platform, { label: a.label, pid: a.pid, makeActive: a.makeActive !== false });
17046
+ const label = a.key || (a.pid != null ? `PID ${a.pid}` : "default");
17047
+ return { content: [{ type: "text", text: ok ? `\u5B9E\u4F8B "${label}" \u5DF2\u8FDE\u63A5` : `\u5B9E\u4F8B "${label}" \u8FDE\u63A5\u5931\u8D25` }] };
16473
17048
  },
16474
17049
  cad_pool_disconnect: async (a) => {
16475
17050
  const ok = await pool.disconnect(a.key);
@@ -16519,261 +17094,15 @@ ${CAD_PLATFORMS.join("\n")}
16519
17094
  return { content: [{ type: "text", text: "\u4E0A\u4E0B\u6587\u5DF2\u6E05\u9664" }] };
16520
17095
  },
16521
17096
  // === 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
- },
17097
+ search_tools,
17098
+ list_tools_by_category,
17099
+ get_tool_info,
16573
17100
  // === 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
- }
17101
+ skill_search,
17102
+ skill_install,
17103
+ skill_list_installed,
17104
+ skill_uninstall,
17105
+ skill_list_categories
16777
17106
  };
16778
17107
  }
16779
17108
  });
@@ -16864,7 +17193,7 @@ var init_mcp_server_state = __esm({
16864
17193
  progressContext = new AsyncLocalStorage();
16865
17194
  sessionServers = /* @__PURE__ */ new Map();
16866
17195
  sessionTransports = /* @__PURE__ */ new Map();
16867
- mcpHandlers = { tools: tools42, handleToolCall };
17196
+ mcpHandlers = { tools: tools43, handleToolCall };
16868
17197
  _stdioServer = null;
16869
17198
  _pendingResourceUpdated = /* @__PURE__ */ new Set();
16870
17199
  _resourceUpdatedTimer = null;
@@ -16888,11 +17217,12 @@ function mcpError(text) {
16888
17217
  return { content: [{ type: "text", text: String(text) }], isError: true };
16889
17218
  }
16890
17219
  async function ensureCadConnected() {
16891
- const { cad: cad2 } = await Promise.resolve().then(() => (init_cad(), cad_exports));
16892
- if (!cad2.connected) {
16893
- await cad2.connect();
17220
+ const { cad: cad2, resolveCadConnection: resolveCadConnection2 } = await Promise.resolve().then(() => (init_cad(), cad_exports));
17221
+ const conn = resolveCadConnection2();
17222
+ if (!conn.connected) {
17223
+ await conn.connect();
16894
17224
  }
16895
- if (!cad2.connected) {
17225
+ if (!conn.connected) {
16896
17226
  throw createError(ERROR_CODES.CAD_NOT_CONNECTED);
16897
17227
  }
16898
17228
  }
@@ -17077,7 +17407,7 @@ __export(resource_handlers_exports, {
17077
17407
  readResource: () => readResource
17078
17408
  });
17079
17409
  import fs13 from "fs";
17080
- import path16 from "path";
17410
+ import path17 from "path";
17081
17411
  function propertyPairsToObject(pairs) {
17082
17412
  if (!Array.isArray(pairs)) return null;
17083
17413
  const obj = {};
@@ -17849,7 +18179,7 @@ async function getDwgList() {
17849
18179
  }
17850
18180
  }
17851
18181
  function loadStandardsFile(filename) {
17852
- const filePath = path16.join(STANDARDS_DIR, filename);
18182
+ const filePath = path17.join(STANDARDS_DIR, filename);
17853
18183
  try {
17854
18184
  const raw = fs13.readFileSync(filePath, "utf-8");
17855
18185
  return JSON.parse(raw);
@@ -17949,6 +18279,119 @@ async function getDwgContext() {
17949
18279
  };
17950
18280
  }
17951
18281
  }
18282
+ async function getDrawingAnalysis(params = {}) {
18283
+ const snapshot = await getCadSnapshot();
18284
+ if (!snapshot || !snapshot.cadConnected) {
18285
+ return { connected: false, error: "CAD \u672A\u8FDE\u63A5" };
18286
+ }
18287
+ try {
18288
+ const detailedCode = `(progn
18289
+ (vl-load-com)
18290
+ (setq r nil)
18291
+ ;; entity counts by type
18292
+ (if (setq ss (ssget "_X"))
18293
+ (progn
18294
+ (setq total (sslength ss) i 0 cnt nil)
18295
+ (while (< i total)
18296
+ (setq typ (cdr (assoc 0 (entget (ssname ss i)))))
18297
+ (setq cnt (if (assoc typ cnt) (subst (cons typ (1+ (cdr (assoc typ cnt)))) (assoc typ cnt) cnt) (cons (cons typ 1) cnt)))
18298
+ (setq i (1+ i)))
18299
+ (setq r (cons (cons "entityCount" total) r))
18300
+ (setq r (cons (cons "entityByType" cnt) r)))
18301
+ (setq r (cons (cons "entityCount" 0) r)))
18302
+ ;; block usage
18303
+ (setq blocks nil)
18304
+ (vlax-for b (vla-get-blocks (vla-get-activedocument (vlax-get-acad-object)))
18305
+ (if (and (= (vla-get-IsXref b) :vlax-false) (/= (vla-get-Name b) "*Model_Space") (/= (vla-get-Name b) "*Paper_Space"))
18306
+ (setq blocks (cons (list "name" (vla-get-Name b) "count" (vla-get-Count b) "isDynamic" (vla-get-IsDynamicBlock b)) blocks))))
18307
+ (setq r (cons (cons "blockUsage" blocks) r))
18308
+ ;; layer summary
18309
+ (setq layers nil le (tblnext "layer" t))
18310
+ (while le
18311
+ (setq ln (cdr (assoc 2 le)) lc (cdr (assoc 62 le)) ll (cdr (assoc 6 le)))
18312
+ (setq layers (cons (list "name" ln "color" (if (< lc 0) (- lc) lc) "off" (< lc 0) "linetype" ll) layers))
18313
+ (setq le (tblnext "layer" nil)))
18314
+ (setq r (cons (cons "layers" layers) r))
18315
+ ;; text stats
18316
+ (if (setq ts (ssget "_X" '((0 . "TEXT,MTEXT"))))
18317
+ (progn
18318
+ (setq tc (sslength ts) th nil ti 0)
18319
+ (while (< ti tc)
18320
+ (setq h (cdr (assoc 40 (entget (ssname ts ti)))))
18321
+ (if h (setq th (cons h th)))
18322
+ (setq ti (1+ ti)))
18323
+ (setq th (vl-sort th '<))
18324
+ (setq r (cons (cons "textCount" tc) r))
18325
+ (setq r (cons (cons "textHeights" (list "min" (car th) "max" (last th) "median" (nth (fix (/ (length th) 2)) th))) r)))
18326
+ (setq r (cons (cons "textCount" 0) r)))
18327
+ ;; system variables
18328
+ (setq r (cons (cons "sysvars" (list
18329
+ (cons "ltscale" (getvar "ltscale"))
18330
+ (cons "dimscale" (getvar "dimscale"))
18331
+ (cons "insunits" (getvar "insunits"))
18332
+ (cons "measurement" (getvar "measurement"))
18333
+ (cons "celtype" (getvar "celtype"))
18334
+ (cons "clayer" (getvar "clayer"))
18335
+ (cons "cecolor" (getvar "cecolor"))
18336
+ (cons "osmode" (getvar "osmode"))
18337
+ (cons "snapmode" (getvar "snapmode"))
18338
+ (cons "gridmode" (getvar "gridmode"))
18339
+ (cons "orthomode" (getvar "orthomode"))
18340
+ (cons "plinetype" (getvar "plinetype"))
18341
+ (cons "fillmode" (getvar "fillmode"))
18342
+ (cons "pickstyle" (getvar "pickstyle"))
18343
+ )) r))
18344
+ (vl-prin1-to-string (reverse r)))`;
18345
+ const resultStr = await cad.sendCommandWithResult(detailedCode);
18346
+ const parsed = parseLispRecursive(resultStr);
18347
+ if (!parsed || !Array.isArray(parsed)) {
18348
+ return { connected: true, error: "\u5206\u6790\u7ED3\u679C\u89E3\u6790\u5931\u8D25" };
18349
+ }
18350
+ const analysis = {};
18351
+ for (const pair of parsed) {
18352
+ if (Array.isArray(pair) && pair.length >= 2) {
18353
+ analysis[String(pair[0])] = pair[1];
18354
+ }
18355
+ }
18356
+ return { connected: true, ...analysis };
18357
+ } catch (e) {
18358
+ log(`getDrawingAnalysis error: ${e.message}`);
18359
+ return { connected: true, error: e.message };
18360
+ }
18361
+ }
18362
+ function getToolCategories() {
18363
+ const cats = {};
18364
+ for (const t of tools43) {
18365
+ const cat = t.annotations?.category || "\u5176\u4ED6";
18366
+ if (!cats[cat]) cats[cat] = { count: 0, tools: [] };
18367
+ cats[cat].count++;
18368
+ cats[cat].tools.push(t.name);
18369
+ }
18370
+ return Object.entries(cats).map(([category, info]) => ({
18371
+ category,
18372
+ count: info.count,
18373
+ tools: info.tools
18374
+ })).sort((a, b) => b.count - a.count);
18375
+ }
18376
+ async function checkDrawingStandards() {
18377
+ const standards = getDraftingStandards();
18378
+ if (!standards || standards.error) {
18379
+ return { ok: false, error: standards?.error || "\u5236\u56FE\u89C4\u8303\u672A\u52A0\u8F7D" };
18380
+ }
18381
+ try {
18382
+ const issues = [];
18383
+ for (const rule of standards.layers || []) {
18384
+ const code = `(tblsearch "LAYER" "${escapeLispString(rule.name || rule.pattern)}")`;
18385
+ const exists = await cad.sendCommandWithResult(code);
18386
+ if (rule.required && (!exists || exists === "nil" || exists === "")) {
18387
+ issues.push({ type: "missing_layer", message: `\u7F3A\u5C11\u5FC5\u9700\u56FE\u5C42: ${rule.name}` });
18388
+ }
18389
+ }
18390
+ return { ok: issues.length === 0, issues };
18391
+ } catch (e) {
18392
+ return { ok: false, error: e.message };
18393
+ }
18394
+ }
17952
18395
  function getToolRelationships() {
17953
18396
  return {
17954
18397
  selection: {
@@ -18204,6 +18647,12 @@ async function readResource(uri) {
18204
18647
  return await getDwgContext();
18205
18648
  case "atlisp://tools/relationships":
18206
18649
  return getToolRelationships();
18650
+ case "atlisp://dwg/analysis":
18651
+ return await getDrawingAnalysis(params);
18652
+ case "atlisp://tools/categories":
18653
+ return getToolCategories();
18654
+ case "atlisp://dwg/standards/check":
18655
+ return await checkDrawingStandards();
18207
18656
  default:
18208
18657
  throw new Error(`\u8D44\u6E90\u4E0D\u5B58\u5728: ${uri}`);
18209
18658
  }
@@ -18219,6 +18668,7 @@ var init_resource_handlers = __esm({
18219
18668
  init_handler_utils();
18220
18669
  init_errors();
18221
18670
  init_config();
18671
+ init_tools();
18222
18672
  ENTITY_PROPERTIES_LISP = `
18223
18673
  (defun entity:to-list (val)
18224
18674
  (cond
@@ -18298,11 +18748,11 @@ init_subscription_manager();
18298
18748
  init_subscription_manager();
18299
18749
  init_sse_session();
18300
18750
  init_config();
18301
- import path19 from "path";
18751
+ import path20 from "path";
18302
18752
  import fs16 from "fs";
18303
18753
  import http from "http";
18304
18754
  import https from "https";
18305
- import { fileURLToPath as fileURLToPath7 } from "url";
18755
+ import { fileURLToPath as fileURLToPath8 } from "url";
18306
18756
  import { createRequire as createRequire2 } from "module";
18307
18757
  import express2 from "express";
18308
18758
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -18315,11 +18765,12 @@ init_cad_pool();
18315
18765
  init_subscription_manager();
18316
18766
  init_mcp_server_state();
18317
18767
  init_sse_session();
18768
+ import os10 from "os";
18318
18769
  import express from "express";
18319
18770
  import { randomUUID as randomUUID6, createHash } from "crypto";
18320
18771
  import fs14 from "fs";
18321
- import path17 from "path";
18322
- import { fileURLToPath as fileURLToPath6 } from "url";
18772
+ import path18 from "path";
18773
+ import { fileURLToPath as fileURLToPath7 } from "url";
18323
18774
  import iconv from "iconv-lite";
18324
18775
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
18325
18776
 
@@ -18432,7 +18883,6 @@ var TOOL_PERMISSIONS = {
18432
18883
  "measure_*": "read",
18433
18884
  "stream_*": "read",
18434
18885
  "watch_list": "read",
18435
- "pipeline_list": "read",
18436
18886
  // Write tools
18437
18887
  "create_*": "write",
18438
18888
  "set_*": "write",
@@ -18452,8 +18902,6 @@ var TOOL_PERMISSIONS = {
18452
18902
  "import_*": "import",
18453
18903
  // Admin
18454
18904
  "config_*": "admin",
18455
- "pipeline_save": "admin",
18456
- "pipeline_delete": "admin",
18457
18905
  "webhook_*": "admin",
18458
18906
  "watch_remove": "admin"
18459
18907
  };
@@ -18487,11 +18935,11 @@ function getApiKeyRole(apiKey2, config2) {
18487
18935
 
18488
18936
  // src/routes.js
18489
18937
  init_constants();
18490
- var __dirname6 = path17.dirname(fileURLToPath6(import.meta.url));
18491
- var htmlDir = path17.join(__dirname6, "html");
18938
+ var __dirname6 = path18.dirname(fileURLToPath7(import.meta.url));
18939
+ var htmlDir = path18.join(__dirname6, "html");
18492
18940
  function loadHtml(name, replacements = {}) {
18493
18941
  if (!htmlCache[name]) {
18494
- htmlCache[name] = fs14.readFileSync(path17.join(htmlDir, `${name}.html`), "utf-8");
18942
+ htmlCache[name] = fs14.readFileSync(path18.join(htmlDir, `${name}.html`), "utf-8");
18495
18943
  }
18496
18944
  let html = htmlCache[name];
18497
18945
  for (const [key, value] of Object.entries(replacements)) {
@@ -18504,7 +18952,7 @@ var cachedToolsJson = null;
18504
18952
  var cachedToolsJsonAt = 0;
18505
18953
  function getToolsJson() {
18506
18954
  if (cachedToolsJson && Date.now() - cachedToolsJsonAt < 3e4) return cachedToolsJson;
18507
- cachedToolsJson = JSON.stringify(tools42.map((t) => ({
18955
+ cachedToolsJson = JSON.stringify(tools43.map((t) => ({
18508
18956
  name: t.name,
18509
18957
  description: t.description,
18510
18958
  inputSchema: t.inputSchema
@@ -18574,15 +19022,45 @@ function createAuthMiddleware() {
18574
19022
  return res.status(401).json({ error: "Unauthorized" });
18575
19023
  };
18576
19024
  }
19025
+ function originMatches(pattern, origin) {
19026
+ if (pattern === "*") return true;
19027
+ if (!origin) return false;
19028
+ const getHost = (s) => s.replace(/^https?:\/\//, "").replace(/:\d+$/, "").toLowerCase();
19029
+ const oHost = getHost(origin);
19030
+ let pHost = getHost(pattern);
19031
+ if (pHost.startsWith("*.")) {
19032
+ const suffix = pHost.slice(1);
19033
+ return oHost === suffix.slice(1) || oHost.endsWith(suffix);
19034
+ }
19035
+ return oHost === pHost;
19036
+ }
18577
19037
  function createCorsMiddleware() {
19038
+ const allowedOrigins = corsOrigin.split(",").map((s) => s.trim()).filter(Boolean);
19039
+ const allowAll = allowedOrigins.includes("*");
18578
19040
  return (req, res, next) => {
18579
19041
  if (!enableCors) return next();
18580
- res.setHeader("Access-Control-Allow-Origin", corsOrigin);
19042
+ const origin = req.headers.origin;
19043
+ const isPnaPreflight = req.method === "OPTIONS" && req.headers["access-control-request-private-network"] === "true";
19044
+ if (allowAll) {
19045
+ if (isPnaPreflight && origin) {
19046
+ res.setHeader("Access-Control-Allow-Origin", origin);
19047
+ res.setHeader("Vary", "Origin");
19048
+ } else {
19049
+ res.setHeader("Access-Control-Allow-Origin", corsOrigin);
19050
+ }
19051
+ } else if (origin && allowedOrigins.some((p) => originMatches(p, origin))) {
19052
+ res.setHeader("Access-Control-Allow-Origin", origin);
19053
+ res.setHeader("Vary", "Origin");
19054
+ } else if (!origin) {
19055
+ res.setHeader("Access-Control-Allow-Origin", allowedOrigins[0] || "*");
19056
+ } else {
19057
+ return res.status(403).json({ error: "Origin not allowed" });
19058
+ }
18581
19059
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS, CONNECT");
18582
19060
  res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Accept, mcp-session-id, last-event-id");
18583
19061
  res.setHeader("Access-Control-Expose-Headers", "mcp-session-id, content-type, x-accel-buffering");
18584
19062
  res.setHeader("Access-Control-Max-Age", "86400");
18585
- res.setHeader("Vary", "Accept");
19063
+ res.setHeader("Access-Control-Allow-Private-Network", "true");
18586
19064
  if (req.method === "OPTIONS") return res.status(204).end();
18587
19065
  next();
18588
19066
  };
@@ -18673,7 +19151,7 @@ function setupRoutes(app) {
18673
19151
  },
18674
19152
  mcp: {
18675
19153
  sessions: sessionServers.size,
18676
- tools: tools42.length,
19154
+ tools: tools43.length,
18677
19155
  sseSessions: sseCount
18678
19156
  },
18679
19157
  pool: {
@@ -18683,6 +19161,11 @@ function setupRoutes(app) {
18683
19161
  }
18684
19162
  });
18685
19163
  });
19164
+ app.get("/go", (req, res) => {
19165
+ const remoteUrl = "https://atlisp.cn/agent";
19166
+ const cid = config_default.clientId || os10.hostname();
19167
+ res.redirect(302, `${remoteUrl}?clientId=${encodeURIComponent(cid)}`);
19168
+ });
18686
19169
  app.get("/health/full", async (req, res) => {
18687
19170
  const memUsage = process.memoryUsage();
18688
19171
  const poolStats = pool ? pool.getStats() : {};
@@ -18696,7 +19179,7 @@ function setupRoutes(app) {
18696
19179
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
18697
19180
  cad: { connected: cad.connected, platform: cad.getPlatform?.(), version: cad.getVersion?.(), commandQueueSize: cad._getQueueSize ? cad._getQueueSize() : 0, restartCount: cad._getRestartCount ? cad._getRestartCount() : 0 },
18698
19181
  system: { uptime: process.uptime(), memory: { rssMB: Math.round(memUsage.rss / 1024 / 1024), heapUsedMB: Math.round(memUsage.heapUsed / 1024 / 1024), heapTotalMB: Math.round(memUsage.heapTotal / 1024 / 1024) }, cpu: process.cpuUsage() },
18699
- mcp: { sessions: sessionServers.size, tools: tools42.length, sseSessions: sseCount },
19182
+ mcp: { sessions: sessionServers.size, tools: tools43.length, sseSessions: sseCount },
18700
19183
  pool: poolStats
18701
19184
  });
18702
19185
  });
@@ -18734,7 +19217,7 @@ function setupRoutes(app) {
18734
19217
  app.get("/api/docs", (req, res) => {
18735
19218
  const html = loadHtml("api-docs", {
18736
19219
  VERSION: SERVER_VERSION,
18737
- TOOLS_COUNT: tools42.length
19220
+ TOOLS_COUNT: tools43.length
18738
19221
  });
18739
19222
  res.setHeader("Content-Type", "text/html; charset=utf-8");
18740
19223
  res.send(html);
@@ -18755,7 +19238,7 @@ function setupRoutes(app) {
18755
19238
  SESSIONS_COUNT: sessionServers.size,
18756
19239
  CAD_STATUS_CLASS: cadConnected ? "status-ok" : "status-error",
18757
19240
  CAD_STATUS_TEXT: cadStatusText,
18758
- TOOLS_COUNT: tools42.length
19241
+ TOOLS_COUNT: tools43.length
18759
19242
  });
18760
19243
  res.setHeader("Content-Type", "text/html; charset=utf-8");
18761
19244
  res.send(html);
@@ -18947,7 +19430,7 @@ function sendWelcome(ws, clientId) {
18947
19430
  server: { name: SERVER_NAME, version: SERVER_VERSION },
18948
19431
  capabilities: SERVER_CAPABILITIES,
18949
19432
  clientId,
18950
- toolCount: tools42.length,
19433
+ toolCount: tools43.length,
18951
19434
  cadConnected: cad.connected,
18952
19435
  cadPlatform: cad.connected ? cad.getPlatform() : null,
18953
19436
  cadVersion: cad.connected ? cad.getVersion() : null
@@ -19199,9 +19682,9 @@ function createWebSocketServer(httpServer) {
19199
19682
  // src/plugin-loader.js
19200
19683
  init_logger();
19201
19684
  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");
19685
+ import path19 from "path";
19686
+ import os11 from "os";
19687
+ var PLUGIN_DIR = path19.join(os11.homedir(), ".atlisp", "mcp-plugins");
19205
19688
  var loadedPlugins = [];
19206
19689
  var watchCallbacks = [];
19207
19690
  var watcher = null;
@@ -19226,7 +19709,7 @@ function clearModuleCache(pluginPath) {
19226
19709
  if (key) delete import.meta[key];
19227
19710
  }
19228
19711
  async function loadSinglePlugin(file) {
19229
- const pluginPath = path18.join(PLUGIN_DIR, file);
19712
+ const pluginPath = path19.join(PLUGIN_DIR, file);
19230
19713
  clearModuleCache(pluginPath);
19231
19714
  try {
19232
19715
  const plugin = await import(`${pluginPath}?t=${Date.now()}`);
@@ -19276,7 +19759,7 @@ function startPluginWatcher() {
19276
19759
  if (!filename || !filename.endsWith(".js") || filename.startsWith("_")) return;
19277
19760
  const existingIndex = loadedPlugins.findIndex((p) => p.name === filename.replace(".js", "") || p._file === filename);
19278
19761
  if (eventType === "rename") {
19279
- const filePath = path18.join(PLUGIN_DIR, filename);
19762
+ const filePath = path19.join(PLUGIN_DIR, filename);
19280
19763
  if (!fs15.existsSync(filePath)) {
19281
19764
  if (existingIndex !== -1) {
19282
19765
  loadedPlugins.splice(existingIndex, 1);
@@ -19316,6 +19799,91 @@ function stopPluginWatcher() {
19316
19799
  init_tool_registry();
19317
19800
  init_mcp_server_state();
19318
19801
 
19802
+ // src/health-reporter.js
19803
+ init_config();
19804
+ init_cad();
19805
+ init_cad_pool();
19806
+ init_mcp_server_state();
19807
+ init_subscription_manager();
19808
+ init_logger();
19809
+ import os12 from "os";
19810
+ var REPORT_URL = "https://atlisp.cn/api/mcp/health";
19811
+ var REPORT_INTERVAL = 3e4;
19812
+ var intervalHandle = null;
19813
+ function gatherHealth() {
19814
+ let cadResponseTime = 0;
19815
+ if (cad.connected) {
19816
+ const t0 = Date.now();
19817
+ try {
19818
+ cad.ping();
19819
+ } catch {
19820
+ }
19821
+ cadResponseTime = Date.now() - t0;
19822
+ }
19823
+ const memUsage = process.memoryUsage();
19824
+ let sseCount = 0;
19825
+ try {
19826
+ sseCount = sessionManager?.count?.() || 0;
19827
+ } catch {
19828
+ }
19829
+ return {
19830
+ clientId: config_default.clientId || os12.hostname(),
19831
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
19832
+ status: "ok",
19833
+ cad: {
19834
+ connected: cad.connected,
19835
+ platform: cad.getPlatform ? cad.getPlatform() : null,
19836
+ version: cad.getVersion ? cad.getVersion() : null,
19837
+ responseTime: cadResponseTime,
19838
+ commandQueueSize: cad._getQueueSize ? cad._getQueueSize() : 0,
19839
+ restartCount: cad._getRestartCount ? cad._getRestartCount() : 0
19840
+ },
19841
+ system: {
19842
+ uptime: process.uptime(),
19843
+ memory: {
19844
+ rss: Math.round(memUsage.rss / 1024 / 1024),
19845
+ heapUsed: Math.round(memUsage.heapUsed / 1024 / 1024),
19846
+ heapTotal: Math.round(memUsage.heapTotal / 1024 / 1024)
19847
+ }
19848
+ },
19849
+ mcp: {
19850
+ sessions: sessionServers.size,
19851
+ sseSessions: sseCount
19852
+ },
19853
+ pool: pool ? {
19854
+ total: pool.getStats().total,
19855
+ active: pool.getStats().active,
19856
+ instances: pool.getStats().instances
19857
+ } : null
19858
+ };
19859
+ }
19860
+ async function report() {
19861
+ try {
19862
+ const data = gatherHealth();
19863
+ const res = await fetch(REPORT_URL, {
19864
+ method: "POST",
19865
+ headers: { "Content-Type": "application/json" },
19866
+ body: JSON.stringify(data),
19867
+ signal: AbortSignal.timeout(1e4)
19868
+ });
19869
+ if (!res.ok) {
19870
+ error(`Health report failed: ${res.status}`);
19871
+ }
19872
+ } catch (e) {
19873
+ error(`Health report error: ${e.message}`);
19874
+ }
19875
+ }
19876
+ function startHealthReporter() {
19877
+ report();
19878
+ intervalHandle = setInterval(report, REPORT_INTERVAL);
19879
+ }
19880
+ function stopHealthReporter() {
19881
+ if (intervalHandle) {
19882
+ clearInterval(intervalHandle);
19883
+ intervalHandle = null;
19884
+ }
19885
+ }
19886
+
19319
19887
  // src/streamable-http.js
19320
19888
  init_logger();
19321
19889
  init_mcp_handlers();
@@ -19326,7 +19894,7 @@ var mcpServer = null;
19326
19894
  var streamableTransport = null;
19327
19895
  async function initStreamableHttpServer() {
19328
19896
  if (mcpServer) return;
19329
- mcpServer = createMcpServer({ tools: tools42, handleToolCall });
19897
+ mcpServer = createMcpServer({ tools: tools43, handleToolCall });
19330
19898
  streamableTransport = new StreamableHTTPServerTransport2({
19331
19899
  sessionIdGenerator: () => randomUUID8()
19332
19900
  });
@@ -19377,9 +19945,9 @@ function setupStreamableHttpRoutes(app) {
19377
19945
  init_mcp_tools();
19378
19946
  init_function_handlers();
19379
19947
  init_constants();
19380
- var __dirname7 = path19.dirname(fileURLToPath7(import.meta.url));
19948
+ var __dirname7 = path20.dirname(fileURLToPath8(import.meta.url));
19381
19949
  var require3 = createRequire2(import.meta.url);
19382
- var { version: version2 } = require3(path19.join(__dirname7, "..", "package.json"));
19950
+ var { version: version2 } = require3(path20.join(__dirname7, "..", "package.json"));
19383
19951
  var isMcpScript = process.argv[1]?.includes("atlisp-mcp");
19384
19952
  if (isMcpScript) {
19385
19953
  const cliArgs2 = process.argv.slice(2);
@@ -19413,6 +19981,7 @@ Options:
19413
19981
  --no-lint Disable lint pre-check for eval_lisp
19414
19982
  --lint-off Same as --no-lint
19415
19983
  --api-key <key> API key for authentication
19984
+ --client-id <id> Client identifier for remote health report
19416
19985
  --security-level <type> Security level: strict, standard, relaxed (default: strict)
19417
19986
 
19418
19987
  Environment Variables:
@@ -19433,7 +20002,8 @@ Environment Variables:
19433
20002
  LINT_LEVEL Lint level (error/warn/off)
19434
20003
  NO_LINT Disable lint (true/false)
19435
20004
  REQUEST_LOG_ENABLED Enable request logging (true/false, default: true)
19436
- CORS_ORIGIN CORS origin (default: *)
20005
+ CORS_ORIGIN CORS origin (default: https://atlisp.cn)
20006
+ CLIENT_ID Client identifier for remote health report
19437
20007
  RATE_LIMIT_WINDOW Rate limit window in ms (default: 60000)
19438
20008
  RATE_LIMIT_MAX Max requests per window (default: 100)
19439
20009
 
@@ -19489,6 +20059,8 @@ For more info: https://atlisp.cn
19489
20059
  process.env.SSL_CERT_PATH = cliArgs2[++i];
19490
20060
  } else if (arg === "--api-key" && cliArgs2[i + 1]) {
19491
20061
  process.env.MCP_API_KEY = cliArgs2[++i];
20062
+ } else if (arg === "--client-id" && cliArgs2[i + 1]) {
20063
+ process.env.CLIENT_ID = cliArgs2[++i];
19492
20064
  } else if (arg === "--metrics-enabled" && cliArgs2[i + 1]) {
19493
20065
  process.env.METRICS_ENABLED = cliArgs2[++i];
19494
20066
  }
@@ -19511,7 +20083,7 @@ async function initCadConnection() {
19511
20083
  }
19512
20084
  async function startServer() {
19513
20085
  await loadPlugins();
19514
- initPlugins({ cad, config: config_default, log, registerTool, tools: tools42 });
20086
+ initPlugins({ cad, config: config_default, log, registerTool, tools: tools43 });
19515
20087
  onPluginChange((type, name) => {
19516
20088
  log(`Plugin changed: ${type} ${name}`);
19517
20089
  broadcastToolListChanged();
@@ -19521,7 +20093,7 @@ async function startServer() {
19521
20093
  startPluginWatcher();
19522
20094
  let promptsWatcher = null;
19523
20095
  try {
19524
- const promptsDir = path19.join(__dirname7, "..", "prompts");
20096
+ const promptsDir = path20.join(__dirname7, "..", "prompts");
19525
20097
  if (fs16.existsSync(promptsDir)) {
19526
20098
  promptsWatcher = fs16.watch(promptsDir, (eventType, filename) => {
19527
20099
  if (filename && filename.endsWith(".json")) {
@@ -19535,7 +20107,7 @@ async function startServer() {
19535
20107
  }
19536
20108
  if (config_default.transport === "stdio") {
19537
20109
  await initCadConnection();
19538
- const mcpServer2 = createStdioServer({ tools: tools42, handleToolCall });
20110
+ const mcpServer2 = createStdioServer({ tools: tools43, handleToolCall });
19539
20111
  const transport = new StdioServerTransport();
19540
20112
  const sessionId = "stdio-session";
19541
20113
  sessionManager.createSession(sessionId);
@@ -19583,10 +20155,12 @@ async function startServer() {
19583
20155
  const proto = useSsl ? "https" : "http";
19584
20156
  const mode = config_default.transport === "ws" ? "WebSocket" : config_default.transport === "streamable-http" ? "Streamable HTTP" : "\u591A\u4F1A\u8BDD";
19585
20157
  console.error(`@lisp MCP Server \u542F\u52A8 - ${proto}://${config_default.host}:${config_default.port} (${mode}\u6A21\u5F0F)`);
20158
+ startHealthReporter();
19586
20159
  });
19587
20160
  async function shutdown(signal) {
19588
20161
  console.error(`
19589
20162
  \u6536\u5230 ${signal}\uFF0C\u6B63\u5728\u4F18\u96C5\u5173\u95ED...`);
20163
+ stopHealthReporter();
19590
20164
  stopPluginWatcher();
19591
20165
  if (promptsWatcher) {
19592
20166
  promptsWatcher.close();
@@ -19636,5 +20210,5 @@ export {
19636
20210
  handleToolCall,
19637
20211
  loadAtlibFunctionLib,
19638
20212
  startServer,
19639
- tools42 as tools
20213
+ tools43 as tools
19640
20214
  };