@atlisp/mcp 1.8.19 → 1.8.20
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 +1903 -1326
- package/dist/cad-worker.js +0 -2
- package/dist/config.js +28 -0
- package/dist/handlers/cad-handlers.js +15 -0
- package/dist/handlers/draw-handlers.js +41 -0
- package/dist/handlers/lint-handlers.js +64 -0
- package/dist/handlers/resource-defs.js +2 -0
- package/dist/handlers/resource-handlers.js +92 -23
- package/dist/lisp-security.js +0 -17
- package/dist/pipelines/analyze-and-report.json +16 -0
- package/dist/pipelines/batch-export-pdfs.json +11 -0
- package/dist/pipelines/batch-layer-move.json +19 -0
- package/dist/pipelines/export-current-drawing.json +18 -0
- package/dist/pipelines/purge-and-save.json +16 -0
- package/dist/prompts/definitions/analyze-drawing.json +6 -0
- package/dist/prompts/definitions/batch-draw-lines.json +14 -0
- package/dist/prompts/definitions/coding-conventions.json +11 -0
- package/dist/prompts/definitions/color-convert.json +30 -0
- package/dist/prompts/definitions/curve-analysis.json +26 -0
- package/dist/prompts/definitions/excel-report.json +9 -0
- package/dist/prompts/definitions/export-entities.json +17 -0
- package/dist/prompts/definitions/generate-dimension.json +8 -0
- package/dist/prompts/definitions/geometry-calc.json +9 -0
- package/dist/prompts/definitions/json-exchange.json +22 -0
- package/dist/prompts/definitions/manage-blocks.json +28 -0
- package/dist/prompts/definitions/manage-layers.json +35 -0
- package/dist/prompts/definitions/pickset-filter.json +31 -0
- package/dist/prompts/definitions/string-process.json +40 -0
- package/dist/prompts/definitions/test-autolisp.json +33 -0
- package/dist/prompts/definitions/text-process.json +26 -0
- package/dist/prompts/definitions/workflow-3d-model.json +24 -0
- package/dist/prompts/definitions/workflow-batch-operations.json +24 -0
- package/dist/prompts/definitions/workflow-block-manage.json +32 -0
- package/dist/prompts/definitions/workflow-drawing-export.json +23 -0
- package/dist/prompts/definitions/workflow-entity-analysis.json +23 -0
- package/dist/prompts/definitions/workflow-layer-management.json +38 -0
- package/dist/prompts/definitions/workflow-plot-publish.json +28 -0
- package/dist/prompts/definitions/workflow-sheetset.json +29 -0
- package/dist/prompts/definitions/workflow-text-process.json +29 -0
- package/dist/prompts/definitions/workflow-xref-manage.json +28 -0
- package/package.json +4 -2
package/dist/cad-worker.js
CHANGED
|
@@ -496,8 +496,6 @@ async function waitForIdle(platform) {
|
|
|
496
496
|
return false;
|
|
497
497
|
}
|
|
498
498
|
|
|
499
|
-
export { checkParens } from './lisp-security.js';
|
|
500
|
-
|
|
501
499
|
async function connectToPlatform(targetPlatform) {
|
|
502
500
|
return new Promise((resolve, reject) => {
|
|
503
501
|
cadConnectByName(targetPlatform, (e, r) => {
|
package/dist/config.js
CHANGED
|
@@ -34,6 +34,10 @@ const envSchema = z.object({
|
|
|
34
34
|
LLM_MAX_TOKENS: z.string().optional().default('4096'),
|
|
35
35
|
LLM_TEMPERATURE: z.string().optional().default('0.7'),
|
|
36
36
|
WORKER_POOL_SIZE: z.string().optional().default('2'),
|
|
37
|
+
LINT_PRESET: z.string().optional().default(''),
|
|
38
|
+
LINT_CONFIG_PATH: z.string().optional().default(''),
|
|
39
|
+
LINT_LEVEL: z.string().optional().default(''),
|
|
40
|
+
NO_LINT: z.string().optional().default(''),
|
|
37
41
|
});
|
|
38
42
|
|
|
39
43
|
const configSchema = z.object({
|
|
@@ -73,6 +77,10 @@ const configSchema = z.object({
|
|
|
73
77
|
llmTemperature: z.number().optional(),
|
|
74
78
|
maxPoolSize: z.number().optional(),
|
|
75
79
|
workerPoolSize: z.number().optional(),
|
|
80
|
+
lintPreset: z.string().optional(),
|
|
81
|
+
lintConfigPath: z.string().optional(),
|
|
82
|
+
lintLevel: z.string().optional(),
|
|
83
|
+
noLint: z.boolean().optional(),
|
|
76
84
|
});
|
|
77
85
|
|
|
78
86
|
function parseArgs() {
|
|
@@ -103,6 +111,17 @@ function parseArgs() {
|
|
|
103
111
|
i++;
|
|
104
112
|
} else if (args[i] === '--debug') {
|
|
105
113
|
result.debug = true;
|
|
114
|
+
} else if (args[i] === '--lint-preset' && args[i + 1]) {
|
|
115
|
+
result.lintPreset = args[i + 1];
|
|
116
|
+
i++;
|
|
117
|
+
} else if (args[i] === '--lint-config' && args[i + 1]) {
|
|
118
|
+
result.lintConfigPath = args[i + 1];
|
|
119
|
+
i++;
|
|
120
|
+
} else if (args[i] === '--lint-level' && args[i + 1]) {
|
|
121
|
+
result.lintLevel = args[i + 1];
|
|
122
|
+
i++;
|
|
123
|
+
} else if (args[i] === '--no-lint' || args[i] === '--lint-off') {
|
|
124
|
+
result.noLint = true;
|
|
106
125
|
}
|
|
107
126
|
}
|
|
108
127
|
return result;
|
|
@@ -174,8 +193,17 @@ const config = {
|
|
|
174
193
|
llmTemperature: fileConfig?.llmTemperature || parseFloat(env.LLM_TEMPERATURE),
|
|
175
194
|
maxPoolSize: fileConfig?.maxPoolSize || 3,
|
|
176
195
|
workerPoolSize: fileConfig?.workerPoolSize || parseInt(env.WORKER_POOL_SIZE, 10),
|
|
196
|
+
lintPreset: fileConfig?.lintPreset || cliArgs.lintPreset || env.LINT_PRESET || undefined,
|
|
197
|
+
lintConfigPath: fileConfig?.lintConfigPath || cliArgs.lintConfigPath || env.LINT_CONFIG_PATH || undefined,
|
|
198
|
+
lintLevel: fileConfig?.lintLevel || cliArgs.lintLevel || env.LINT_LEVEL || undefined,
|
|
199
|
+
noLint: fileConfig?.noLint ?? cliArgs.noLint ?? (env.NO_LINT === 'true'),
|
|
177
200
|
};
|
|
178
201
|
|
|
202
|
+
if (config.lintConfigPath) process.env.LINT_CONFIG_PATH = config.lintConfigPath;
|
|
203
|
+
if (config.lintPreset) process.env.LINT_PRESET = config.lintPreset;
|
|
204
|
+
if (config.lintLevel) process.env.LINT_LEVEL = config.lintLevel;
|
|
205
|
+
if (config.noLint) process.env.NO_LINT = 'true';
|
|
206
|
+
|
|
179
207
|
export function validateConfig() {
|
|
180
208
|
const errors = [];
|
|
181
209
|
if (config.transport === 'http' || config.transport === 'ws') {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { cad } from '../cad.js';
|
|
2
2
|
import { log } from '../logger.js';
|
|
3
3
|
import { withCadConnection, mcpSuccess, mcpError, escapeLispString } from '../handler-utils.js';
|
|
4
|
+
import { analyzeLispCode } from '../lint-analyzer.js';
|
|
4
5
|
|
|
5
6
|
export async function connectCad(platform = null) {
|
|
6
7
|
log(`connect_cad called${platform ? ` (target: ${platform})` : ''}, cad.connected before: ${cad.connected}`);
|
|
@@ -23,6 +24,13 @@ export async function connectCad(platform = null) {
|
|
|
23
24
|
export const evalLisp = withCadConnection(async (code, withResult = false, encoding = null) => {
|
|
24
25
|
const trimmed = (code || '').trim();
|
|
25
26
|
if (!trimmed) return mcpSuccess('命令为空,未发送');
|
|
27
|
+
|
|
28
|
+
const analysis = analyzeLispCode(trimmed);
|
|
29
|
+
if (!analysis.valid) {
|
|
30
|
+
const detail = analysis.errors.map(e => ` L${e.line}: [${e.rule}] ${e.message}`).join('\n');
|
|
31
|
+
return mcpError(`代码存在安全问题:\n${detail}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
26
34
|
if (withResult) {
|
|
27
35
|
const result = await cad.sendCommandWithResult(trimmed, encoding);
|
|
28
36
|
if (result !== null) {
|
|
@@ -42,6 +50,13 @@ export const getCadInfo = withCadConnection(async () => {
|
|
|
42
50
|
export const atCommand = withCadConnection(async (command) => {
|
|
43
51
|
const trimmed = (command || '').trim();
|
|
44
52
|
if (!trimmed) return mcpSuccess('命令为空,未发送');
|
|
53
|
+
|
|
54
|
+
const analysis = analyzeLispCode(trimmed);
|
|
55
|
+
if (!analysis.valid) {
|
|
56
|
+
const detail = analysis.errors.map(e => ` L${e.line}: [${e.rule}] ${e.message}`).join('\n');
|
|
57
|
+
return mcpError(`代码存在安全问题:\n${detail}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
45
60
|
await cad.sendCommand(trimmed + '\n');
|
|
46
61
|
return mcpSuccess('已发送命令');
|
|
47
62
|
});
|
|
@@ -85,3 +85,44 @@ export const drawHatch = withCadConnection(async (points, pattern) => {
|
|
|
85
85
|
await cad.sendCommand(`(command "_.HATCH" "${escapeLispString(pat)}" "" "")`);
|
|
86
86
|
return mcpSuccess(`已创建图案填充: ${pat}`);
|
|
87
87
|
});
|
|
88
|
+
|
|
89
|
+
export const drawHelix = withCadConnection(async (center, topRadius, bottomRadius, height, turns = 3) => {
|
|
90
|
+
await cad.sendCommand(`(command "_.HELIX" "${escapeLispString(center)}" ${bottomRadius} ${topRadius} ${height} ${turns})`);
|
|
91
|
+
return mcpSuccess(`已创建螺旋: 中心 ${center},半径 ${bottomRadius}→${topRadius},高度 ${height},${turns} 圈`);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
export const drawDonut = withCadConnection(async (center, innerRadius, outerRadius) => {
|
|
95
|
+
await cad.sendCommand(`(command "_.DONUT" ${innerRadius} ${outerRadius} "${escapeLispString(center)}" "")`);
|
|
96
|
+
return mcpSuccess(`已绘制圆环: 中心 ${center},内径 ${innerRadius},外径 ${outerRadius}`);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
export const draw2dSolid = withCadConnection(async (p1, p2, p3, p4) => {
|
|
100
|
+
await cad.sendCommand(`(command "_.SOLID" "${escapeLispString(p1)}" "${escapeLispString(p2)}" "${escapeLispString(p3)}" "${escapeLispString(p4)}" "")`);
|
|
101
|
+
return mcpSuccess(`已创建 2D 实体: ${p1} ${p2} ${p3} ${p4}`);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
export const drawXline = withCadConnection(async (point, direction) => {
|
|
105
|
+
await cad.sendCommand(`(command "_.XLINE" "${escapeLispString(point)}" "${escapeLispString(direction)}")`);
|
|
106
|
+
return mcpSuccess(`已创建构造线: 经过 ${point},方向 ${direction}`);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
export const drawRay = withCadConnection(async (point, direction) => {
|
|
110
|
+
await cad.sendCommand(`(command "_.RAY" "${escapeLispString(point)}" "${escapeLispString(direction)}")`);
|
|
111
|
+
return mcpSuccess(`已创建射线: 起点 ${point},方向 ${direction}`);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
export const drawLeader = withCadConnection(async (points, annotation) => {
|
|
115
|
+
const pts = points.split(/\s+/).filter(Boolean);
|
|
116
|
+
const cmds = pts.map(p => `"${escapeLispString(p)}"`).join(' ');
|
|
117
|
+
if (annotation) {
|
|
118
|
+
await cad.sendCommand(`(command "_.LEADER" ${cmds} "" "${escapeLispString(annotation)}" "")`);
|
|
119
|
+
} else {
|
|
120
|
+
await cad.sendCommand(`(command "_.LEADER" ${cmds} "" "" "")`);
|
|
121
|
+
}
|
|
122
|
+
return mcpSuccess(`已创建引线标注: ${points}`);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
export const drawTable = withCadConnection(async (insertionPoint, rows, columns, rowHeight = 10, colWidth = 20) => {
|
|
126
|
+
await cad.sendCommand(`(command "_.TABLE" "${escapeLispString(insertionPoint)}" ${columns} ${rows} ${rowHeight} ${colWidth})`);
|
|
127
|
+
return mcpSuccess(`已创建表格: ${insertionPoint},${rows}×${columns}`);
|
|
128
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { analyzeLispCode } from '../lint-analyzer.js';
|
|
2
|
+
import { formatCode } from '@atlisp/lint/dist/formatter.js';
|
|
3
|
+
import { lintProject } from '@atlisp/lint/dist/project.js';
|
|
4
|
+
import { RULES } from '@atlisp/lint/dist/rules.js';
|
|
5
|
+
import { loadConfig } from '@atlisp/lint/dist/config.js';
|
|
6
|
+
|
|
7
|
+
function getConfig(input) {
|
|
8
|
+
const base = loadConfig();
|
|
9
|
+
if (input?.checks) Object.assign(base.checks, input.checks);
|
|
10
|
+
if (input?.dangerous_calls) Object.assign(base.dangerous_calls, input.dangerous_calls);
|
|
11
|
+
return base;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function analyzeLispCodeHandler(args) {
|
|
15
|
+
const { code, config } = args || {};
|
|
16
|
+
if (!code || !code.trim()) {
|
|
17
|
+
return {
|
|
18
|
+
content: [{ type: 'text', text: JSON.stringify({ valid: true, issues: [], summary: '代码为空,无需分析' }) }],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
const result = analyzeLispCode(code, { lintConfig: config });
|
|
22
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function formatLispCodeHandler(args) {
|
|
26
|
+
const { code, tabWidth } = args || {};
|
|
27
|
+
if (!code) {
|
|
28
|
+
return { content: [{ type: 'text', text: '需要 code 参数' }], isError: true };
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
const formatted = formatCode(code, { tabWidth: tabWidth || 2 });
|
|
32
|
+
return { content: [{ type: 'text', text: formatted }] };
|
|
33
|
+
} catch (e) {
|
|
34
|
+
return { content: [{ type: 'text', text: `格式化失败: ${e.message}` }], isError: true };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function listLintRulesHandler() {
|
|
39
|
+
try {
|
|
40
|
+
const list = RULES.map(r => ({
|
|
41
|
+
rule: r.name,
|
|
42
|
+
severity: r.severity || 'warn',
|
|
43
|
+
description: r.description || '',
|
|
44
|
+
fixable: !!r.fix,
|
|
45
|
+
}));
|
|
46
|
+
return { content: [{ type: 'text', text: JSON.stringify(list, null, 2) }] };
|
|
47
|
+
} catch (e) {
|
|
48
|
+
return { content: [{ type: 'text', text: `获取规则列表失败: ${e.message}` }], isError: true };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function lintProjectHandler(args) {
|
|
53
|
+
const { directory, config: configInput } = args || {};
|
|
54
|
+
if (!directory) {
|
|
55
|
+
return { content: [{ type: 'text', text: '需要 directory 参数' }], isError: true };
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
const lintConfig = getConfig(configInput);
|
|
59
|
+
const results = await lintProject(directory, lintConfig);
|
|
60
|
+
return { content: [{ type: 'text', text: JSON.stringify(results, null, 2) }] };
|
|
61
|
+
} catch (e) {
|
|
62
|
+
return { content: [{ type: 'text', text: `项目分析失败: ${e.message}` }], isError: true };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -20,6 +20,8 @@ export const RESOURCES = [
|
|
|
20
20
|
{ uri: 'atlisp://platforms', name: 'Platforms', description: '支持的平台信息', mimeType: 'application/json', subscribable: false },
|
|
21
21
|
{ uri: 'atlisp://standards/drafting', name: 'Drafting Standards', description: 'CAD 制图规范', mimeType: 'application/json', subscribable: false },
|
|
22
22
|
{ uri: 'atlisp://standards/coding', name: 'Coding Standards', description: '@lisp 编码规范', mimeType: 'application/json', subscribable: false },
|
|
23
|
+
{ uri: 'atlisp://dwg/context', name: 'DWG Context', description: '当前图纸上下文摘要。返回文档名/路径/布局/UCS/图层列表/实体统计/系统变量组合信息,帮助快速了解当前 CAD 状态', mimeType: 'application/json', subscribable: true },
|
|
24
|
+
{ uri: 'atlisp://tools/relationships', name: 'Tool Relationships', description: '工具依赖关系图。描述工具间的调用链关系(如 select_entities → batch_delete),帮助理解工具组合使用方式', mimeType: 'application/json', subscribable: false },
|
|
23
25
|
];
|
|
24
26
|
|
|
25
27
|
export const TBL_INFO = {
|
|
@@ -1047,6 +1047,90 @@ async function getCadEvents(params) {
|
|
|
1047
1047
|
* Fetches a lightweight snapshot of current CAD state.
|
|
1048
1048
|
* Returns a flat object with entity count, layer names, DWG info, etc.
|
|
1049
1049
|
*/
|
|
1050
|
+
async function getDwgContext() {
|
|
1051
|
+
const snapshot = await getCadSnapshot();
|
|
1052
|
+
if (!snapshot || !snapshot.cadConnected) {
|
|
1053
|
+
return { connected: false, message: 'CAD 未连接' };
|
|
1054
|
+
}
|
|
1055
|
+
try {
|
|
1056
|
+
const code = `(progn
|
|
1057
|
+
(vl-load-com)
|
|
1058
|
+
(setq ctx nil)
|
|
1059
|
+
(setq ctx (cons (cons "layout" (getvar "ctab")) ctx))
|
|
1060
|
+
(setq ctx (cons (cons "ucs" (getvar "ucsname")) ctx))
|
|
1061
|
+
(setq ctx (cons (cons "insunits" (getvar "insunits")) ctx))
|
|
1062
|
+
(setq ctx (cons (cons "ltscale" (getvar "ltscale")) ctx))
|
|
1063
|
+
(setq ctx (cons (cons "limmin" (vl-princ-to-string (getvar "limmin"))) ctx))
|
|
1064
|
+
(setq ctx (cons (cons "limmax" (vl-princ-to-string (getvar "limmax"))) ctx))
|
|
1065
|
+
(setq ctx (cons (cons "extmin" (vl-princ-to-string (getvar "extmin"))) ctx))
|
|
1066
|
+
(setq ctx (cons (cons "extmax" (vl-princ-to-string (getvar "extmax"))) ctx))
|
|
1067
|
+
(vl-prin1-to-string ctx))`;
|
|
1068
|
+
const resultStr = await cad.sendCommandWithResult(code);
|
|
1069
|
+
const parsed = parseLispRecursive(resultStr);
|
|
1070
|
+
return {
|
|
1071
|
+
connected: true,
|
|
1072
|
+
docName: snapshot.dwgName,
|
|
1073
|
+
docPath: snapshot.dwgPath,
|
|
1074
|
+
platform: snapshot.platform,
|
|
1075
|
+
entityCount: snapshot.entityCount,
|
|
1076
|
+
layerCount: snapshot.layerCount,
|
|
1077
|
+
layerNames: snapshot.layerNames,
|
|
1078
|
+
extra: parsed && Array.isArray(parsed) ? Object.fromEntries(parsed) : {},
|
|
1079
|
+
};
|
|
1080
|
+
} catch {
|
|
1081
|
+
return {
|
|
1082
|
+
connected: true,
|
|
1083
|
+
docName: snapshot.dwgName,
|
|
1084
|
+
docPath: snapshot.dwgPath,
|
|
1085
|
+
platform: snapshot.platform,
|
|
1086
|
+
entityCount: snapshot.entityCount,
|
|
1087
|
+
layerCount: snapshot.layerCount,
|
|
1088
|
+
layerNames: snapshot.layerNames,
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
function getToolRelationships() {
|
|
1094
|
+
return {
|
|
1095
|
+
selection: {
|
|
1096
|
+
tools: ['select_entities', 'select_all', 'select_last', 'select_by_color', 'find_entities_in_window', 'find_entities_near_point'],
|
|
1097
|
+
next: ['batch_delete', 'batch_move', 'batch_copy', 'batch_set_layer', 'batch_set_color', 'batch_set_linetype', 'batch_rotate', 'batch_scale', 'batch_mirror', 'batch_explode', 'batch_match_properties'],
|
|
1098
|
+
},
|
|
1099
|
+
drawing: {
|
|
1100
|
+
tools: ['draw_line', 'draw_circle', 'draw_arc', 'draw_rectangle', 'draw_polygon', 'draw_polyline', 'draw_spline', 'draw_text', 'draw_mtext', 'draw_ellipse'],
|
|
1101
|
+
next: ['get_entity', 'set_entity', 'batch_match_properties', 'copy_entity', 'move_entity', 'rotate_entity', 'scale_entity'],
|
|
1102
|
+
},
|
|
1103
|
+
editing: {
|
|
1104
|
+
tools: ['trim_entity', 'extend_entity', 'fillet', 'chamfer', 'break_entity', 'join_entities', 'stretch', 'offset_entity', 'mirror_entity'],
|
|
1105
|
+
next: ['get_entity', 'set_entity'],
|
|
1106
|
+
},
|
|
1107
|
+
batch_ops: {
|
|
1108
|
+
tools: ['batch_copy', 'batch_move', 'batch_delete', 'batch_set_layer', 'batch_set_color', 'batch_explode', 'batch_rotate', 'batch_scale'],
|
|
1109
|
+
previous: ['select_entities', 'select_all'],
|
|
1110
|
+
next: ['save_dwg'],
|
|
1111
|
+
},
|
|
1112
|
+
export: {
|
|
1113
|
+
tools: ['export_pdf', 'export_dxf', 'export_dwf', 'export_svg', 'export_stl', 'export_bmp'],
|
|
1114
|
+
previous: ['open_dwg', 'new_document', 'save_dwg'],
|
|
1115
|
+
next: [],
|
|
1116
|
+
},
|
|
1117
|
+
layers: {
|
|
1118
|
+
tools: ['create_layer', 'set_layer', 'layer_isolate', 'layer_freeze_all', 'layer_thaw_all', 'layer_lock_all', 'layer_unlock_all', 'layer_merge'],
|
|
1119
|
+
next: ['set_current_layer', 'batch_set_layer'],
|
|
1120
|
+
},
|
|
1121
|
+
blocks: {
|
|
1122
|
+
tools: ['get_blocks', 'insert_block', 'explode_block', 'get_block_attributes', 'set_block_attribute', 'get_dynamic_block_properties'],
|
|
1123
|
+
previous: ['select_entities'],
|
|
1124
|
+
next: ['save_dwg'],
|
|
1125
|
+
},
|
|
1126
|
+
analysis: {
|
|
1127
|
+
tools: ['analyze_lisp_code', 'stream_entities', 'stream_text', 'stream_block_refs', 'get_entity_info', 'get_bounding_box', 'measure_distance', 'measure_area'],
|
|
1128
|
+
previous: [],
|
|
1129
|
+
next: [],
|
|
1130
|
+
},
|
|
1131
|
+
};
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1050
1134
|
async function getCadSnapshot() {
|
|
1051
1135
|
const cached = getCached('cad:snapshot');
|
|
1052
1136
|
if (cached) return cached;
|
|
@@ -1198,45 +1282,26 @@ export async function listResources(subscribedUris = []) {
|
|
|
1198
1282
|
};
|
|
1199
1283
|
|
|
1200
1284
|
if (hasLive) {
|
|
1201
|
-
// Enrich resource descriptors with live CAD state
|
|
1202
1285
|
switch (r.uri) {
|
|
1203
1286
|
case 'atlisp://cad/info':
|
|
1204
1287
|
entry.description = `CAD 连接信息 (${snapshot.platform || '未知平台'}${snapshot.dwgName ? ', ' + snapshot.dwgName : ''})`;
|
|
1205
|
-
entry.annotations = {
|
|
1206
|
-
platform: snapshot.platform,
|
|
1207
|
-
dwgName: snapshot.dwgName,
|
|
1208
|
-
cadConnected: true,
|
|
1209
|
-
};
|
|
1210
1288
|
break;
|
|
1211
|
-
|
|
1212
1289
|
case 'atlisp://dwg/entities':
|
|
1213
1290
|
entry.description = `图元列表。${snapshot.entityCount} 个实体,${snapshot.layerCount} 个图层。支持 ?type=LINE&layer=0&bbox=0,0,100,100`;
|
|
1214
|
-
entry.size = snapshot.entityCount;
|
|
1215
|
-
entry.annotations = {
|
|
1216
|
-
entityCount: snapshot.entityCount,
|
|
1217
|
-
layerCount: snapshot.layerCount,
|
|
1218
|
-
};
|
|
1219
1291
|
break;
|
|
1220
|
-
|
|
1221
1292
|
case 'atlisp://dwg/name':
|
|
1222
1293
|
entry.description = `当前 DWG 文件名: ${snapshot.dwgName || '(未命名)'}`;
|
|
1223
|
-
entry.annotations = { name: snapshot.dwgName };
|
|
1224
1294
|
break;
|
|
1225
|
-
|
|
1226
1295
|
case 'atlisp://dwg/path':
|
|
1227
|
-
entry.description = snapshot.dwgPath
|
|
1228
|
-
? `文件完整路径: ${snapshot.dwgPath}`
|
|
1229
|
-
: '文件完整路径';
|
|
1230
|
-
entry.annotations = { path: snapshot.dwgPath };
|
|
1296
|
+
entry.description = snapshot.dwgPath ? `文件完整路径: ${snapshot.dwgPath}` : '文件完整路径';
|
|
1231
1297
|
break;
|
|
1232
|
-
|
|
1233
1298
|
case 'atlisp://dwg/tbl':
|
|
1234
1299
|
entry.description = `CAD 表数据 (${snapshot.layerCount} 个图层)。支持路径方式 atlisp://dwg/tbl/layer`;
|
|
1235
|
-
entry.annotations = { layerCount: snapshot.layerCount };
|
|
1236
1300
|
break;
|
|
1237
|
-
|
|
1238
|
-
|
|
1301
|
+
case 'atlisp://dwg/context':
|
|
1302
|
+
entry.description = `当前图纸上下文: ${snapshot.dwgName || '(未命名)'},${snapshot.entityCount} 实体,${snapshot.layerCount} 图层`;
|
|
1239
1303
|
break;
|
|
1304
|
+
default: break;
|
|
1240
1305
|
}
|
|
1241
1306
|
}
|
|
1242
1307
|
|
|
@@ -1316,6 +1381,10 @@ export async function readResource(uri) {
|
|
|
1316
1381
|
return getCodingStandards();
|
|
1317
1382
|
case 'atlisp://cad/events':
|
|
1318
1383
|
return await getCadEvents(params);
|
|
1384
|
+
case 'atlisp://dwg/context':
|
|
1385
|
+
return await getDwgContext();
|
|
1386
|
+
case 'atlisp://tools/relationships':
|
|
1387
|
+
return getToolRelationships();
|
|
1319
1388
|
default:
|
|
1320
1389
|
throw new Error(`资源不存在: ${uri}`);
|
|
1321
1390
|
}
|
package/dist/lisp-security.js
CHANGED
|
@@ -5,27 +5,10 @@ export const THREAT_WARN = 'warn';
|
|
|
5
5
|
export const THREAT_ALLOW = 'allow';
|
|
6
6
|
|
|
7
7
|
export const SECURITY_RULES = [
|
|
8
|
-
{ re: /\beval\s*[(\s]/i, severity: THREAT_BLOCK, msg: '禁止使用 eval 函数' },
|
|
9
8
|
{ re: /\bstartapp\s*[(\s]/i, severity: THREAT_BLOCK, msg: '禁止使用 startapp 函数' },
|
|
10
9
|
{ re: /\bvl-bt\b/i, severity: THREAT_BLOCK, msg: '禁止使用 vl-bt 函数' },
|
|
11
|
-
{ re: /\bvl-exit-with-value\b/i, severity: THREAT_BLOCK, msg: '禁止使用 vl-exit-with-value' },
|
|
12
|
-
{ re: /\bvl-exit-with-error\b/i, severity: THREAT_BLOCK, msg: '禁止使用 vl-exit-with-error' },
|
|
13
10
|
{ re: /\bvlax-import-type-library\b/i, severity: THREAT_BLOCK, msg: '禁止导入类型库' },
|
|
14
|
-
{ re: /\bvlax-add-cmd\b/i, severity: THREAT_BLOCK, msg: '禁止注册新命令' },
|
|
15
|
-
{ re: /\bvla-Delete\b/i, severity: THREAT_BLOCK, msg: '禁止直接删除 vla 对象' },
|
|
16
|
-
{ re: /\bvla-Save\b/i, severity: THREAT_BLOCK, msg: '禁止直接保存文档' },
|
|
17
|
-
{ re: /\bdirectory\s*[(\s]/i, severity: THREAT_BLOCK, msg: '禁止使用 directory 函数' },
|
|
18
|
-
{ re: /\bvla-deletefile\b/i, severity: THREAT_BLOCK, msg: '禁止删除文件' },
|
|
19
|
-
{ re: /\bvla-copyfile\b/i, severity: THREAT_BLOCK, msg: '禁止复制文件' },
|
|
20
|
-
{ re: /\bvla-movefile\b/i, severity: THREAT_BLOCK, msg: '禁止移动文件' },
|
|
21
|
-
{ re: /\bvl-file-copy\s*[(\s]/i, severity: THREAT_BLOCK, msg: '禁止文件复制' },
|
|
22
|
-
{ re: /\bvl-file-delete\s*[(\s]/i, severity: THREAT_BLOCK, msg: '禁止文件删除' },
|
|
23
|
-
{ re: /\bvl-file-rename\s*[(\s]/i, severity: THREAT_BLOCK, msg: '禁止文件重命名' },
|
|
24
11
|
{ re: /\bdos_command_string\s*[(\s]/i, severity: THREAT_BLOCK, msg: '禁止使用 dos_command_string' },
|
|
25
|
-
{ re: /\bload\s*[(\s"]/i, severity: THREAT_WARN, msg: 'load 函数可能加载外部代码' },
|
|
26
|
-
{ re: /\bcommand\s*["\s]*[_(]?\s*["']?\.?\s*_?(?:quit|exit|close|open|new|recover|audit|purge|wblock|export)/i, severity: THREAT_BLOCK, msg: '禁止使用危险 CAD 命令' },
|
|
27
|
-
{ re: /\b(?:read-line|read-char|write-line|write-char)\s*[(\s]/i, severity: THREAT_WARN, msg: '文件 I/O 操作可能影响系统' },
|
|
28
|
-
{ re: /\bvla-add\b/i, severity: THREAT_ALLOW, msg: 'vla-add 创建新对象' },
|
|
29
12
|
];
|
|
30
13
|
|
|
31
14
|
export function stripStringsAndComments(code) {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "analyze-and-report",
|
|
3
|
+
"description": "分析当前图纸实体并生成统计报告。先获取所有实体,再逐个获取详细信息",
|
|
4
|
+
"steps": [
|
|
5
|
+
{
|
|
6
|
+
"name": "entities",
|
|
7
|
+
"tool": "stream_entities",
|
|
8
|
+
"args": { "chunk": 500, "offset": 0 }
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"name": "info",
|
|
12
|
+
"tool": "get_cad_info",
|
|
13
|
+
"args": {}
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "batch-layer-move",
|
|
3
|
+
"description": "按条件选择实体并移动到目标图层。先选择指定类型/图层的实体,再移动到新图层",
|
|
4
|
+
"steps": [
|
|
5
|
+
{
|
|
6
|
+
"name": "selection",
|
|
7
|
+
"tool": "select_entities",
|
|
8
|
+
"args": { "filter": { "type": "LINE", "layer": "0" } }
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"name": "move",
|
|
12
|
+
"tool": "batch_set_layer",
|
|
13
|
+
"args": {
|
|
14
|
+
"handles": "$ref:selection.result",
|
|
15
|
+
"layerName": "TargetLayer"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "export-current-drawing",
|
|
3
|
+
"description": "导出当前图纸为 PDF。先获取图纸信息,再执行导出",
|
|
4
|
+
"steps": [
|
|
5
|
+
{
|
|
6
|
+
"name": "info",
|
|
7
|
+
"tool": "get_cad_info",
|
|
8
|
+
"args": {}
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"name": "export",
|
|
12
|
+
"tool": "export_pdf",
|
|
13
|
+
"args": {
|
|
14
|
+
"outputPath": "$ref:info.docName.pdf"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "batch-draw-lines",
|
|
3
|
+
"description": "批量绘制线段",
|
|
4
|
+
"arguments": [
|
|
5
|
+
{ "name": "count", "description": "线段数量", "required": true, "default": 5 },
|
|
6
|
+
{ "name": "length", "description": "线段长度", "required": false, "default": 1000 }
|
|
7
|
+
],
|
|
8
|
+
"templates": {
|
|
9
|
+
"default": {
|
|
10
|
+
"code": "(defun c:batch-lines (/ i)\n (setq i 0)\n (repeat {{count}}\n (command \"_.line\" (list (* i {{length}}) 0) (list (* (1+ i) {{length}}) 0) \"\")\n (setq i (1+ i))\n )\n (princ)\n)\n(c:batch-lines)",
|
|
11
|
+
"description": "批量绘制 {{count}} 条长度为 {{length}} 的水平线段:\n\n此代码将绘制 {{count}} 条首尾相连的水平线段。"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "coding-conventions",
|
|
3
|
+
"description": "AutoLISP 编码规范",
|
|
4
|
+
"arguments": [],
|
|
5
|
+
"templates": {
|
|
6
|
+
"default": {
|
|
7
|
+
"code": "",
|
|
8
|
+
"description": "## AutoLISP 编码规范\n\n### 禁止作为变量名的内置函数\n\n以下 AutoLISP 内置函数不可用作变量名,否则会覆盖内置函数导致程序错误:\n\n| 变量名 | 内置函数 | 说明 |\n|--------|----------|------|\n| `angle` | `(angle pt1 pt2)` | 严禁使用!获取两点连线与X轴夹角 |\n| `length` | `(length list)` | 获取列表长度 |\n| `type` | `(type symbol)` | 获取数据类型 |\n| `assoc` | `(assoc key alist)` | 关联表查找 |\n| `member` | `(member item list)` | 成员检测 |\n| `subst` | `(subst new old list)` | 替换列表元素 |\n| `append` | `(append list ...)` | 合并列表 |\n| `reverse` | `(reverse list)` | 反转列表 |\n| `apply` | `(apply fn list)` | 应用函数 |\n| `mapcar` | `(mapcar fn list...)` | 映射函数 |\n\n### 正确做法\n\n```lisp\n;; 错误:覆盖了内置 angle 函数\n(setq angle 45)\n\n;; 正确:使用有意义的变量名\n(setq rot-angle 45)\n(setq rotation 45)\n(setq ang 45)\n```"
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "color-convert",
|
|
3
|
+
"description": "颜色转换:ACI/RGB/TrueColor/CSS互转",
|
|
4
|
+
"arguments": [
|
|
5
|
+
{ "name": "action", "description": "操作 (aci2rgb/rgb2css/truecolor2rgb/rgb2truecolor/rgb)", "required": true, "default": "aci2rgb" },
|
|
6
|
+
{ "name": "value", "description": "颜色值", "required": true, "default": "1" }
|
|
7
|
+
],
|
|
8
|
+
"templates": {
|
|
9
|
+
"aci2rgb": {
|
|
10
|
+
"code": "(color:aci2rgb {{value}})",
|
|
11
|
+
"description": "ACI颜色索引 {{value}} 转RGB"
|
|
12
|
+
},
|
|
13
|
+
"rgb2css": {
|
|
14
|
+
"code": "(color:rgb2css (list {{value}}))",
|
|
15
|
+
"description": "RGB ({{value}}) 转CSS颜色 #FFFFFF"
|
|
16
|
+
},
|
|
17
|
+
"truecolor2rgb": {
|
|
18
|
+
"code": "(color:truecolor2rgb {{value}})",
|
|
19
|
+
"description": "TrueColor整数 {{value}} 转RGB列表"
|
|
20
|
+
},
|
|
21
|
+
"rgb2truecolor": {
|
|
22
|
+
"code": "(color:rgb2truecolor (list {{value}}))",
|
|
23
|
+
"description": "RGB ({{value}}) 转TrueColor整数"
|
|
24
|
+
},
|
|
25
|
+
"rgb": {
|
|
26
|
+
"code": "(color:rgb {{value}})",
|
|
27
|
+
"description": "计算RGB合成值:{{value}}"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "curve-analysis",
|
|
3
|
+
"description": "曲线分析:交点、最近点、子段、缓冲区",
|
|
4
|
+
"arguments": [
|
|
5
|
+
{ "name": "action", "description": "操作 (inters/subsegments/closestpoint/join)", "required": true, "default": "inters" },
|
|
6
|
+
{ "name": "distance", "description": "连接容差/偏移距离", "required": false, "default": "100" }
|
|
7
|
+
],
|
|
8
|
+
"templates": {
|
|
9
|
+
"inters": {
|
|
10
|
+
"code": "(progn\n (setq ss (ssget \"_X\" (list (cons 0 \"LWPOLYLINE,LINE,ARC,CIRCLE\"))))\n (if (and ss (>= (sslength ss) 2))\n (progn\n (setq elst (pickset:to-list ss))\n (setq e1 (car elst) e2 (cadr elst))\n (curve:inters e1 e2 0)\n )\n )\n)",
|
|
11
|
+
"description": "选择前两个曲线对象,计算其交点(可指定 LWPOLYLINE, LINE, ARC, CIRCLE)"
|
|
12
|
+
},
|
|
13
|
+
"subsegments": {
|
|
14
|
+
"code": "(progn\n (setq e (car (pickset:to-list (ssget \":S\" (list (cons 0 \"LWPOLYLINE\"))))))\n (if e (curve:subsegments e))\n)",
|
|
15
|
+
"description": "选择一个LWPOLYLINE,返回其子段数"
|
|
16
|
+
},
|
|
17
|
+
"closestpoint": {
|
|
18
|
+
"code": "(progn\n (setq e (car (pickset:to-list (ssget \":S\" (list (cons 0 \"LWPOLYLINE,LINE,ARC,CIRCLE\"))))))\n (if e (curve:pickclosepointto e (getpoint \"\\n选择参考点: \")))\n)",
|
|
19
|
+
"description": "选择一个曲线对象,然后拾取一个点,返回曲线上离该点最近的点"
|
|
20
|
+
},
|
|
21
|
+
"join": {
|
|
22
|
+
"code": "(progn\n (setq ss (ssget (list (cons 0 \"LINE,LWPOLYLINE\"))))\n (if ss (curve:join (pickset:to-list ss) {{distance}}))\n)",
|
|
23
|
+
"description": "选择LINE/LWPOLYLINE线段,用 PEDIT Join 合并,容差距离={{distance}}"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "excel-report",
|
|
3
|
+
"description": "Excel报表:导出CAD实体数据到Excel",
|
|
4
|
+
"arguments": [
|
|
5
|
+
{ "name": "visible", "description": "是否显示Excel窗口 (true/false)", "required": false, "default": "false" },
|
|
6
|
+
{ "name": "savePath", "description": "保存路径,不填则不保存", "required": false, "default": "" }
|
|
7
|
+
],
|
|
8
|
+
"handlerFile": "excel-report"
|
|
9
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "export-entities",
|
|
3
|
+
"description": "导出图纸实体统计",
|
|
4
|
+
"arguments": [
|
|
5
|
+
{ "name": "action", "description": "导出格式 (json/list)", "required": false, "default": "json" }
|
|
6
|
+
],
|
|
7
|
+
"templates": {
|
|
8
|
+
"json": {
|
|
9
|
+
"code": "(progn\n (setq ss(ssget \"_X\"))\n (if ss\n (progn\n (setq elst(mapcar(quote(lambda(x)(list(cdr(assoc 0(entget x)))(vlax-get x 'ObjectName))))(pickset:to-list ss)))\n (stat:stat(mapcar(quote car)elst))\n )\n )\n)",
|
|
10
|
+
"description": "导出实体统计 (json 格式):\n\n执行后将输出实体统计信息。"
|
|
11
|
+
},
|
|
12
|
+
"list": {
|
|
13
|
+
"code": "(progn\n (princ \"\\n实体统计:\\n\")\n (princ \"类型 数量\\n\")\n (princ \"---------------------\\n\")\n (setq ss(ssget \"_X\"))\n (if ss\n (progn\n (setq elst(mapcar(quote(lambda(x)(cdr(assoc 0(entget x))))) (pickset:to-list ss)))\n (foreach t '(\"LINE\" \"CIRCLE\" \"ARC\" \"TEXT\" \"MTEXT\" \"POLYLINE\" \"LWPOLYLINE\" \"INSERT\")\n (setq cnt(length(member t elst)))\n (if(> cnt 0)(princ(strcat t \" \"(itoa cnt) \"\\n\")))\n )\n )\n )\n)",
|
|
14
|
+
"description": "导出实体统计 (list 格式):\n\n执行后将输出实体统计信息。"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "geometry-calc",
|
|
3
|
+
"description": "几何计算:凸包、点到线距离、面内测试、变换",
|
|
4
|
+
"arguments": [
|
|
5
|
+
{ "name": "action", "description": "操作 (convexhull/dist-point-line/in-curve-p/transform/centroid)", "required": true, "default": "centroid" },
|
|
6
|
+
{ "name": "points", "description": "点集 \"x1,y1;x2,y2;...\"", "required": false, "default": "0,0;100,0;100,100;0,100" }
|
|
7
|
+
],
|
|
8
|
+
"handlerFile": "geometry-calc"
|
|
9
|
+
}
|