@atlisp/cli 1.2.0

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 (45) hide show
  1. package/.github/workflows/publish.yml +18 -0
  2. package/.github/workflows/test.yml +14 -0
  3. package/README.md +88 -0
  4. package/__tests__/__snapshots__/cli.test.js.snap +12 -0
  5. package/__tests__/build.test.js +48 -0
  6. package/__tests__/cli.test.js +246 -0
  7. package/__tests__/config.test.js +71 -0
  8. package/__tests__/deps.test.js +64 -0
  9. package/__tests__/index.test.js +63 -0
  10. package/__tests__/lint.test.js +58 -0
  11. package/atlisp.js +219 -0
  12. package/commands/batch.js +14 -0
  13. package/commands/build.js +267 -0
  14. package/commands/checksum.js +23 -0
  15. package/commands/config.js +264 -0
  16. package/commands/dcl-migrate.js +62 -0
  17. package/commands/doc.js +45 -0
  18. package/commands/doctor.js +292 -0
  19. package/commands/find.js +14 -0
  20. package/commands/index.js +33 -0
  21. package/commands/info.js +46 -0
  22. package/commands/init.js +202 -0
  23. package/commands/install.js +23 -0
  24. package/commands/lint.js +79 -0
  25. package/commands/list.js +99 -0
  26. package/commands/publish.js +237 -0
  27. package/commands/pull.js +118 -0
  28. package/commands/qrcode.js +33 -0
  29. package/commands/test.js +223 -0
  30. package/commands/watch.js +47 -0
  31. package/completion/atlisp-completion.bash +107 -0
  32. package/completion/atlisp-completion.ps1 +64 -0
  33. package/index.js +13 -0
  34. package/kernel-order.json +28 -0
  35. package/lib/atlisp.js +15 -0
  36. package/lib/batch.js +173 -0
  37. package/lib/cad.js +199 -0
  38. package/lib/common.js +189 -0
  39. package/lib/deps.js +121 -0
  40. package/lib/docgen.js +93 -0
  41. package/lib/find.js +154 -0
  42. package/lib/mcp-client.js +203 -0
  43. package/package.json +49 -0
  44. package/scripts/add-deps.js +81 -0
  45. package/scripts/fix-deps.js +98 -0
