@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.
- package/dist/atlisp-mcp.js +1 -10
- package/dist/handlers/3d-handlers.js +128 -0
- package/dist/handlers/batch-handlers.js +260 -0
- package/dist/handlers/block-handlers.js +214 -0
- package/dist/handlers/cad-handlers.js +180 -0
- package/dist/handlers/constraint-handlers.js +130 -0
- package/dist/handlers/dim-hatch-handlers.js +340 -0
- package/dist/handlers/draw-handlers.js +175 -0
- package/dist/handlers/edit-handlers.js +179 -0
- package/dist/handlers/entity-handlers.js +196 -0
- package/dist/handlers/external-handlers.js +218 -0
- package/dist/handlers/file-handlers.js +172 -0
- package/dist/handlers/function-handlers.js +158 -0
- package/dist/handlers/layer-handlers.js +181 -0
- package/dist/handlers/package-handlers.js +89 -0
- package/dist/handlers/parametric-handlers.js +272 -0
- package/dist/handlers/pdf-annotation-handlers.js +117 -0
- package/dist/handlers/prompt-handlers.js +9 -0
- package/dist/handlers/purge-handlers.js +137 -0
- package/dist/handlers/query-print-handlers.js +662 -0
- package/dist/handlers/resource-cache.js +63 -0
- package/dist/handlers/resource-defs.js +79 -0
- package/dist/handlers/resource-handlers.js +131 -0
- package/dist/handlers/resource-readers.js +1045 -0
- package/dist/handlers/sheetset-handlers.js +430 -0
- package/dist/handlers/spatial-handlers.js +174 -0
- package/dist/handlers/style-handlers.js +162 -0
- package/dist/handlers/text-handlers.js +198 -0
- package/dist/handlers/ucs-layout-handlers.js +271 -0
- package/dist/handlers/viewport-handlers.js +150 -0
- package/dist/handlers/xdata-handlers.js +148 -0
- package/dist/handlers/xref-handlers.js +334 -0
- package/dist/lisp-security.js +7 -2
- 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
|
+
}
|
package/dist/lisp-security.js
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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"
|