@atlisp/mcp 1.8.16 → 1.8.18

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.
Files changed (34) hide show
  1. package/dist/atlisp-mcp.js +1 -10
  2. package/dist/handlers/3d-handlers.js +128 -0
  3. package/dist/handlers/batch-handlers.js +260 -0
  4. package/dist/handlers/block-handlers.js +214 -0
  5. package/dist/handlers/cad-handlers.js +180 -0
  6. package/dist/handlers/constraint-handlers.js +130 -0
  7. package/dist/handlers/dim-hatch-handlers.js +340 -0
  8. package/dist/handlers/draw-handlers.js +175 -0
  9. package/dist/handlers/edit-handlers.js +179 -0
  10. package/dist/handlers/entity-handlers.js +196 -0
  11. package/dist/handlers/external-handlers.js +218 -0
  12. package/dist/handlers/file-handlers.js +172 -0
  13. package/dist/handlers/function-handlers.js +158 -0
  14. package/dist/handlers/layer-handlers.js +181 -0
  15. package/dist/handlers/package-handlers.js +89 -0
  16. package/dist/handlers/parametric-handlers.js +272 -0
  17. package/dist/handlers/pdf-annotation-handlers.js +117 -0
  18. package/dist/handlers/prompt-handlers.js +9 -0
  19. package/dist/handlers/purge-handlers.js +137 -0
  20. package/dist/handlers/query-print-handlers.js +662 -0
  21. package/dist/handlers/resource-cache.js +63 -0
  22. package/dist/handlers/resource-defs.js +79 -0
  23. package/dist/handlers/resource-handlers.js +131 -0
  24. package/dist/handlers/resource-readers.js +1045 -0
  25. package/dist/handlers/sheetset-handlers.js +430 -0
  26. package/dist/handlers/spatial-handlers.js +174 -0
  27. package/dist/handlers/style-handlers.js +162 -0
  28. package/dist/handlers/text-handlers.js +198 -0
  29. package/dist/handlers/ucs-layout-handlers.js +271 -0
  30. package/dist/handlers/viewport-handlers.js +150 -0
  31. package/dist/handlers/xdata-handlers.js +148 -0
  32. package/dist/handlers/xref-handlers.js +334 -0
  33. package/dist/lisp-security.js +7 -2
  34. package/package.json +2 -2