package/atlisp.js ADDED
@@ -0,0 +1,219 @@
1
+ #!/usr/bin/env node
2
+
3
+ const chalk = require("chalk");
4
+ const figlet = require("figlet");
5
+ const inquirer = require("inquirer");
6
+ const path = require("path");
7
+ const cmdreg = require("./commands/index");
8
+
9
+ // Load all command modules
10
+ cmdreg.load(path.join(__dirname, "commands"));
11
+
12
+ const COMMANDS = cmdreg.getAll();
13
+
14
+ const HELP = `@atlisp/cli v${require("./package.json").version}
15
+ 用法:
16
+ atlisp [命令] [选项]
17
+
18
+ 环境变量:
19
+ ATLISP_KERNEL_SRC 内核源码目录 (默认 ~/atlisp/kernel/src)
20
+ ATLISP_KERNEL_TEST 内核测试目录 (默认 ~/atlisp/kernel/test)
21
+ ATLISP_KERNEL_ROOT 内核根目录 (默认 ~/atlisp/kernel)
22
+
23
+ 命令:
24
+
25
+ 命令:
26
+ install [--auto] 显示 @lisp Core 安装代码(--auto 自动安装到 CAD)
27
+ pull <包名> [--auto] [--download] 拉取/下载应用包
28
+ remove <包名> [--auto] 卸载应用包
29
+ list 列出所有可用应用包
30
+ search <关键词> 搜索应用包
31
+ init <包名> [--template basic|dialog|ai|enterprise] 创建包项目脚手架
32
+ checksum <文件> 计算并添加 @checksum: 到模块文件
33
+ config get <键> 读取 @lisp 配置项
34
+ config set <键> <值> 设置 @lisp 配置项
35
+ config list 列出所有 @lisp 配置项
36
+ login [<邮箱>] 登录 @lisp 账号并保存 token
37
+ build [--out <路径>] 构建 @lisp_u.lsp 单体文件
38
+ build-module <模块名> [--out <路径>] 构建模块单体文件
39
+ deploy 部署 src/modules/*.lsp 到 dev.atlisp.cn
40
+ test [--platform sbcl|cad] [--list] [--load] [--manual] [--skip-passed] 运行测试
41
+ batch --path <路径> --expr <表达式> 批处理 DWG 文件
42
+ find --path <路径> --name <名称> 在 DWG 中查找元素
43
+ check 检查 CAD 连接状态
44
+ doctor [--fix] 环境诊断(--fix 自动修复)
45
+ qrcode <文本> 生成终端二维码
46
+ qrcode-ascii <文本> 生成终端二维码(ASCII 模式)
47
+ info <包名> 查看已安装包的详细信息
48
+ doc <函数名> 查询函数文档
49
+ sync [--push|--pull|--status] 触发配置同步
50
+ --version, -V 显示版本
51
+ --help, -h 显示帮助
52
+
53
+ 不带参数时启动交互式菜单。`;
54
+
55
+ function showBanner() {
56
+ console.log(
57
+ chalk.green(
58
+ figlet.textSync("@Lisp", {
59
+ font: "Ghost",
60
+ horizontalLayout: "default",
61
+ verticalLayout: "default",
62
+ })
63
+ )
64
+ );
65
+ console.log("@lisp — CAD 应用云管理器。支持 AutoCAD、中望CAD、浩辰CAD、BricsCAD。");
66
+ }
67
+
68
+ async function runCLI(args) {
69
+ const cmd = args[0];
70
+ const rest = args.slice(1);
71
+
72
+ if (cmd === "--version" || cmd === "-V") {
73
+ console.log(require("./package.json").version);
74
+ return;
75
+ }
76
+ if (cmd === "--help" || cmd === "-h" || cmd === undefined) {
77
+ console.log(HELP);
78
+ return;
79
+ }
80
+
81
+ const handler = cmdreg.get(cmd);
82
+ if (handler) {
83
+ await handler(rest);
84
+ } else {
85
+ console.error(chalk.red("未知命令: " + cmd + "\n"));
86
+ console.log(HELP);
87
+ process.exit(1);
88
+ }
89
+ }
90
+
91
+ async function runInteractive() {
92
+ showBanner();
93
+ const answers = await inquirer.prompt([
94
+ {
95
+ type: "list",
96
+ name: "COMMAND",
97
+ message: "请选择你要执行的任务?",
98
+ choices: [
99
+ "install core", "install core (auto)",
100
+ "pull package", "pull package (auto)", "download package (local)",
101
+ "remove package", "remove package (auto)",
102
+ "list packages", "search package",
103
+ new inquirer.Separator(),
104
+ "init new package project",
105
+ "query function doc",
106
+ "login @lisp account",
107
+ "build @lisp_u.lsp", "build network module", "build pkgman module",
108
+ new inquirer.Separator(),
109
+ "test (SBCL)", "test (CAD/MCP)", "check CAD", "doctor", "sync config",
110
+ "generate QR code", "generate QR code (ascii)",
111
+ new inquirer.Separator(),
112
+ "batch process DWGs", "find in DWGs",
113
+ ],
114
+ },
115
+ ]);
116
+ const { COMMAND } = answers;
117
+ if (COMMAND === "install core") {
118
+ await cmdreg.get("install")([]);
119
+ } else if (COMMAND === "install core (auto)") {
120
+ await cmdreg.get("install")(["--auto"]);
121
+ } else if (COMMAND === "pull package") {
122
+ const { PKGNAME } = await inquirer.prompt([
123
+ { name: "PKGNAME", type: "input", message: "请输入包名?" },
124
+ ]);
125
+ await cmdreg.get("pull")([PKGNAME]);
126
+ } else if (COMMAND === "pull package (auto)") {
127
+ const { PKGNAME } = await inquirer.prompt([
128
+ { name: "PKGNAME", type: "input", message: "请输入包名?" },
129
+ ]);
130
+ await cmdreg.get("pull")([PKGNAME, "--auto"]);
131
+ } else if (COMMAND === "download package (local)") {
132
+ const { PKGNAME } = await inquirer.prompt([
133
+ { name: "PKGNAME", type: "input", message: "请输入包名?" },
134
+ ]);
135
+ await cmdreg.get("pull")([PKGNAME, "--download"]);
136
+ } else if (COMMAND === "remove package") {
137
+ const { PKGNAME } = await inquirer.prompt([
138
+ { name: "PKGNAME", type: "input", message: "请输入要卸载的包名?" },
139
+ ]);
140
+ await cmdreg.get("remove")([PKGNAME]);
141
+ } else if (COMMAND === "remove package (auto)") {
142
+ const { PKGNAME } = await inquirer.prompt([
143
+ { name: "PKGNAME", type: "input", message: "请输入要卸载的包名?" },
144
+ ]);
145
+ await cmdreg.get("remove")([PKGNAME, "--auto"]);
146
+ } else if (COMMAND === "list packages") {
147
+ await cmdreg.get("list")([]);
148
+ } else if (COMMAND === "search package") {
149
+ const { QUERY } = await inquirer.prompt([
150
+ { name: "QUERY", type: "input", message: "请输入搜索关键词?" },
151
+ ]);
152
+ await cmdreg.get("search")([QUERY]);
153
+ } else if (COMMAND === "init new package project") {
154
+ const { PKGNAME } = await inquirer.prompt([
155
+ { name: "PKGNAME", type: "input", message: "请输入包名?" },
156
+ ]);
157
+ const { TEMPLATE } = await inquirer.prompt([
158
+ { name: "TEMPLATE", type: "list", message: "选择模板?",
159
+ choices: ["basic (minimal)", "dialog (with DCL)", "ai (with AI agent)", "enterprise (with RBAC)"] },
160
+ ]);
161
+ const tplMap = { "basic (minimal)": "basic", "dialog (with DCL)": "dialog", "ai (with AI agent)": "ai", "enterprise (with RBAC)": "enterprise" };
162
+ process.argv.push("--template", tplMap[TEMPLATE]);
163
+ await cmdreg.get("init")([PKGNAME]);
164
+ } else if (COMMAND === "query function doc") {
165
+ const { FUNCNAME } = await inquirer.prompt([
166
+ { name: "FUNCNAME", type: "input", message: "请输入函数名 (如 string:length)?" },
167
+ ]);
168
+ await cmdreg.get("doc")([FUNCNAME]);
169
+ } else if (COMMAND === "login @lisp account") {
170
+ await cmdreg.get("login")([]);
171
+ } else if (COMMAND === "build @lisp_u.lsp") {
172
+ await cmdreg.get("build")([]);
173
+ } else if (COMMAND === "build network module") {
174
+ await cmdreg.get("build-module")(["network"]);
175
+ } else if (COMMAND === "build pkgman module") {
176
+ await cmdreg.get("build-module")(["pkgman"]);
177
+ } else if (COMMAND === "test (SBCL)") {
178
+ await cmdreg.get("test")(["--platform", "sbcl"]);
179
+ } else if (COMMAND === "test (CAD/MCP)") {
180
+ await cmdreg.get("test")(["--platform", "cad"]);
181
+ } else if (COMMAND === "check CAD") {
182
+ await cmdreg.get("check")([]);
183
+ } else if (COMMAND === "doctor") {
184
+ await cmdreg.get("doctor")([]);
185
+ } else if (COMMAND === "sync config") {
186
+ await cmdreg.get("sync")(["--status"]);
187
+ } else if (COMMAND === "generate QR code") {
188
+ const { TEXT } = await inquirer.prompt([
189
+ { name: "TEXT", type: "input", message: "输入二维码内容?" },
190
+ ]);
191
+ await cmdreg.get("qrcode")([TEXT]);
192
+ } else if (COMMAND === "generate QR code (ascii)") {
193
+ const { TEXT } = await inquirer.prompt([
194
+ { name: "TEXT", type: "input", message: "输入二维码内容?" },
195
+ ]);
196
+ await cmdreg.get("qrcode-ascii")([TEXT]);
197
+ } else if (COMMAND === "batch process DWGs") {
198
+ const answers = await inquirer.prompt([
199
+ { name: "path", type: "input", message: "DWG 目录路径?" },
200
+ { name: "expr", type: "input", message: "Lisp 表达式?" },
201
+ { name: "save", type: "confirm", message: "保存修改?", default: false },
202
+ ]);
203
+ await cmdreg.get("batch")(["--path", answers.path, "--expr", answers.expr, "--save", String(answers.save), "--force"]);
204
+ } else if (COMMAND === "find in DWGs") {
205
+ const answers = await inquirer.prompt([
206
+ { name: "path", type: "input", message: "DWG 目录路径?" },
207
+ { name: "name", type: "input", message: "要查找的元素名称?" },
208
+ { name: "table", type: "input", message: "表名 (默认 Blocks)?", default: "Blocks" },
209
+ ]);
210
+ await cmdreg.get("find")(["--path", answers.path, "--name", answers.name, "--table", answers.table]);
211
+ }
212
+ }
213
+
214
+ const args = process.argv.slice(2);
215
+ if (args.length === 0) {
216
+ runInteractive().catch(err => console.error(chalk.red(err.message)));
217
+ } else {
218
+ runCLI(args).catch(err => console.error(chalk.red(err.message)));
219
+ }
@@ -0,0 +1,14 @@
1
+ const chalk = require("chalk");
2
+ const { batch } = require("../lib/batch");
3
+ const { parseArgs } = require("../lib/common");
4
+
5
+ async function cmdBatch(opts) {
6
+ const result = await batch(opts);
7
+ if (!result) process.exit(1);
8
+ }
9
+
10
+ function register(reg) {
11
+ reg("batch", (args) => cmdBatch(parseArgs(args)));
12
+ }
13
+
14
+ module.exports = { register };
@@ -0,0 +1,267 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const os = require("os");
4
+ const chalk = require("chalk");
5
+ const { parseArgs, getKernelSrc } = require("../lib/common");
6
+ const { topologicalSort, KERNEL_ORDER: ORDER } = require("../lib/deps");
7
+ const KERNEL_SRC = getKernelSrc();
8
+ const KERNEL_ROOT = path.dirname(KERNEL_SRC);
9
+
10
+ const MODULES_SRC = path.join(KERNEL_ROOT, "modules");
11
+ const KERNEL_ORDER = (() => {
12
+ try {
13
+ const cached = require("../kernel-order.json");
14
+ return cached;
15
+ } catch (e) {
16
+ // 无缓存文件时从注解推导
17
+ try {
18
+ return topologicalSort();
19
+ } catch (e2) {
20
+ console.error(chalk.yellow(" ⚠ kernel-order.json 不存在且无法从注解推导,扫描 src/ 目录"));
21
+ return [];
22
+ }
23
+ }
24
+ })();
25
+
26
+ const DEPS_RE = /;;\s*@depends\s+(.+)/;
27
+ const PROVIDES_RE = /;;\s*@provides\s+(.+)/;
28
+
29
+ function readAnnotations(filePath) {
30
+ if (!fs.existsSync(filePath)) return { deps: [], provides: [] };
31
+ const content = fs.readFileSync(filePath, "utf-8");
32
+ const firstLines = content.split("\n").slice(0, 10);
33
+ const depsLine = firstLines.find(l => DEPS_RE.test(l));
34
+ const provLine = firstLines.find(l => PROVIDES_RE.test(l));
35
+ const deps = depsLine ? depsLine.match(DEPS_RE)[1].trim().split(/\s+/) : [];
36
+ const provides = provLine ? provLine.match(PROVIDES_RE)[1].trim().split(/\s+/) : [];
37
+ return { deps, provides };
38
+ }
39
+
40
+ function validateDeps() {
41
+ const fileProvides = {};
42
+ const fileDeps = {};
43
+ const order = KERNEL_ORDER.length > 0 ? KERNEL_ORDER : (() => {
44
+ try {
45
+ const deps = require("../lib/deps");
46
+ const allFiles = [];
47
+ function walk(dir) {
48
+ for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
49
+ const p = path.join(dir, e.name);
50
+ if (e.isDirectory() && !e.name.startsWith(".")) walk(p);
51
+ else if (e.isFile() && e.name.endsWith(".lsp")) allFiles.push(path.relative(KERNEL_SRC, p));
52
+ }
53
+ }
54
+ walk(KERNEL_SRC);
55
+ return deps.topologicalSort(allFiles);
56
+ } catch (e) { return []; }
57
+ })();
58
+
59
+ for (const f of order) {
60
+ const fullPath = path.join(KERNEL_SRC, f);
61
+ const anno = readAnnotations(fullPath);
62
+ fileProvides[f] = anno.provides;
63
+ fileDeps[f] = anno.deps;
64
+ for (const p of anno.provides) {
65
+ if (fileProvides[p] && fileProvides[p] !== f) {
66
+ console.warn(chalk.yellow(` ⚠ Duplicate provides "${p}" in ${f} and ${fileProvides[p]}`));
67
+ }
68
+ }
69
+ }
70
+
71
+ let issues = [];
72
+ for (const f of KERNEL_ORDER) {
73
+ const allProvided = new Set();
74
+ for (const prev of KERNEL_ORDER) {
75
+ if (prev === f) break;
76
+ for (const p of (fileProvides[prev] || [])) allProvided.add(p);
77
+ }
78
+ for (const dep of (fileDeps[f] || [])) {
79
+ if (!allProvided.has(dep)) {
80
+ issues.push(`${f}: missing dependency "${dep}" (not provided by any preceding file)`);
81
+ }
82
+ }
83
+ }
84
+
85
+ if (issues.length > 0) {
86
+ console.error(chalk.red("\n ✗ Dependency validation failed:\n"));
87
+ for (const issue of issues) {
88
+ console.error(chalk.red(" " + issue));
89
+ }
90
+ console.log("");
91
+ return false;
92
+ }
93
+ return true;
94
+ }
95
+
96
+ function findModuleSubfiles(moduleName) {
97
+ const moduleDir = path.join(MODULES_SRC, moduleName);
98
+ if (!fs.existsSync(moduleDir)) return [];
99
+ function collectFiles(dir, prefix) {
100
+ let result = [];
101
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
102
+ for (const entry of entries.sort()) {
103
+ const relPath = prefix ? path.join(prefix, entry.name) : entry.name;
104
+ if (entry.isDirectory()) {
105
+ result = result.concat(collectFiles(path.join(dir, entry.name), relPath));
106
+ } else if (entry.isFile() && entry.name.endsWith(".lsp") &&
107
+ entry.name.startsWith(moduleName + "-")) {
108
+ result.push(relPath);
109
+ }
110
+ }
111
+ return result;
112
+ }
113
+ return collectFiles(moduleDir, moduleName);
114
+ }
115
+
116
+ function concatFiles(files, srcDir, baseDir) {
117
+ const parts = [];
118
+ for (const f of files) {
119
+ const fullPath = path.join(srcDir, f);
120
+ if (!fs.existsSync(fullPath)) {
121
+ console.error(chalk.red(`错误: 找不到文件 ${fullPath}`));
122
+ process.exit(1);
123
+ }
124
+ const content = fs.readFileSync(fullPath, "utf-8").replace(/[ \t]+$/gm, "");
125
+ const header = baseDir
126
+ ? `;; ── ${path.relative(baseDir, fullPath)} ──────────────────────────────────────────\n`
127
+ : `;; ── ${f} ──────────────────────────────────────────\n`;
128
+ parts.push(header + content);
129
+ }
130
+ return parts.join("\n\n");
131
+ }
132
+
133
+ function buildModule(moduleName, outPath) {
134
+ const subfiles = findModuleSubfiles(moduleName);
135
+ if (subfiles.length === 0) {
136
+ console.error(chalk.red(`错误: 在 ${path.join(MODULES_SRC, moduleName)}/ 中未找到子文件`));
137
+ process.exit(1);
138
+ }
139
+ if (!outPath) {
140
+ outPath = path.join(MODULES_SRC, moduleName + ".lsp");
141
+ }
142
+ console.log(chalk.cyan(`正在构建 modules/${moduleName}.lsp...`));
143
+ const concatenated = concatFiles(subfiles, MODULES_SRC, MODULES_SRC);
144
+ const fileList = subfiles.map(f => `;; ║ ${f}`).join("\n");
145
+ const header = `;; ╔══════════════════════════════════════════════════════════╗
146
+ ;; ║ ${moduleName}.lsp - @lisp module (monolithic build)${" ".repeat(Math.max(0, 43 - moduleName.length))}║
147
+ ;; ║ ║
148
+ ;; ║ Auto-generated from ${moduleName}/*.lsp sub-files.${" ".repeat(Math.max(0, 38 - moduleName.length))}║
149
+ ${fileList}
150
+ ;; ╚══════════════════════════════════════════════════════════╝
151
+
152
+ `;
153
+ const output = header + concatenated + "\n";
154
+ fs.writeFileSync(outPath, output, "utf-8");
155
+ console.log(chalk.green(`✓ ${subfiles.length} 个子文件已合并 → ${outPath}`));
156
+ }
157
+
158
+ async function cmdBuild(outPath, opts = {}) {
159
+ const srcDir = KERNEL_SRC || getKernelSrc();
160
+ if (!outPath) {
161
+ outPath = path.join(os.homedir(), "@lisp", "@lisp_u.lsp");
162
+ }
163
+
164
+ if (opts.lint) {
165
+ console.log(chalk.cyan("运行 lint 检查..."));
166
+ try {
167
+ const { cmdLint, collectLspFiles } = require("./lint");
168
+ await cmdLint(srcDir, false);
169
+ console.log(chalk.green(" ✓ lint 通过\n"));
170
+ } catch (e) {
171
+ if (process.env.CI) process.exit(1);
172
+ }
173
+ }
174
+
175
+ console.log(chalk.cyan("验证源文件依赖..."));
176
+ const depsOk = validateDeps();
177
+ if (!depsOk) {
178
+ if (process.env.CI) {
179
+ process.exit(1);
180
+ }
181
+ console.log(chalk.yellow(" 继续构建(设置 CI=1 可在依赖错误时终止)\n"));
182
+ } else {
183
+ console.log(chalk.green(" ✓ 依赖验证通过\n"));
184
+ }
185
+
186
+ console.log(chalk.cyan("正在构建 @lisp_u.lsp..."));
187
+ const output = concatFiles(KERNEL_ORDER, srcDir);
188
+ const outDir = path.dirname(outPath);
189
+ if (!fs.existsSync(outDir)) {
190
+ fs.mkdirSync(outDir, { recursive: true });
191
+ }
192
+ fs.writeFileSync(outPath, output, "utf-8");
193
+ console.log(chalk.green(`✓ ${KERNEL_ORDER.length} 个文件已合并 → ${outPath}`));
194
+ const moduleSrcDir = MODULES_SRC;
195
+ if (fs.existsSync(moduleSrcDir)) {
196
+ for (const entry of fs.readdirSync(moduleSrcDir, { withFileTypes: true })) {
197
+ if (entry.isDirectory()) {
198
+ const subfiles = findModuleSubfiles(entry.name);
199
+ if (subfiles.length > 0) {
200
+ buildModule(entry.name, path.join(moduleSrcDir, entry.name + ".lsp"));
201
+ }
202
+ }
203
+ }
204
+ }
205
+ }
206
+
207
+ async function cmdBuildModule(moduleName, outPath) {
208
+ if (!moduleName) {
209
+ console.log(chalk.cyan("\n可用的模块:"));
210
+ const entries = fs.readdirSync(MODULES_SRC, { withFileTypes: true });
211
+ let found = false;
212
+ for (const entry of entries) {
213
+ if (entry.isDirectory()) {
214
+ const subfiles = findModuleSubfiles(entry.name);
215
+ if (subfiles.length > 0) {
216
+ console.log(chalk.gray(" " + entry.name + " (" + subfiles.length + " 个子文件)"));
217
+ found = true;
218
+ }
219
+ }
220
+ }
221
+ if (!found) console.log(chalk.gray(" (没有可通过 build-module 构建的模块)"));
222
+ console.log(chalk.cyan("\n用法: atlisp build-module <模块名> [--out <路径>]"));
223
+ console.log(chalk.gray("示例: atlisp build-module network"));
224
+ return;
225
+ }
226
+ return buildModule(moduleName, outPath);
227
+ }
228
+
229
+ async function handler(args) {
230
+ const cmd = args[0];
231
+ const val = args[1];
232
+ const opts = {};
233
+ for (let i = 0; i < args.length; i++) {
234
+ if (args[i].startsWith("--")) {
235
+ const key = args[i].slice(2).replace(/-([a-z])/g, (_, c) => c.toUpperCase());
236
+ const v = args[i + 1] && !args[i + 1].startsWith("--") ? args[i + 1] : true;
237
+ if (v !== true) i++;
238
+ opts[key] = v;
239
+ }
240
+ }
241
+ if (cmd === "build-module" || cmd === "bm") {
242
+ await cmdBuildModule(val, opts.out);
243
+ } else if (cmd === "build-network" || cmd === "bn") {
244
+ console.log(chalk.yellow("⚠ build-network 已弃用,请使用 build-module network"));
245
+ await cmdBuildModule("network", typeof val === "string" ? val : undefined);
246
+ } else {
247
+ await cmdBuild(val, opts);
248
+ }
249
+ }
250
+
251
+ function register(reg) {
252
+ reg("build", (args) => {
253
+ const opts = parseArgs(args);
254
+ cmdBuild(opts.out, opts);
255
+ }, ["b"]);
256
+ reg("build-module", (args) => {
257
+ const opts = parseArgs(args);
258
+ cmdBuildModule(args[0], opts.out);
259
+ }, ["bm"]);
260
+ reg("build-network", (args) => {
261
+ console.log(chalk.yellow("⚠ 已弃用,请使用 build-module network"));
262
+ const opts = parseArgs(args);
263
+ cmdBuildModule("network", opts.out);
264
+ }, ["bn"]);
265
+ }
266
+
267
+ module.exports = { register, buildModule, findModuleSubfiles, concatFiles, cmdBuild, KERNEL_SRC, MODULES_SRC, KERNEL_ORDER };
@@ -0,0 +1,23 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const chalk = require("chalk");
4
+ const common = require("../lib/common");
5
+
6
+ async function handler(args) {
7
+ const filePath = args[0];
8
+ if (!filePath) {
9
+ console.error(chalk.red("用法: atlisp checksum <文件路径>"));
10
+ process.exit(1);
11
+ }
12
+ const content = fs.readFileSync(filePath, "utf-8");
13
+ const cleanEnd = content.replace(/\n;; @checksum: -?\d+\s*$/, "");
14
+ const hash = common.computeChecksum(cleanEnd);
15
+ fs.writeFileSync(filePath, cleanEnd + "\n;; @checksum: " + hash + "\n");
16
+ console.log(chalk.green("Checksum: " + hash + " → " + filePath));
17
+ }
18
+
19
+ function register(reg) {
20
+ reg("checksum", handler, ["cs"]);
21
+ }
22
+
23
+ module.exports = { register };