@@ -0,0 +1,150 @@
1
+ import { cad } from '../cad.js';
2
+ import { escapeLispString } from '../handler-utils.js';
3
+
4
+ export async function createViewport(params) {
5
+ if (!cad.connected) { await cad.connect(); }
6
+ if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
7
+ try {
8
+ const code = `(progn
9
+ (vl-load-com)
10
+ (setvar "CTAB" "${escapeLispString(params.layout)}")
11
+ (command "_.MVIEW" "${escapeLispString(params.corner1)}" "${escapeLispString(params.corner2)}")
12
+ (entlast)
13
+ )`;
14
+ const result = await cad.sendCommandWithResult(code);
15
+ return { content: [{ type: 'text', text: `已在布局 ${params.layout} 创建视口,句柄: ${result}` }] };
16
+ } catch (e) {
17
+ return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true };
18
+ }
19
+ }
20
+
21
+ export async function createPolygonalViewport(params) {
22
+ if (!cad.connected) { await cad.connect(); }
23
+ if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
24
+ try {
25
+ const pts = params.points.split(/\s+/).filter(Boolean);
26
+ const pointArgs = pts.map(p => `"${escapeLispString(p)}"`).join(' ');
27
+ const code = `(progn
28
+ (vl-load-com)
29
+ (setvar "CTAB" "${escapeLispString(params.layout)}")
30
+ (command "_.MVIEW" "P" ${pointArgs} "C")
31
+ (entlast)
32
+ )`;
33
+ const result = await cad.sendCommandWithResult(code);
34
+ return { content: [{ type: 'text', text: `已在布局 ${params.layout} 创建多边形视口,句柄: ${result}` }] };
35
+ } catch (e) {
36
+ return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true };
37
+ }
38
+ }
39
+
40
+ export async function setViewportScale(params) {
41
+ if (!cad.connected) { await cad.connect(); }
42
+ if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
43
+ try {
44
+ const code = `(progn
45
+ (vl-load-com)
46
+ (if (setq vp (handent "${escapeLispString(params.handle)}"))
47
+ (progn
48
+ (setvar "CVPORT" (cdr (assoc 69 (entget vp))))
49
+ (command "_.ZOOM" (strcat (rtos (/ 1.0 ${params.scale}) 2 8) "XP"))
50
+ "OK"
51
+ )
52
+ "NOTFOUND"
53
+ )
54
+ )`;
55
+ const result = await cad.sendCommandWithResult(code);
56
+ if (result === 'OK' || result === '"OK"') {
57
+ return { content: [{ type: 'text', text: `已设置视口比例: 1:${params.scale}` }] };
58
+ }
59
+ return { content: [{ type: 'text', text: `设置比例失败: ${result}` }], isError: true };
60
+ } catch (e) {
61
+ return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true };
62
+ }
63
+ }
64
+
65
+ export async function lockViewport(params) {
66
+ if (!cad.connected) { await cad.connect(); }
67
+ if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
68
+ try {
69
+ const code = `(progn
70
+ (vl-load-com)
71
+ (if (setq vp (handent "${escapeLispString(params.handle)}"))
72
+ (progn
73
+ (setq ed (entget vp))
74
+ (entmod (subst '(90 . 16384) (assoc 90 ed) ed))
75
+ "OK"
76
+ )
77
+ "NOTFOUND"
78
+ )
79
+ )`;
80
+ const result = await cad.sendCommandWithResult(code);
81
+ if (result === 'OK' || result === '"OK"') {
82
+ return { content: [{ type: 'text', text: `已锁定视口: ${params.handle}` }] };
83
+ }
84
+ return { content: [{ type: 'text', text: `锁定视口失败: ${result}` }], isError: true };
85
+ } catch (e) {
86
+ return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true };
87
+ }
88
+ }
89
+
90
+ export async function unlockViewport(params) {
91
+ if (!cad.connected) { await cad.connect(); }
92
+ if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
93
+ try {
94
+ const code = `(progn
95
+ (vl-load-com)
96
+ (if (setq vp (handent "${escapeLispString(params.handle)}"))
97
+ (progn
98
+ (setq ed (entget vp))
99
+ (setq val (cdr (assoc 90 ed)))
100
+ (entmod (subst (cons 90 (logand val (~ 16384))) (assoc 90 ed) ed))
101
+ "OK"
102
+ )
103
+ "NOTFOUND"
104
+ )
105
+ )`;
106
+ const result = await cad.sendCommandWithResult(code);
107
+ if (result === 'OK' || result === '"OK"') {
108
+ return { content: [{ type: 'text', text: `已解锁视口: ${params.handle}` }] };
109
+ }
110
+ return { content: [{ type: 'text', text: `解锁视口失败: ${result}` }], isError: true };
111
+ } catch (e) {
112
+ return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true };
113
+ }
114
+ }
115
+
116
+ export async function maximizeViewport(params) {
117
+ if (!cad.connected) { await cad.connect(); }
118
+ if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
119
+ try {
120
+ const code = `(progn
121
+ (vl-load-com)
122
+ (if (setq vp (handent "${escapeLispString(params.handle)}"))
123
+ (progn
124
+ (command "_.VPMAX" vp)
125
+ "OK"
126
+ )
127
+ "NOTFOUND"
128
+ )
129
+ )`;
130
+ const result = await cad.sendCommandWithResult(code);
131
+ if (result === 'OK' || result === '"OK"') {
132
+ return { content: [{ type: 'text', text: `已最大化视口: ${params.handle}` }] };
133
+ }
134
+ return { content: [{ type: 'text', text: `最大化视口失败: ${result}` }], isError: true };
135
+ } catch (e) {
136
+ return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true };
137
+ }
138
+ }
139
+
140
+ export async function minimizeViewport(params) {
141
+ if (!cad.connected) { await cad.connect(); }
142
+ if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
143
+ try {
144
+ const code = `(command "_.VPMIN")`;
145
+ const result = await cad.sendCommandWithResult(code);
146
+ return { content: [{ type: 'text', text: '已最小化视口' }] };
147
+ } catch (e) {
148
+ return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true };
149
+ }
150
+ }
@@ -0,0 +1,148 @@
1
+ import { cad } from '../cad.js';
2
+ import { log } from '../logger.js';
3
+ import { mcpSuccess, mcpError, ensureCadConnected, escapeLispString } from '../handler-utils.js';
4
+
5
+ export async function getXdata(handle, appName = 'ACAD') {
6
+ try {
7
+ await ensureCadConnected();
8
+ const result = await cad.sendCommandWithResult(`(progn
9
+ (setq ent (handent "${escapeLispString(handle)}"))
10
+ (if ent (progn
11
+ (setq xdata (assoc -3 (entget ent (list "${escapeLispString(appName)}"))))
12
+ (if xdata (princ (vl-prin1-to-string (cdr xdata))) (princ "nil"))
13
+ ) (princ "nil"))
14
+ )`);
15
+ let xdata = null;
16
+ if (result && result !== 'nil' && result.trim()) { try { xdata = JSON.parse(result.trim()); } catch { xdata = result.trim(); } }
17
+ return mcpSuccess(JSON.stringify({ handle, appName, xdata }));
18
+ } catch (e) { return mcpError(`错误: ${e.message}`); }
19
+ }
20
+
21
+ export async function setXdata(handle, appName, data) {
22
+ try {
23
+ await ensureCadConnected();
24
+ const dataStr = typeof data === 'string' ? data : JSON.stringify(data);
25
+ const result = await cad.sendCommandWithResult(`(progn
26
+ (regapp "${escapeLispString(appName)}")
27
+ (setq ent (handent "${escapeLispString(handle)}"))
28
+ (if ent (progn
29
+ (setq oldxdata (assoc -3 (entget ent (list "${escapeLispString(appName)}"))))
30
+ (setq entlist (if oldxdata (subst (cons -3 (list "${escapeLispString(appName)}" ${dataStr})) oldxdata (entget ent)) (append (entget ent) (list (cons -3 (list "${escapeLispString(appName)}" ${dataStr}))))))
31
+ (entmod entlist) (princ "ok"))
32
+ (princ "nil"))
33
+ )`);
34
+ return mcpSuccess(JSON.stringify({ handle, appName, data: dataStr, set: result?.includes('ok') }));
35
+ } catch (e) { return mcpError(`错误: ${e.message}`); }
36
+ }
37
+
38
+ export async function deleteXdata(handle, appName) {
39
+ try {
40
+ await ensureCadConnected();
41
+ const result = await cad.sendCommandWithResult(`(progn
42
+ (setq ent (handent "${escapeLispString(handle)}"))
43
+ (if ent (progn
44
+ (setq oldxdata (assoc -3 (entget ent (list "${escapeLispString(appName)}"))))
45
+ (if oldxdata (progn (entmod (vl-remove oldxdata (entget ent))) (princ "deleted")) (princ "notfound"))
46
+ ) (princ "nil"))
47
+ )`);
48
+ return mcpSuccess(JSON.stringify({ handle, appName, deleted: result?.includes('deleted') || result?.includes('notfound') }));
49
+ } catch (e) { return mcpError(`错误: ${e.message}`); }
50
+ }
51
+
52
+ export async function listXdataApps(handle) {
53
+ try {
54
+ await ensureCadConnected();
55
+ const result = await cad.sendCommandWithResult(`(progn
56
+ (setq ent (handent "${escapeLispString(handle)}"))
57
+ (if ent (progn
58
+ (setq apps nil)
59
+ (foreach x (entget ent) (if (= (car x) -3) (setq apps (cons (cadr x) apps))))
60
+ (princ (vl-prin1-to-string (reverse apps)))
61
+ ) (princ "nil"))
62
+ )`);
63
+ let apps = [];
64
+ if (result && result !== 'nil' && result.trim()) { try { apps = JSON.parse(result.trim()); } catch { apps = result.trim().replace(/[()]/g, '').split(' ').filter(Boolean); } }
65
+ return mcpSuccess(JSON.stringify({ handle, apps }));
66
+ } catch (e) { return mcpError(`错误: ${e.message}`); }
67
+ }
68
+
69
+ export async function getAllEntityXdata(handle) {
70
+ try {
71
+ await ensureCadConnected();
72
+ const result = await cad.sendCommandWithResult(`(progn
73
+ (setq ent (handent "${escapeLispString(handle)}"))
74
+ (if ent (progn
75
+ (setq result nil)
76
+ (foreach x (entget ent) (if (= (car x) -3) (setq result (cons (list (cadr x) (cddr x)) result))))
77
+ (princ (vl-prin1-to-string (reverse result)))
78
+ ) (princ "nil"))
79
+ )`);
80
+ let xdata = {};
81
+ if (result && result !== 'nil' && result.trim()) { try { xdata = JSON.parse(result.trim()); } catch { xdata = {}; } }
82
+ return mcpSuccess(JSON.stringify({ handle, xdata }));
83
+ } catch (e) { return mcpError(`错误: ${e.message}`); }
84
+ }
85
+
86
+ export async function copyXdata(sourceHandle, targetHandle, appName = null) {
87
+ try {
88
+ await ensureCadConnected();
89
+ if (appName) {
90
+ const xdataResult = await getXdata(sourceHandle, appName);
91
+ if (xdataResult.content?.[0]) {
92
+ const parsed = JSON.parse(xdataResult.content[0].text);
93
+ if (parsed.xdata) return await setXdata(targetHandle, appName, parsed.xdata);
94
+ }
95
+ return mcpError('源实体未找到扩展数据');
96
+ } else {
97
+ const allXdata = await getAllEntityXdata(sourceHandle);
98
+ if (allXdata.content?.[0]) {
99
+ const parsed = JSON.parse(allXdata.content[0].text);
100
+ if (parsed.xdata) {
101
+ for (const [app, data] of Object.entries(parsed.xdata)) { await setXdata(targetHandle, app, data); }
102
+ return mcpSuccess(JSON.stringify({ handles: [sourceHandle, targetHandle], copiedApps: Object.keys(parsed.xdata) }));
103
+ }
104
+ }
105
+ return mcpError('未找到扩展数据');
106
+ }
107
+ } catch (e) { return mcpError(`错误: ${e.message}`); }
108
+ }
109
+
110
+ export async function searchByXdata(appName, key, value) {
111
+ try {
112
+ await ensureCadConnected();
113
+ const result = await cad.sendCommandWithResult(`(progn
114
+ (setq i -1 results nil ss (ssget "X" (list (cons -3 (list "${escapeLispString(appName)}" (list (cons 1000 "${escapeLispString(key)}") (cons 1000 "${escapeLispString(value)}")))))))
115
+ (if ss (repeat (sslength ss) (setq results (cons (cdr (assoc -1 (entget (ssname ss (setq i (1+ i)))))) results))))
116
+ (princ (vl-prin1-to-string (reverse results)))
117
+ )`);
118
+ let handles = [];
119
+ if (result && result !== 'nil' && result.trim()) { try { handles = JSON.parse(result.trim()); } catch { handles = []; } }
120
+ return mcpSuccess(JSON.stringify({ appName, key, value, handles, count: handles.length }));
121
+ } catch (e) { return mcpError(`错误: ${e.message}`); }
122
+ }
123
+
124
+ export async function exportXdataToFile(handle, filePath) {
125
+ try {
126
+ await ensureCadConnected();
127
+ const xdataResult = await getAllEntityXdata(handle);
128
+ const parsed = JSON.parse(xdataResult.content[0].text);
129
+ const fs = await import('fs');
130
+ fs.writeFileSync(filePath, JSON.stringify(parsed.xdata, null, 2));
131
+ return mcpSuccess(JSON.stringify({ handle, filePath }));
132
+ } catch (e) { return mcpError(`错误: ${e.message}`); }
133
+ }
134
+
135
+ export async function importXdataFromFile(handle, filePath, appName) {
136
+ try {
137
+ await ensureCadConnected();
138
+ const fs = await import('fs');
139
+ const data = fs.readFileSync(filePath, 'utf8');
140
+ const xdata = JSON.parse(data);
141
+ if (appName && xdata[appName]) {
142
+ return await setXdata(handle, appName, xdata[appName]);
143
+ } else {
144
+ for (const [app, data] of Object.entries(xdata)) { await setXdata(handle, app, data); }
145
+ return mcpSuccess(JSON.stringify({ handle, imported: Object.keys(xdata) }));
146
+ }
147
+ } catch (e) { return mcpError(`错误: ${e.message}`); }
148
+ }
@@ -0,0 +1,334 @@
1
+ import { cad } from '../cad.js';
2
+ import { mcpSuccess, mcpError, escapeLispString } from '../handler-utils.js';
3
+
4
+ function esc(s) {
5
+ return escapeLispString(String(s));
6
+ }
7
+
8
+ function lispPoint(pt) {
9
+ if (!pt || pt === '0,0,0') return '(list 0 0 0)';
10
+ const parts = pt.split(',').map(Number);
11
+ return `(list ${parts[0] || 0} ${parts[1] || 0} ${parts[2] || 0})`;
12
+ }
13
+
14
+ export async function attachXref(args) {
15
+ try {
16
+ const path = esc(args.path);
17
+ const point = lispPoint(args.point);
18
+ const scale = args.scale || 1;
19
+ const rotation = args.rotation || 0;
20
+ const overlay = args.overlay ? '_O' : '_A';
21
+ const result = await cad.sendCommandWithResult(
22
+ `(command "_.XATTACH" "${path}" "${overlay}" "${point}" "${scale}" "${rotation}") (princ)`
23
+ );
24
+ return mcpSuccess(`外部参照已附着: ${args.path}`);
25
+ } catch (e) {
26
+ return mcpError(`附着外部参照失败: ${e.message}`);
27
+ }
28
+ }
29
+
30
+ export async function detachXref(args) {
31
+ try {
32
+ const name = esc(args.name);
33
+ await cad.sendCommand(
34
+ `(command "_.XREF" "_D" "${name}" "") (princ)`
35
+ );
36
+ return mcpSuccess(`外部参照已拆离: ${args.name}`);
37
+ } catch (e) {
38
+ return mcpError(`拆离外部参照失败: ${e.message}`);
39
+ }
40
+ }
41
+
42
+ export async function reloadXref(args) {
43
+ try {
44
+ const name = esc(args.name);
45
+ await cad.sendCommand(
46
+ `(command "_.XREF" "_R" "${name}" "") (princ)`
47
+ );
48
+ return mcpSuccess(`外部参照已重载: ${args.name}`);
49
+ } catch (e) {
50
+ return mcpError(`重载外部参照失败: ${e.message}`);
51
+ }
52
+ }
53
+
54
+ export async function unloadXref(args) {
55
+ try {
56
+ const name = esc(args.name);
57
+ await cad.sendCommand(
58
+ `(command "_.XREF" "_U" "${name}" "") (princ)`
59
+ );
60
+ return mcpSuccess(`外部参照已卸载: ${args.name}`);
61
+ } catch (e) {
62
+ return mcpError(`卸载外部参照失败: ${e.message}`);
63
+ }
64
+ }
65
+
66
+ export async function listXrefs() {
67
+ try {
68
+ const result = await cad.sendCommandWithResult(
69
+ `(progn
70
+ (setq xrefs (vla-get-blocks (vla-get-activedocument (vlax-get-acad-object))))
71
+ (setq res nil)
72
+ (vlax-for b xrefs
73
+ (if (and (= (vla-get-isxref b) :vlax-true) (/= (vla-get-name b) "*U*"))
74
+ (setq res (cons
75
+ (list
76
+ (cons "name" (vla-get-name b))
77
+ (cons "path" (vla-get-path b))
78
+ (cons "xref" T)
79
+ )
80
+ res)
81
+ )
82
+ )
83
+ )
84
+ (if res (vl-princ-to-string res) "[]")
85
+ )`
86
+ );
87
+ return mcpSuccess(result || '[]');
88
+ } catch (e) {
89
+ return mcpError(`获取外部参照列表失败: ${e.message}`);
90
+ }
91
+ }
92
+
93
+ export async function bindXref(args) {
94
+ try {
95
+ const name = esc(args.name);
96
+ const bindType = args.bindType === 'insert' ? '_I' : '_B';
97
+ await cad.sendCommand(
98
+ `(command "_.XREF" "_B" "${name}" "${bindType}") (princ)`
99
+ );
100
+ return mcpSuccess(`外部参照已绑定: ${args.name}`);
101
+ } catch (e) {
102
+ return mcpError(`绑定外部参照失败: ${e.message}`);
103
+ }
104
+ }
105
+
106
+ export async function clipXref(args) {
107
+ try {
108
+ const name = esc(args.name);
109
+ const pts = String(args.points).trim();
110
+ await cad.sendCommandWithResult(
111
+ `(command "_.XCLIP" "_N" "${name}" "_P" "_P" "${pts}" "") (princ)`
112
+ );
113
+ return mcpSuccess(`外部参照已裁剪: ${args.name}`);
114
+ } catch (e) {
115
+ return mcpError(`裁剪外部参照失败: ${e.message}`);
116
+ }
117
+ }
118
+
119
+ export async function pathXref(args) {
120
+ try {
121
+ const name = esc(args.name);
122
+ const newPath = esc(args.newPath);
123
+ const result = await cad.sendCommandWithResult(
124
+ `(progn
125
+ (setq blocks (vla-get-blocks (vla-get-activedocument (vlax-get-acad-object))))
126
+ (vlax-for b blocks
127
+ (if (and (= (vla-get-isxref b) :vlax-true) (= (vla-get-name b) "${name}"))
128
+ (progn
129
+ (vla-put-path b "${newPath}")
130
+ (vla-reload b)
131
+ (princ "ok")
132
+ )
133
+ )
134
+ )
135
+ (princ "not found")
136
+ )`
137
+ );
138
+ if (result.includes('ok')) {
139
+ return mcpSuccess(`外部参照路径已更新: ${args.name} → ${args.newPath}`);
140
+ }
141
+ return mcpError(`未找到外部参照: ${args.name}`);
142
+ } catch (e) {
143
+ return mcpError(`修改外部参照路径失败: ${e.message}`);
144
+ }
145
+ }
146
+
147
+ export async function xrefNotifications(args) {
148
+ try {
149
+ const val = args.enable;
150
+ if (val === undefined || val === null) {
151
+ const result = await cad.sendCommandWithResult(`(getvar "XREFNOTIFY")`);
152
+ const current = parseInt(result?.trim() || '0');
153
+ return mcpSuccess(JSON.stringify({
154
+ enabled: current > 0,
155
+ value: current,
156
+ balloonNotification: current === 2
157
+ }));
158
+ }
159
+ const newVal = val === true ? 1 : val === false ? 0 : Number(val);
160
+ await cad.sendCommand(`(setvar "XREFNOTIFY" ${newVal})`);
161
+ return mcpSuccess(JSON.stringify({ enabled: newVal > 0, value: newVal }));
162
+ } catch (e) {
163
+ return mcpError(`设置外部参照通知失败: ${e.message}`);
164
+ }
165
+ }
166
+
167
+ export async function xrefCompare(args) {
168
+ try {
169
+ const xrefName = esc(args.xrefName || '');
170
+ const handle = esc(args.handle || '');
171
+ const result = await cad.sendCommandWithResult(`(progn
172
+ (vl-load-com)
173
+ (setq doc (vla-get-activedocument (vlax-get-acad-object)))
174
+ (setq blocks (vla-get-blocks doc))
175
+ (setq target nil)
176
+ (vlax-for b blocks
177
+ (if (or ${xrefName ? `(= (vla-get-name b) "${xrefName}")` : 'nil'} ${handle ? `(= (vla-get-handle b) "${handle}")` : 'nil'})
178
+ (setq target b)
179
+ )
180
+ )
181
+ (if (and target (= (vla-get-isxref target) :vlax-true))
182
+ (progn
183
+ (setq ents nil)
184
+ (vlax-for e target
185
+ (setq ents (cons (list
186
+ (cons "handle" (vla-get-handle e))
187
+ (cons "type" (vla-get-objectname e))
188
+ ) ents))
189
+ )
190
+ (princ (vl-princ-to-string (list
191
+ (cons "name" (vla-get-name target))
192
+ (cons "path" (vla-get-path target))
193
+ (cons "loaded" (vla-get-isloaded target))
194
+ (cons "entities" (reverse ents))
195
+ )))
196
+ )
197
+ (princ "null")
198
+ )
199
+ )`);
200
+ if (result === 'null' || !result) {
201
+ return mcpError(`外部参照未找到: ${args.xrefName || args.handle}`);
202
+ }
203
+ return mcpSuccess(result);
204
+ } catch (e) {
205
+ return mcpError(`比较外部参照失败: ${e.message}`);
206
+ }
207
+ }
208
+
209
+ export async function xrefConvertToBlock(args) {
210
+ try {
211
+ const xrefName = esc(args.xrefName || args.handle || '');
212
+ const bindType = args.bindType === 'insert' ? 1 : 0;
213
+ const result = await cad.sendCommandWithResult(`(progn
214
+ (vl-load-com)
215
+ (setq doc (vla-get-activedocument (vlax-get-acad-object)))
216
+ (setq blocks (vla-get-blocks doc))
217
+ (setq done nil)
218
+ (vlax-for b blocks
219
+ (if (and (= (vla-get-isxref b) :vlax-true) (= (vla-get-name b) "${xrefName}"))
220
+ (progn
221
+ (vla-bind b ${bindType})
222
+ (setq done T)
223
+ )
224
+ )
225
+ )
226
+ (princ (if done "ok" "not found"))
227
+ )`);
228
+ if (result && result.trim() === 'ok') {
229
+ return mcpSuccess(JSON.stringify({
230
+ name: args.xrefName || args.handle,
231
+ converted: true,
232
+ bindType: bindType === 0 ? 'bind' : 'insert'
233
+ }));
234
+ }
235
+ return mcpError(`未找到外部参照: ${xrefName}`);
236
+ } catch (e) {
237
+ return mcpError(`转换外部参照为块失败: ${e.message}`);
238
+ }
239
+ }
240
+
241
+ export async function xrefDemote(args) {
242
+ try {
243
+ const parentXref = esc(args.parentXref || '');
244
+ const result = await cad.sendCommandWithResult(`(progn
245
+ (vl-load-com)
246
+ (setq doc (vla-get-activedocument (vlax-get-acad-object)))
247
+ (setq blocks (vla-get-blocks doc))
248
+ (setq parent nil)
249
+ (vlax-for b blocks
250
+ (if (or (= (vla-get-name b) "${parentXref}") (= (vla-get-handle b) "${parentXref}"))
251
+ (setq parent b)
252
+ )
253
+ )
254
+ (if (and parent (= (vla-get-isxref parent) :vlax-true))
255
+ (progn
256
+ (setq demoted nil)
257
+ (vlax-for e parent
258
+ (if (= (vla-get-objectname e) "AcDbBlockReference")
259
+ (progn
260
+ (setq refName (vla-get-name e))
261
+ (setq refBlock (vlax-catch-apply (function (lambda (n) (vla-item blocks n))) (list refName)))
262
+ (if (and refBlock (= (vla-get-isxref refBlock) :vlax-true))
263
+ (progn
264
+ (setq already nil)
265
+ (vlax-for b2 blocks
266
+ (if (and (= (vla-get-isxref b2) :vlax-true) (= (vla-get-name b2) refName) (not (eq b2 parent)))
267
+ (setq already T)
268
+ )
269
+ )
270
+ (if (not already)
271
+ (progn
272
+ (command "_.XREF" "_A" (vla-get-path refBlock) "0,0,0" "1" "0")
273
+ (setq demoted (cons refName demoted))
274
+ )
275
+ )
276
+ )
277
+ )
278
+ )
279
+ )
280
+ )
281
+ (princ (vl-princ-to-string (list (cons "demoted" (reverse demoted)))))
282
+ )
283
+ (princ "null")
284
+ )
285
+ )`);
286
+ if (result === 'null' || !result) {
287
+ return mcpError(`父外部参照未找到或不是外部参照: ${args.parentXref}`);
288
+ }
289
+ return mcpSuccess(result);
290
+ } catch (e) {
291
+ return mcpError(`降级外部参照失败: ${e.message}`);
292
+ }
293
+ }
294
+
295
+ export async function xrefPathRelative(args) {
296
+ try {
297
+ const names = args.xrefNames;
298
+ const isAll = names === 'all';
299
+ const nameList = isAll ? [] : (Array.isArray(names) ? names : [names]);
300
+ const nameListStr = nameList.map(n => `"${esc(n)}"`).join(' ');
301
+
302
+ const result = await cad.sendCommandWithResult(`(progn
303
+ (vl-load-com)
304
+ (setq doc (vla-get-activedocument (vlax-get-acad-object)))
305
+ (setq blocks (vla-get-blocks doc))
306
+ (setq dwgPrefix (getvar "DWGPREFIX"))
307
+ (setq results nil)
308
+ (vlax-for b blocks
309
+ (if (and (= (vla-get-isxref b) :vlax-true)
310
+ (or ${isAll ? 'T' : `(member (vla-get-name b) (list ${nameListStr}))`}))
311
+ (progn
312
+ (setq curPath (vla-get-path b))
313
+ (setq name (vla-get-name b))
314
+ (setq dwgDir (vl-string-right-trim "\\\\/" (strcase dwgPrefix)))
315
+ (setq xp (strcase curPath))
316
+ (if (and (> (strlen xp) (strlen dwgDir)) (= (substr xp 1 (strlen dwgDir)) dwgDir))
317
+ (progn
318
+ (setq newPath (strcat ".\\\\" (substr xp (+ (strlen dwgDir) 2))))
319
+ (vla-put-path b newPath)
320
+ (vla-reload b)
321
+ )
322
+ (setq newPath curPath)
323
+ )
324
+ (setq results (cons (list (cons "name" name) (cons "oldPath" curPath) (cons "newPath" newPath)) results))
325
+ )
326
+ )
327
+ )
328
+ (princ (vl-princ-to-string (reverse results)))
329
+ )`);
330
+ return mcpSuccess(result || '[]');
331
+ } catch (e) {
332
+ return mcpError(`转换外部参照路径为相对路径失败: ${e.message}`);
333
+ }
334
+ }
@@ -35,9 +35,14 @@ export function stripStringsAndComments(code) {
35
35
 
36
36
  export function validateLispCodeWorker(code) {
37
37
  const clean = stripStringsAndComments(code);
38
- for (const { re, msg } of SECURITY_RULES) {
38
+ for (const { re, severity, msg } of SECURITY_RULES) {
39
39
  if (re.test(clean)) {
40
- return { valid: false, error: msg };
40
+ if (severity === THREAT_BLOCK) {
41
+ return { valid: false, error: msg };
42
+ }
43
+ if (severity === THREAT_WARN) {
44
+ log('WARN: ' + msg);
45
+ }
41
46
  }
42
47
  }
43
48
  return { valid: true };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlisp/mcp",
3
- "version": "1.8.16",
3
+ "version": "1.8.18",
4
4
  "description": "MCP Server for @lisp on CAD,support AutoCAD/GstarCAD/ZWCAD/BricsCAD or CAD platform compatible with AutoLISP",
5
5
  "type": "module",
6
6
  "bin": {
@@ -18,7 +18,7 @@
18
18
  "start": "node src/atlisp-mcp.js",
19
19
  "build": "npm run build:main && npm run build:worker",
20
20
  "build:main": "esbuild src/atlisp-mcp.js --bundle --platform=node --target=node18 --format=esm --outdir=dist --external:edge-js --packages=external --banner:js=\"#!/usr/bin/env node\"",
21
- "build:worker": "node -e \"const fs=require('fs');const path=require('path');for(const f of ['cad-worker.js','lisp-security.js','logger.js','config.js'])fs.copyFileSync('src/'+f,'dist/'+f);if(!fs.existsSync('dist/html'))fs.mkdirSync('dist/html');for(const f of fs.readdirSync('src/html'))fs.copyFileSync('src/html/'+f,'dist/html/'+f)\"",
21
+ "build:worker": "node -e \"const fs=require('fs');const path=require('path');for(const f of ['cad-worker.js','lisp-security.js','logger.js','config.js'])fs.copyFileSync('src/'+f,'dist/'+f);if(!fs.existsSync('dist/html'))fs.mkdirSync('dist/html');for(const f of fs.readdirSync('src/html'))fs.copyFileSync('src/html/'+f,'dist/html/'+f);if(!fs.existsSync('dist/handlers'))fs.mkdirSync('dist/handlers');for(const f of fs.readdirSync('src/handlers'))fs.copyFileSync('src/handlers/'+f,'dist/handlers/'+f)\"",
22
22
  "prepublishOnly": "npm run build",
23
23
  "test": "vitest run",
24
24
  "test:watch": "vitest"