@chatbi-v/cli 2.0.4 → 2.0.5

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/index.js CHANGED
@@ -14,12 +14,12 @@ var __export = (target, all) => {
14
14
  __defProp(target, name, { get: all[name], enumerable: true });
15
15
  };
16
16
 
17
- // ../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/esm_shims.js
17
+ // ../../../chatbi-v/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/esm_shims.js
18
18
  import path from "path";
19
19
  import { fileURLToPath } from "url";
20
20
  var getFilename, getDirname, __dirname;
21
21
  var init_esm_shims = __esm({
22
- "../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/esm_shims.js"() {
22
+ "../../../chatbi-v/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/esm_shims.js"() {
23
23
  "use strict";
24
24
  getFilename = () => fileURLToPath(import.meta.url);
25
25
  getDirname = () => path.dirname(getFilename());
@@ -84,11 +84,7 @@ var init_utils = __esm({
84
84
  if (pkgName === "@chatbi-v/cli") {
85
85
  return path2.resolve(_dirname, "../../");
86
86
  }
87
- const localPath = path2.resolve(_dirname, "../../node_modules", pkgName);
88
- if (fs.existsSync(localPath)) {
89
- return localPath;
90
- }
91
- throw new Error(`\u65E0\u6CD5\u627E\u5230\u5305 ${pkgName}\uFF0C\u8BF7\u786E\u4FDD\u5DF2\u901A\u8FC7 pnpm/npm \u5B89\u88C5\u3002`);
87
+ return null;
92
88
  }
93
89
  };
94
90
  getCliRoot = async () => {
@@ -105,269 +101,299 @@ var init_utils = __esm({
105
101
  checkDir = path2.dirname(checkDir);
106
102
  }
107
103
  if (!myCliRoot) {
108
- myCliRoot = findPackageRoot("@chatbi-v/cli");
104
+ myCliRoot = findPackageRoot("@chatbi-v/cli") || "";
109
105
  }
110
106
  return myCliRoot;
111
107
  };
112
108
  }
113
109
  });
114
110
 
115
- // src/sandbox.ts
116
- import fs2 from "fs-extra";
111
+ // src/constants.ts
117
112
  import path3 from "path";
118
113
  import os from "os";
119
- import pc2 from "picocolors";
120
- import { execa } from "execa";
121
- import Handlebars from "handlebars";
122
- import fg from "fast-glob";
123
- var Sandbox;
124
- var init_sandbox = __esm({
125
- "src/sandbox.ts"() {
114
+ var SANDBOX_CONFIG, GLOBAL_PATHS, DEPENDENCY_VERSIONS, DEFAULT_CONFIG;
115
+ var init_constants = __esm({
116
+ "src/constants.ts"() {
126
117
  "use strict";
127
118
  init_esm_shims();
128
- init_utils();
129
- Sandbox = class _Sandbox {
130
- static {
131
- this.BASE_DIR = path3.join(os.homedir(), ".chatbi-v-core");
132
- }
133
- static {
134
- this.CORE_PACKAGES = [
135
- "@chatbi-v/core",
136
- "@chatbi-v/mocks",
137
- "@chatbi-v/tsconfig",
138
- "@chatbi-v/tailwind-config",
139
- "@chatbi-v/plugin-theme-manager",
140
- "@chatbi-v/plugin-layout-transform",
141
- "@chatbi-v/plugin-system-monitor"
142
- ];
143
- }
119
+ SANDBOX_CONFIG = {
120
+ /** 用户主目录下的沙箱根目录名 */
121
+ BASE_NAME: ".chatbi-v-core",
122
+ /** 内部子目录结构 */
123
+ DIRS: {
124
+ VERSIONS: "versions",
125
+ CURRENT: "current",
126
+ CACHE: ".chatbi"
127
+ },
128
+ /** 关键标识文件 */
129
+ LOCK_FILE: ".chatbi-version"
130
+ };
131
+ GLOBAL_PATHS = {
132
+ /** 沙箱根路径 */
133
+ BASE_DIR: path3.join(os.homedir(), SANDBOX_CONFIG.BASE_NAME),
134
+ /** Monorepo 扫描目录 */
135
+ MONOREPO_ROOTS: ["apps", "plugins", "packages"]
136
+ };
137
+ DEPENDENCY_VERSIONS = {
138
+ // 基础框架
139
+ "react": "^18.3.1",
140
+ "react-dom": "^18.3.1",
141
+ "antd": "^5.20.0",
142
+ // 构建工具
143
+ "vite": "^5.0.8",
144
+ "typescript": "^5.3.3",
145
+ "tailwindcss": "^3.4.1",
146
+ "autoprefixer": "^10.4.17",
147
+ "postcss": "^8.4.35",
148
+ "less": "^4.2.0",
149
+ // 类型定义
150
+ "@types/react": "^18.3.1",
151
+ "@types/react-dom": "^18.3.1",
152
+ "@types/node": "^20.11.20",
153
+ "@vitejs/plugin-react": "^4.2.1"
154
+ };
155
+ DEFAULT_CONFIG = {
156
+ /** 支持的配置文件名 */
157
+ CONFIG_FILES: [
158
+ "chatbi.config.ts",
159
+ "chatbi.config.js",
160
+ "chatbi.config.json",
161
+ ".chatbirc"
162
+ ],
163
+ /** 默认 UI 主题 */
164
+ THEME: "standard"
165
+ };
166
+ }
167
+ });
168
+
169
+ // src/sandbox/SandboxPath.ts
170
+ import path4 from "path";
171
+ import fs2 from "fs-extra";
172
+ var SandboxPath;
173
+ var init_SandboxPath = __esm({
174
+ "src/sandbox/SandboxPath.ts"() {
175
+ "use strict";
176
+ init_esm_shims();
177
+ init_constants();
178
+ SandboxPath = class {
144
179
  static {
145
- this.RUNTIME_DEPS = [
146
- "@ant-design/x",
147
- "@ant-design/icons",
148
- "antd",
149
- "react",
150
- "react-dom",
151
- "lucide-react",
152
- "framer-motion",
153
- "clsx",
154
- "tailwind-merge",
155
- "react-router-dom",
156
- "zustand",
157
- "axios",
158
- "less",
159
- "vite"
160
- ];
180
+ /**
181
+ * 沙箱的基础存储目录,通常位于用户主目录下的 .chatbi-v-core
182
+ */
183
+ this.BASE_DIR = GLOBAL_PATHS.BASE_DIR;
161
184
  }
185
+ /**
186
+ * 获取沙箱根目录
187
+ * @returns 沙箱根目录的绝对路径
188
+ */
162
189
  static getRoot() {
163
190
  return this.BASE_DIR;
164
191
  }
192
+ /**
193
+ * 获取存储所有版本的根目录
194
+ * @returns versions 目录的绝对路径
195
+ */
165
196
  static getVersionRoot() {
166
- return path3.join(this.BASE_DIR, "versions");
197
+ return path4.join(this.BASE_DIR, SANDBOX_CONFIG.DIRS.VERSIONS);
167
198
  }
199
+ /**
200
+ * 获取指定版本的沙箱路径
201
+ * @param version 版本号,或者 'current' 表示当前激活版本
202
+ * @returns 对应版本的绝对路径
203
+ */
168
204
  static getVersionPath(version) {
169
- if (version === "current") {
170
- return path3.join(this.BASE_DIR, "versions", "current");
205
+ if (version === SANDBOX_CONFIG.DIRS.CURRENT) {
206
+ return path4.join(this.BASE_DIR, SANDBOX_CONFIG.DIRS.VERSIONS, SANDBOX_CONFIG.DIRS.CURRENT);
171
207
  }
172
- return path3.join(this.BASE_DIR, "versions", version);
208
+ return path4.join(this.BASE_DIR, SANDBOX_CONFIG.DIRS.VERSIONS, version);
173
209
  }
174
210
  /**
175
- * 优雅清理缓存环境
176
- * @param cwd 当前工作目录
177
- * @param deep 是否递归清理子包
211
+ * 递归向上查找工作区根目录
212
+ * 识别标准:包含 pnpm-workspace.yaml 或 package.json 中定义了 workspaces
213
+ * @param cwd 起始查找目录
214
+ * @returns 查找到的工作区根目录路径,若未找到则返回 cwd
178
215
  */
179
- static async clean(cwd, deep = false) {
180
- const localCache = path3.join(cwd, ".chatbi");
181
- if (fs2.existsSync(localCache)) {
182
- await fs2.remove(localCache);
183
- }
184
- if (deep) {
185
- const pkgPath = path3.join(cwd, "package.json");
216
+ static async getWorkspaceRoot(cwd) {
217
+ let current = cwd;
218
+ while (current !== path4.parse(current).root) {
219
+ const pkgPath = path4.join(current, "package.json");
220
+ const pnpmWorkspacePath = path4.join(current, "pnpm-workspace.yaml");
221
+ if (fs2.existsSync(pnpmWorkspacePath)) return current;
186
222
  if (fs2.existsSync(pkgPath)) {
187
- const pkg = await fs2.readJson(pkgPath);
188
- if (pkg.workspaces || fs2.existsSync(path3.join(cwd, "pnpm-workspace.yaml"))) {
189
- const subDirs = ["apps", "plugins", "packages"];
190
- for (const dir of subDirs) {
191
- const dirPath = path3.join(cwd, dir);
192
- if (fs2.existsSync(dirPath)) {
193
- const entries = await fs2.readdir(dirPath, { withFileTypes: true });
194
- for (const entry of entries) {
195
- if (entry.isDirectory()) {
196
- const subPkgCache = path3.join(dirPath, entry.name, ".chatbi");
197
- if (fs2.existsSync(subPkgCache)) {
198
- await fs2.remove(subPkgCache);
199
- }
200
- }
201
- }
202
- }
203
- }
223
+ try {
224
+ const pkg = await fs2.readJson(pkgPath);
225
+ if (pkg.workspaces) return current;
226
+ } catch (e) {
204
227
  }
205
228
  }
229
+ current = path4.dirname(current);
206
230
  }
207
- if (fs2.existsSync(this.BASE_DIR)) {
208
- await fs2.remove(this.BASE_DIR);
209
- }
231
+ return cwd;
232
+ }
233
+ };
234
+ }
235
+ });
236
+
237
+ // src/sandbox/SandboxRenderer.ts
238
+ import fs3 from "fs-extra";
239
+ import path5 from "path";
240
+ import Handlebars from "handlebars";
241
+ var SandboxRenderer;
242
+ var init_SandboxRenderer = __esm({
243
+ "src/sandbox/SandboxRenderer.ts"() {
244
+ "use strict";
245
+ init_esm_shims();
246
+ Handlebars.registerHelper("json", (obj) => JSON.stringify(obj, null, 2));
247
+ Handlebars.registerHelper("eq", (a, b) => a === b);
248
+ SandboxRenderer = class {
249
+ static {
250
+ /**
251
+ * 需要转义的模板关键字列表
252
+ * 只有匹配这些关键字的 {{ }} 才会被 Handlebars 处理,其余(如 React 的 {{ style }})将被转义跳过
253
+ */
254
+ this.TEMPLATE_KEYWORDS = [
255
+ "name",
256
+ "version",
257
+ "projectName",
258
+ "projectTitle",
259
+ "projectVersion",
260
+ "cliVersion",
261
+ "tsconfigPath",
262
+ "theme",
263
+ "isNebula",
264
+ "isGlass",
265
+ "isBusiness",
266
+ "isApp",
267
+ "isShell",
268
+ "pluginName",
269
+ "pluginPackageName",
270
+ "pluginVersion",
271
+ "pluginDisplayName",
272
+ "pluginDescription",
273
+ "pluginClassName",
274
+ "pluginPath",
275
+ "pluginFolderName",
276
+ "pluginType",
277
+ "pluginId",
278
+ "className",
279
+ "dependencies",
280
+ "devDependencies",
281
+ "json"
282
+ ];
210
283
  }
211
284
  /**
212
- * 准备内核沙箱环境 (安装/更新)
285
+ * 递归渲染目录模板
286
+ * @param srcDir 模板源目录
287
+ * @param destDir 目标输出目录
288
+ * @param data 渲染模板所需的变量上下文
213
289
  */
214
- static async prepare(version, force = false) {
215
- const versionPath = this.getVersionPath(version);
216
- if (fs2.existsSync(versionPath) && !force) {
217
- if (fs2.existsSync(path3.join(versionPath, "node_modules/tailwindcss")) && fs2.existsSync(path3.join(versionPath, "node_modules/react-router-dom"))) {
218
- return versionPath;
219
- }
220
- }
221
- if (fs2.existsSync(versionPath)) {
222
- await fs2.remove(path3.join(versionPath, "node_modules"));
223
- await fs2.remove(path3.join(versionPath, "pnpm-lock.yaml"));
224
- }
225
- await fs2.ensureDir(versionPath);
226
- const spinner = createSpinner(`\u6B63\u5728\u521D\u59CB\u5316\u5185\u6838\u6C99\u7BB1 ${pc2.cyan(version)}...`).start();
227
- try {
228
- const rootPkgJson = {
229
- name: `chatbi-sandbox-${version}`,
230
- private: true,
231
- dependencies: {},
232
- devDependencies: {
233
- "tailwindcss": "^3.4.1",
234
- "autoprefixer": "^10.4.17",
235
- "postcss": "^8.4.35",
236
- "vite": "^5.0.8",
237
- "@vitejs/plugin-react": "^4.2.1",
238
- "less": "^4.2.0",
239
- "typescript": "^5.3.3",
240
- "@types/react": "^18.3.1",
241
- "@types/react-dom": "^18.3.1",
242
- "@types/node": "^20.11.20"
243
- }
244
- };
245
- for (const pkgName of _Sandbox.CORE_PACKAGES) {
246
- rootPkgJson.dependencies[pkgName] = pkgName === "@chatbi-v/core" ? version : "latest";
247
- }
248
- for (const pkgName of _Sandbox.RUNTIME_DEPS) {
249
- if (!rootPkgJson.dependencies[pkgName]) {
250
- rootPkgJson.dependencies[pkgName] = "latest";
251
- }
252
- }
253
- rootPkgJson.dependencies["react"] = "^18.3.1";
254
- rootPkgJson.dependencies["react-dom"] = "^18.3.1";
255
- rootPkgJson.dependencies["antd"] = "^5.20.0";
256
- await fs2.writeJson(path3.join(versionPath, "package.json"), rootPkgJson, { spaces: 2 });
257
- await fs2.writeFile(path3.join(versionPath, ".npmrc"), "shamefully-hoist=true\nauto-install-peers=true\n");
258
- spinner.text = pc2.gray(" \u{1F50D} \u68C0\u67E5\u672C\u5730\u5F00\u53D1\u73AF\u5883...");
259
- const isLocalDev = await this.tryLinkLocalPackages(versionPath);
260
- const installArgs = ["install"];
261
- if (isLocalDev) {
262
- installArgs.push("--no-frozen-lockfile");
290
+ static async renderDirectory(srcDir, destDir, data) {
291
+ if (!fs3.existsSync(srcDir)) return;
292
+ const entries = await fs3.readdir(srcDir, { withFileTypes: true });
293
+ for (const entry of entries) {
294
+ const srcPath = path5.join(srcDir, entry.name);
295
+ const destPath = path5.join(destDir, entry.name.replace(/\.hbs$/, ""));
296
+ if (entry.isDirectory()) {
297
+ await fs3.ensureDir(destPath);
298
+ await this.renderDirectory(srcPath, destPath, data);
299
+ } else {
300
+ await this.renderFile(srcPath, destPath, data);
263
301
  }
264
- spinner.text = pc2.gray(` \u23F3 \u6267\u884C pnpm ${installArgs.join(" ")}...`);
265
- await execa("pnpm", installArgs, { cwd: versionPath });
266
- spinner.text = pc2.gray(" \u{1F3A8} \u540C\u6B65 Shell \u6A21\u677F...");
267
- await this.ensureShell(version, force);
268
- spinner.succeed(pc2.green(`\u5185\u6838\u6C99\u7BB1 ${version} \u521D\u59CB\u5316\u6210\u529F`));
269
- } catch (e) {
270
- spinner.fail(pc2.red(`\u5185\u6838\u6C99\u7BB1\u521D\u59CB\u5316\u5931\u8D25: ${e.message}`));
271
- throw e;
272
302
  }
273
- return versionPath;
274
303
  }
275
304
  /**
276
- * 确保 Shell 模板已同步到沙箱
305
+ * 渲染单个文件
277
306
  */
278
- static async ensureShell(version, force = false) {
279
- const versionPath = this.getVersionPath(version);
280
- const shellDestDir = path3.join(versionPath, "shell");
281
- const needsRender = force || !fs2.existsSync(shellDestDir) || !fs2.existsSync(path3.join(shellDestDir, "tsconfig.paths.json"));
282
- if (!needsRender) {
283
- return shellDestDir;
307
+ static async renderFile(srcPath, destPath, data) {
308
+ const isTemplate = srcPath.endsWith(".hbs");
309
+ if (!isTemplate) {
310
+ await fs3.copy(srcPath, destPath);
311
+ return;
284
312
  }
285
- const cliRoot = await getCliRoot();
286
- const shellTemplateDir = path3.join(cliRoot, "templates", "app");
287
- if (fs2.existsSync(shellTemplateDir)) {
288
- await fs2.remove(shellDestDir);
289
- await fs2.ensureDir(path3.join(shellDestDir, "empty"));
290
- let kernelVersion = version;
291
- const templateData = {
292
- name: "chatbi-shell",
293
- projectName: "chatbi-shell",
294
- projectTitle: "ChatBI Shell",
295
- projectVersion: "0.1.0",
296
- version: kernelVersion,
297
- cliVersion: version,
298
- theme: "standard",
299
- isNebula: false,
300
- isGlass: false,
301
- isBusiness: true,
302
- isApp: true,
303
- isShell: true,
304
- tsconfigPath: "./tsconfig.paths.json"
305
- };
306
- await this.renderDirectory(shellTemplateDir, shellDestDir, templateData);
307
- const tsConfigPaths = {
308
- compilerOptions: {
309
- baseUrl: ".",
310
- paths: {
311
- "@/*": ["./src/*"]
312
- }
313
- }
314
- };
315
- await fs2.writeJson(path3.join(shellDestDir, "tsconfig.paths.json"), tsConfigPaths, { spaces: 2 });
313
+ const content = await fs3.readFile(srcPath, "utf-8");
314
+ if (!content.includes("{{")) {
315
+ await fs3.outputFile(destPath, content);
316
+ return;
317
+ }
318
+ try {
319
+ const keywordsPattern = this.TEMPLATE_KEYWORDS.join("|");
320
+ const safeContent = content.replace(
321
+ new RegExp(`\\{\\{(?!\\{)(?!\\s*([#/]?(?:${keywordsPattern}))\\b)`, "g"),
322
+ "\\{{"
323
+ );
324
+ const template = Handlebars.compile(safeContent);
325
+ const result = template(data);
326
+ await fs3.outputFile(destPath, result);
327
+ } catch (e) {
328
+ let result = content;
329
+ for (const [key, val] of Object.entries(data)) {
330
+ const regex = new RegExp(`\\{\\{\\s*${key}\\s*\\}\\}`, "g");
331
+ result = result.replace(regex, typeof val === "object" ? JSON.stringify(val) : String(val));
332
+ }
333
+ await fs3.outputFile(destPath, result);
316
334
  }
317
- return shellDestDir;
318
335
  }
336
+ };
337
+ }
338
+ });
339
+
340
+ // src/sandbox/SandboxContext.ts
341
+ import fs4 from "fs-extra";
342
+ import path6 from "path";
343
+ var SandboxContext;
344
+ var init_SandboxContext = __esm({
345
+ "src/sandbox/SandboxContext.ts"() {
346
+ "use strict";
347
+ init_esm_shims();
348
+ init_SandboxPath();
349
+ init_constants();
350
+ SandboxContext = class {
319
351
  /**
320
- * 递归渲染目录模板
352
+ * 注入项目虚拟上下文
353
+ * 包括:
354
+ * 1. 确定集中式 .chatbi 存放位置并处理子包软链
355
+ * 2. 生成适配沙箱的 tsconfig.json (配置路径别名)
356
+ * 3. 软链接沙箱的 node_modules 到项目 .chatbi 目录下
357
+ * 4. 确保 .chatbi 被加入 .gitignore
358
+ *
359
+ * @param projectRoot 当前项目根目录
360
+ * @param version 内核版本号
361
+ * @param corePackages 核心包名列表
362
+ * @param runtimeDeps 运行时依赖包名列表
321
363
  */
322
- static async renderDirectory(srcDir, destDir, data) {
323
- const entries = await fs2.readdir(srcDir, { withFileTypes: true });
324
- for (const entry of entries) {
325
- const srcPath = path3.join(srcDir, entry.name);
326
- const destPath = path3.join(destDir, entry.name.replace(/\.hbs$/, ""));
327
- if (entry.isDirectory()) {
328
- await fs2.ensureDir(destPath);
329
- await this.renderDirectory(srcPath, destPath, data);
330
- } else {
331
- if (entry.name.endsWith(".hbs")) {
332
- const content = await fs2.readFile(srcPath, "utf-8");
333
- const hasHandlebarsVars = content.includes("{{");
334
- if (hasHandlebarsVars) {
335
- try {
336
- const safeContent = content.replace(/\{\{(?!\s*([#/]?(?:if|else|each|with|unless|name|version|projectName|projectTitle|projectVersion|cliVersion|tsconfigPath|theme|isNebula|isGlass|isBusiness|isApp|isShell|pluginName|pluginPackageName|pluginVersion|pluginDisplayName|pluginDescription|pluginClassName|pluginPath|pluginFolderName|pluginType|pluginId|className))\b)/g, "\\{{");
337
- const template = Handlebars.compile(safeContent);
338
- const result = template(data);
339
- await fs2.outputFile(destPath, result);
340
- } catch (e) {
341
- let result = content;
342
- for (const [key, val] of Object.entries(data)) {
343
- const regex = new RegExp(`\\{\\{\\s*${key}\\s*\\}\\}`, "g");
344
- result = result.replace(regex, String(val));
345
- }
346
- await fs2.outputFile(destPath, result);
364
+ static async inject(projectRoot, version, corePackages, runtimeDeps) {
365
+ const workspaceRoot = await SandboxPath.getWorkspaceRoot(projectRoot);
366
+ const chatbiDir = path6.join(workspaceRoot, SANDBOX_CONFIG.DIRS.CACHE);
367
+ await fs4.ensureDir(chatbiDir);
368
+ if (workspaceRoot !== projectRoot) {
369
+ const localChatbi = path6.join(projectRoot, SANDBOX_CONFIG.DIRS.CACHE);
370
+ try {
371
+ const stats = await fs4.lstat(localChatbi).catch(() => null);
372
+ if (stats) {
373
+ if (stats.isSymbolicLink()) {
374
+ const linkTarget = await fs4.readlink(localChatbi);
375
+ if (linkTarget !== chatbiDir) {
376
+ await fs4.remove(localChatbi);
377
+ await fs4.symlink(chatbiDir, localChatbi, "dir");
347
378
  }
348
379
  } else {
349
- await fs2.outputFile(destPath, content);
380
+ await fs4.remove(localChatbi);
381
+ await fs4.symlink(chatbiDir, localChatbi, "dir");
350
382
  }
351
383
  } else {
352
- await fs2.copy(srcPath, destPath);
384
+ await fs4.symlink(chatbiDir, localChatbi, "dir");
353
385
  }
386
+ } catch (e) {
354
387
  }
355
388
  }
356
- }
357
- /**
358
- * 注入项目虚拟上下文 (tsconfig, alias)
359
- */
360
- static async injectContext(projectRoot, version) {
361
- const chatbiDir = path3.join(projectRoot, ".chatbi");
362
- await fs2.ensureDir(chatbiDir);
363
- const versionPath = this.getVersionPath(version);
364
- const sandboxNodeModules = path3.join(versionPath, "node_modules");
389
+ const versionPath = SandboxPath.getVersionPath(version);
390
+ const sandboxNodeModules = path6.join(versionPath, "node_modules");
365
391
  const corePaths = {};
366
- for (const pkgName of _Sandbox.CORE_PACKAGES) {
392
+ for (const pkgName of corePackages) {
367
393
  corePaths[pkgName] = [`./node_modules/${pkgName}`];
368
394
  corePaths[`${pkgName}/*`] = [`./node_modules/${pkgName}/*`];
369
395
  }
370
- for (const pkgName of _Sandbox.RUNTIME_DEPS) {
396
+ for (const pkgName of runtimeDeps) {
371
397
  corePaths[pkgName] = [`./node_modules/${pkgName}`];
372
398
  corePaths[`${pkgName}/*`] = [`./node_modules/${pkgName}/*`];
373
399
  if (["react", "react-dom", "node"].includes(pkgName)) {
@@ -378,152 +404,119 @@ var init_sandbox = __esm({
378
404
  }
379
405
  corePaths["@types/*"] = ["./node_modules/@types/*"];
380
406
  corePaths["vite/client"] = ["./node_modules/vite/client.d.ts"];
381
- const baseConfigPath = path3.join(sandboxNodeModules, "@chatbi-v/tsconfig/base.json");
382
- let projectPaths = { "@/*": ["../src/*"] };
407
+ const baseConfigPath = path6.join(sandboxNodeModules, "@chatbi-v/tsconfig/base.json");
383
408
  const tsConfig = {
384
409
  extends: baseConfigPath,
385
410
  compilerOptions: {
386
411
  baseUrl: ".",
387
412
  moduleResolution: "bundler",
388
413
  skipLibCheck: true,
389
- // Fix: Use relative path to the symlinked node_modules in .chatbi
390
- // This is much more robust as it works relative to the config file (.chatbi/tsconfig.json)
391
- typeRoots: [
392
- "./node_modules/@types"
393
- ],
394
- // Crucial for symlinked node_modules: treat them as if they are in the project
414
+ typeRoots: ["./node_modules/@types"],
395
415
  preserveSymlinks: true,
396
416
  paths: {
397
417
  ...corePaths,
398
- ...projectPaths,
399
- // Fallback: Try to find anything else in the virtual node_modules
418
+ "@/*": ["../src/*"],
400
419
  "*": ["./node_modules/*"]
401
420
  }
402
421
  }
403
422
  };
404
- await fs2.writeJson(path3.join(chatbiDir, "tsconfig.json"), tsConfig, { spaces: 2 });
405
- await fs2.writeJson(path3.join(chatbiDir, "tsconfig.paths.json"), { compilerOptions: tsConfig.compilerOptions }, { spaces: 2 });
406
- const virtualNodeModules = path3.join(chatbiDir, "node_modules");
407
- if (!fs2.existsSync(sandboxNodeModules)) {
408
- return;
409
- }
410
- try {
423
+ await fs4.writeJson(path6.join(chatbiDir, "tsconfig.json"), tsConfig, { spaces: 2 });
424
+ await fs4.writeJson(path6.join(chatbiDir, "tsconfig.paths.json"), { compilerOptions: tsConfig.compilerOptions }, { spaces: 2 });
425
+ const virtualNodeModules = path6.join(chatbiDir, "node_modules");
426
+ if (fs4.existsSync(sandboxNodeModules)) {
411
427
  try {
412
- const stats = await fs2.lstat(virtualNodeModules);
413
- if (stats) {
414
- await fs2.remove(virtualNodeModules);
415
- }
416
- } catch (e) {
417
- }
418
- await fs2.ensureSymlink(sandboxNodeModules, virtualNodeModules, "dir");
419
- const rootNodeModules = path3.join(projectRoot, "node_modules");
420
- let rootNmExists = false;
421
- try {
422
- rootNmExists = fs2.existsSync(rootNodeModules);
428
+ await fs4.remove(virtualNodeModules).catch(() => {
429
+ });
430
+ await fs4.ensureSymlink(sandboxNodeModules, virtualNodeModules, "dir");
423
431
  } catch (e) {
424
432
  }
425
- if (!rootNmExists) {
426
- try {
427
- await fs2.ensureSymlink(sandboxNodeModules, rootNodeModules, "dir");
428
- } catch (linkErr) {
429
- const appsDir = path3.join(projectRoot, "apps");
430
- if (fs2.existsSync(appsDir)) {
431
- const appDirs = await fg("*", { cwd: appsDir, onlyDirectories: true, absolute: true });
432
- for (const appDir of appDirs) {
433
- const appNm = path3.join(appDir, "node_modules");
434
- if (!fs2.existsSync(appNm)) {
435
- await fs2.ensureSymlink(sandboxNodeModules, appNm, "dir").catch(() => {
436
- });
437
- }
438
- }
439
- }
440
- }
441
- }
442
- } catch (e) {
443
433
  }
444
- const gitignorePath = path3.join(projectRoot, ".gitignore");
445
- if (fs2.existsSync(gitignorePath)) {
446
- let content = await fs2.readFile(gitignorePath, "utf-8");
447
- if (!content.includes(".chatbi")) {
448
- await fs2.appendFile(gitignorePath, "\n# ChatBI\n.chatbi\n");
434
+ const gitignorePath = path6.join(workspaceRoot, ".gitignore");
435
+ if (fs4.existsSync(gitignorePath)) {
436
+ let content = await fs4.readFile(gitignorePath, "utf-8");
437
+ if (!content.includes(SANDBOX_CONFIG.DIRS.CACHE)) {
438
+ await fs4.appendFile(gitignorePath, `
439
+ # ChatBI
440
+ ${SANDBOX_CONFIG.DIRS.CACHE}
441
+ `);
449
442
  }
450
443
  }
451
444
  }
445
+ };
446
+ }
447
+ });
448
+
449
+ // src/sandbox/SandboxPkgManager.ts
450
+ import fs5 from "fs-extra";
451
+ import path7 from "path";
452
+ import pc2 from "picocolors";
453
+ import fg from "fast-glob";
454
+ var SandboxPkgManager;
455
+ var init_SandboxPkgManager = __esm({
456
+ "src/sandbox/SandboxPkgManager.ts"() {
457
+ "use strict";
458
+ init_esm_shims();
459
+ init_utils();
460
+ init_SandboxPath();
461
+ SandboxPkgManager = class {
452
462
  /**
453
- * 获取核心库的 Vite Alias 配置
454
- */
455
- static getCoreAlias(version) {
456
- const versionPath = this.getVersionPath(version);
457
- const sandboxNodeModules = path3.join(versionPath, "node_modules");
458
- const aliases = {};
459
- for (const pkg of this.CORE_PACKAGES) {
460
- aliases[pkg] = path3.join(sandboxNodeModules, pkg);
461
- }
462
- return aliases;
463
- }
464
- /**
465
- * 尝试将本地包 Link 到沙箱中 (模拟安装)
463
+ * 尝试将本地开发环境的包 Link 到沙箱中 (模拟安装)
464
+ * 用于 Monorepo 场景下,让沙箱直接使用最新的本地源码
465
+ * @param versionPath 沙箱版本的存储路径
466
+ * @param corePackages 核心包名列表
467
+ * @param runtimeDeps 运行时依赖包名列表
468
+ * @returns 是否成功 Link 了至少一个包
466
469
  */
467
- static async tryLinkLocalPackages(versionPath) {
470
+ static async tryLinkLocalPackages(versionPath, corePackages, runtimeDeps) {
468
471
  try {
469
472
  const cliRoot = await getCliRoot();
470
- let projectRoot = path3.resolve(cliRoot, "../../..");
471
- let currentDir = cliRoot;
472
- while (currentDir !== path3.parse(currentDir).root) {
473
- if (fs2.existsSync(path3.join(currentDir, "pnpm-workspace.yaml"))) {
474
- projectRoot = currentDir;
475
- break;
476
- }
477
- currentDir = path3.dirname(currentDir);
473
+ const projectRoot = await SandboxPath.getWorkspaceRoot(cliRoot);
474
+ if (projectRoot === cliRoot && !fs5.existsSync(path7.join(projectRoot, "pnpm-workspace.yaml"))) {
475
+ return false;
478
476
  }
479
477
  const possiblePackagesDirs = [
480
- path3.join(projectRoot, "packages"),
481
- // 兼容某些特殊结构,比如 chatbi-v 仓库
482
- path3.resolve(projectRoot, "../chatbi-v/packages")
478
+ path7.join(projectRoot, "packages"),
479
+ // 兼容旧版或其他可能的路径结构
480
+ path7.resolve(projectRoot, "../chatbi-v/packages")
483
481
  ];
484
482
  const localPackages = {};
485
- const patterns = possiblePackagesDirs.filter((dir) => fs2.existsSync(dir)).map((dir) => path3.join(dir, "**/package.json"));
483
+ const patterns = possiblePackagesDirs.filter((dir) => fs5.existsSync(dir)).map((dir) => path7.join(dir, "**/package.json"));
486
484
  if (patterns.length > 0) {
487
485
  const pkgJsonFiles = await fg(patterns, {
488
486
  ignore: ["**/node_modules/**", "**/dist/**", "**/.git/**"],
489
487
  absolute: true,
490
488
  deep: 5
491
- // 允许一定的嵌套深度
492
489
  });
493
490
  for (const pkgJsonPath of pkgJsonFiles) {
494
491
  try {
495
- const pkgJson = await fs2.readJson(pkgJsonPath);
496
- if (this.CORE_PACKAGES.includes(pkgJson.name) || this.RUNTIME_DEPS.includes(pkgJson.name)) {
497
- localPackages[pkgJson.name] = path3.dirname(pkgJsonPath);
492
+ const pkgJson = await fs5.readJson(pkgJsonPath);
493
+ if (corePackages.includes(pkgJson.name) || runtimeDeps.includes(pkgJson.name)) {
494
+ localPackages[pkgJson.name] = path7.dirname(pkgJsonPath);
498
495
  }
499
496
  } catch (e) {
500
497
  }
501
498
  }
502
499
  }
503
- if (Object.keys(localPackages).length === 0) {
504
- return false;
505
- }
500
+ if (Object.keys(localPackages).length === 0) return false;
506
501
  const linkSummary = Object.entries(localPackages).map(([name, pkgPath]) => `${pc2.cyan(name.padEnd(30))} -> ${pc2.gray(pkgPath)}`).join("\n");
507
502
  printBox(
508
503
  pc2.green("\u{1F517} \u5DF2\u68C0\u6D4B\u5230\u672C\u5730\u5F00\u53D1\u73AF\u5883\uFF0C\u5C06\u94FE\u63A5\u4EE5\u4E0B\u6838\u5FC3\u5305\uFF1A\n\n") + linkSummary,
509
504
  "Monorepo Link Mode"
510
505
  );
511
- const sandboxNodeModules = path3.join(versionPath, "node_modules");
512
- await fs2.ensureDir(sandboxNodeModules);
506
+ const sandboxNodeModules = path7.join(versionPath, "node_modules");
507
+ await fs5.ensureDir(sandboxNodeModules);
513
508
  for (const [name, pkgPath] of Object.entries(localPackages)) {
514
- const targetLinkPath = path3.join(sandboxNodeModules, name);
515
- await fs2.ensureDir(path3.dirname(targetLinkPath));
509
+ const targetLinkPath = path7.join(sandboxNodeModules, name);
510
+ await fs5.ensureDir(path7.dirname(targetLinkPath));
516
511
  try {
517
- const stats = await fs2.lstat(targetLinkPath).catch(() => null);
518
- if (stats) {
519
- await fs2.remove(targetLinkPath);
520
- }
512
+ const stats = await fs5.lstat(targetLinkPath).catch(() => null);
513
+ if (stats) await fs5.remove(targetLinkPath);
521
514
  } catch (e) {
522
515
  }
523
- await fs2.symlink(pkgPath, targetLinkPath, "dir");
516
+ await fs5.symlink(pkgPath, targetLinkPath, "dir");
524
517
  }
525
- const sandboxPkgJsonPath = path3.join(versionPath, "package.json");
526
- const sandboxPkgJson = await fs2.readJson(sandboxPkgJsonPath);
518
+ const sandboxPkgJsonPath = path7.join(versionPath, "package.json");
519
+ const sandboxPkgJson = await fs5.readJson(sandboxPkgJsonPath);
527
520
  const sections = ["dependencies", "devDependencies"];
528
521
  let modified = false;
529
522
  for (const section of sections) {
@@ -536,95 +529,287 @@ var init_sandbox = __esm({
536
529
  }
537
530
  }
538
531
  }
539
- if (modified) {
540
- await fs2.writeJson(sandboxPkgJsonPath, sandboxPkgJson, { spaces: 2 });
541
- }
532
+ if (modified) await fs5.writeJson(sandboxPkgJsonPath, sandboxPkgJson, { spaces: 2 });
542
533
  return true;
543
534
  } catch (e) {
544
535
  console.warn(pc2.yellow(` \u26A0\uFE0F Link \u672C\u5730\u6E90\u7801\u5931\u8D25\uFF0C\u5C06\u5C1D\u8BD5\u4ECE NPM \u5B89\u88C5: ${e.message}`));
545
536
  return false;
546
537
  }
547
538
  }
548
- /**
549
- * 切换全局当前使用的内核版本
550
- */
551
- static async useVersion(version) {
552
- const versionRoot = this.getVersionRoot();
553
- const currentLinkPath = path3.join(versionRoot, "current");
554
- const targetPath = this.getVersionPath(version);
555
- if (!fs2.existsSync(targetPath)) {
556
- throw new Error(`\u7248\u672C ${version} \u5C1A\u672A\u5B89\u88C5`);
557
- }
558
- if (fs2.existsSync(currentLinkPath)) {
559
- await fs2.remove(currentLinkPath);
539
+ };
540
+ }
541
+ });
542
+
543
+ // src/sandbox.ts
544
+ import fs6 from "fs-extra";
545
+ import path8 from "path";
546
+ import pc3 from "picocolors";
547
+ import fg2 from "fast-glob";
548
+ import { execa } from "execa";
549
+ var Sandbox;
550
+ var init_sandbox = __esm({
551
+ "src/sandbox.ts"() {
552
+ "use strict";
553
+ init_esm_shims();
554
+ init_utils();
555
+ init_SandboxPath();
556
+ init_SandboxRenderer();
557
+ init_SandboxContext();
558
+ init_SandboxPkgManager();
559
+ init_constants();
560
+ Sandbox = class {
561
+ static {
562
+ /** 核心源码包列表 */
563
+ this.CORE_PACKAGES = [
564
+ "@chatbi-v/core",
565
+ "@chatbi-v/mocks",
566
+ "@chatbi-v/tsconfig",
567
+ "@chatbi-v/tailwind-config",
568
+ "@chatbi-v/plugin-theme-manager",
569
+ "@chatbi-v/plugin-layout-transform",
570
+ "@chatbi-v/plugin-system-monitor"
571
+ ];
572
+ }
573
+ static {
574
+ /** 运行时必须依赖 */
575
+ this.RUNTIME_DEPS = [
576
+ "@ant-design/x",
577
+ "@ant-design/icons",
578
+ "antd",
579
+ "react",
580
+ "react-dom",
581
+ "lucide-react",
582
+ "framer-motion",
583
+ "clsx",
584
+ "tailwind-merge",
585
+ "react-router-dom",
586
+ "zustand",
587
+ "axios",
588
+ "less",
589
+ "vite"
590
+ ];
591
+ }
592
+ /** 获取沙箱根目录 */
593
+ static getRoot() {
594
+ return SandboxPath.getRoot();
595
+ }
596
+ /** 获取所有版本的根目录 */
597
+ static getVersionRoot() {
598
+ return SandboxPath.getVersionRoot();
599
+ }
600
+ /** 获取指定版本的沙箱路径 */
601
+ static getVersionPath(version) {
602
+ return SandboxPath.getVersionPath(version);
603
+ }
604
+ /** 获取项目的工作区根目录 (Monorepo 支持) */
605
+ static async getWorkspaceRoot(cwd) {
606
+ return SandboxPath.getWorkspaceRoot(cwd);
607
+ }
608
+ /** 清理指定版本的沙箱物理文件 */
609
+ static async cleanVersion(version) {
610
+ const versionPath = this.getVersionPath(version);
611
+ if (fs6.existsSync(versionPath)) {
612
+ await fs6.remove(versionPath);
560
613
  }
561
- await fs2.symlink(targetPath, currentLinkPath, "dir");
562
614
  }
563
615
  /**
564
- * 可视化展示沙箱状态
616
+ * 优雅清理缓存环境
617
+ * @param cwd 当前项目路径
618
+ * @param deep 是否执行深度清理
565
619
  */
566
- static async visualizeStatus(projectRoot) {
567
- const version = await this.getCurrentVersion(projectRoot);
568
- const versionPath = this.getVersionPath(version);
569
- const isInstalled = fs2.existsSync(versionPath);
570
- const sandboxNm = path3.join(versionPath, "node_modules");
571
- let statusText = "";
572
- statusText += `${pc2.bold("\u5F53\u524D\u5185\u6838\u7248\u672C:")} ${pc2.cyan(version)}
573
- `;
574
- statusText += `${pc2.bold("\u6C99\u7BB1\u5B89\u88C5\u8DEF\u5F84:")} ${pc2.gray(versionPath)}
575
- `;
576
- statusText += `${pc2.bold("\u5B89\u88C5\u72B6\u6001:")} ${isInstalled ? pc2.green("\u5DF2\u5C31\u7EEA") : pc2.red("\u672A\u5B89\u88C5")}
577
- `;
578
- if (isInstalled) {
579
- const corePkgs = this.CORE_PACKAGES.map((pkg) => {
580
- const pkgPath = path3.join(sandboxNm, pkg);
581
- const exists = fs2.existsSync(pkgPath);
582
- let linkTarget = "";
583
- if (exists) {
584
- try {
585
- const stats = fs2.lstatSync(pkgPath);
586
- if (stats.isSymbolicLink()) {
587
- linkTarget = ` -> ${pc2.yellow(fs2.readlinkSync(pkgPath))}`;
620
+ static async clean(cwd, deep = false) {
621
+ const spinner = createSpinner("\u6B63\u5728\u6E05\u7406\u7F13\u5B58\u76EE\u5F55...").start();
622
+ try {
623
+ const workspaceRoot = await this.getWorkspaceRoot(cwd);
624
+ let cleanedCount = 0;
625
+ const cachePatterns = [
626
+ path8.join(workspaceRoot, `**/${SANDBOX_CONFIG.DIRS.CACHE}`)
627
+ ];
628
+ const cacheDirs = await fg2(cachePatterns, {
629
+ onlyFiles: false,
630
+ // 允许匹配文件和符号链接
631
+ absolute: true,
632
+ ignore: ["**/node_modules/**"],
633
+ dot: true
634
+ // 确保能匹配到以 . 开头的目录
635
+ });
636
+ for (const dir of cacheDirs) {
637
+ const stats = await fs6.lstat(dir).catch(() => null);
638
+ if (stats) {
639
+ await fs6.remove(dir);
640
+ cleanedCount++;
641
+ }
642
+ }
643
+ if (deep) {
644
+ const distPatterns = [
645
+ path8.join(workspaceRoot, "**/dist")
646
+ ];
647
+ const distDirs = await fg2(distPatterns, {
648
+ onlyFiles: false,
649
+ absolute: true,
650
+ ignore: [
651
+ "**/node_modules/**",
652
+ "**/packages/cli/dist/**",
653
+ // 保护 CLI 自身构建产物
654
+ "**/.git/**"
655
+ ]
656
+ });
657
+ for (const dir of distDirs) {
658
+ const stats = await fs6.lstat(dir).catch(() => null);
659
+ if (stats) {
660
+ const hasPkg = fs6.existsSync(path8.join(path8.dirname(dir), "package.json"));
661
+ if (hasPkg) {
662
+ await fs6.remove(dir);
663
+ cleanedCount++;
588
664
  }
589
- } catch (e) {
590
665
  }
591
666
  }
592
- return ` ${exists ? pc2.green("\u2713") : pc2.red("\u2717")} ${pkg.padEnd(30)}${linkTarget}`;
593
- }).join("\n");
594
- statusText += `
595
- ${pc2.bold("\u6838\u5FC3\u7EC4\u4EF6\u72B6\u6001:")}
596
- ${corePkgs}`;
667
+ const globalRoot = this.getRoot();
668
+ if (fs6.existsSync(globalRoot)) {
669
+ await fs6.remove(globalRoot);
670
+ cleanedCount++;
671
+ }
672
+ }
673
+ spinner.succeed(pc3.green(`\u6E05\u7406\u5B8C\u6210\uFF0C\u5171\u6E05\u7406 ${cleanedCount} \u4E2A\u9879\u76EE/\u7F13\u5B58\u76EE\u5F55`));
674
+ } catch (e) {
675
+ spinner.fail(pc3.red(`\u6E05\u7406\u5931\u8D25: ${e.message}`));
597
676
  }
598
- printBox(statusText, "ChatBI \u5185\u6838\u53EF\u89C2\u6D4B\u6027\u9762\u677F");
599
677
  }
600
678
  /**
601
- * 获取当前项目使用的内核版本
679
+ * 准备内核沙箱环境 (安装/更新)
602
680
  */
603
- static async getCurrentVersion(projectRoot) {
681
+ static async prepare(version, force = false) {
682
+ const versionPath = this.getVersionPath(version);
683
+ if (!force && fs6.existsSync(path8.join(versionPath, "node_modules/tailwindcss"))) {
684
+ return versionPath;
685
+ }
686
+ if (fs6.existsSync(versionPath)) {
687
+ await fs6.remove(path8.join(versionPath, "node_modules"));
688
+ }
689
+ await fs6.ensureDir(versionPath);
690
+ const spinner = createSpinner(`\u6B63\u5728\u521D\u59CB\u5316\u5185\u6838\u6C99\u7BB1 ${pc3.cyan(version)}...`).start();
604
691
  try {
605
- const chatbiDir = path3.join(projectRoot, ".chatbi");
606
- const tsconfigPath = path3.join(chatbiDir, "tsconfig.json");
607
- if (fs2.existsSync(tsconfigPath)) {
608
- const tsconfig = await fs2.readJson(tsconfigPath);
609
- if (tsconfig.extends && tsconfig.extends.includes("/versions/")) {
610
- const parts = tsconfig.extends.split("/");
611
- const vIdx = parts.indexOf("versions");
612
- if (vIdx !== -1 && parts[vIdx + 1]) {
613
- return parts[vIdx + 1];
614
- }
615
- }
692
+ const cliRoot = await getCliRoot();
693
+ const dependencies = {};
694
+ for (const pkg of this.RUNTIME_DEPS) {
695
+ dependencies[pkg] = DEPENDENCY_VERSIONS[pkg] || "latest";
696
+ }
697
+ for (const pkg of this.CORE_PACKAGES) {
698
+ dependencies[pkg] = pkg === "@chatbi-v/core" ? version : "latest";
616
699
  }
700
+ const templateData = {
701
+ version,
702
+ dependencies,
703
+ devDependencies: DEPENDENCY_VERSIONS
704
+ };
705
+ await SandboxRenderer.renderDirectory(
706
+ path8.join(cliRoot, "templates/sandbox"),
707
+ versionPath,
708
+ templateData
709
+ );
710
+ spinner.text = pc3.gray(" \u{1F50D} \u68C0\u67E5\u672C\u5730\u5F00\u53D1\u73AF\u5883...");
711
+ const isLocalDev = await SandboxPkgManager.tryLinkLocalPackages(versionPath, this.CORE_PACKAGES, this.RUNTIME_DEPS);
712
+ const installArgs = isLocalDev ? ["install", "--no-frozen-lockfile"] : ["install"];
713
+ spinner.text = pc3.gray(` \u23F3 \u6267\u884C pnpm ${installArgs.join(" ")}...`);
714
+ await execa("pnpm", installArgs, { cwd: versionPath });
715
+ spinner.text = pc3.gray(" \u{1F3A8} \u540C\u6B65 Shell \u6A21\u677F...");
716
+ await this.ensureShell(version, force);
717
+ spinner.succeed(pc3.green(`\u5185\u6838\u6C99\u7BB1 ${version} \u521D\u59CB\u5316\u6210\u529F`));
617
718
  } catch (e) {
719
+ spinner.fail(pc3.red(`\u5185\u6838\u6C99\u7BB1\u521D\u59CB\u5316\u5931\u8D25: ${e.message}`));
720
+ throw e;
618
721
  }
619
- return "current";
722
+ return versionPath;
723
+ }
724
+ /**
725
+ * 确保 Shell 模板已同步到沙箱
726
+ */
727
+ static async ensureShell(version, force = false) {
728
+ const versionPath = this.getVersionPath(version);
729
+ const shellDestDir = path8.join(versionPath, "shell");
730
+ if (!force && fs6.existsSync(path8.join(shellDestDir, "tsconfig.paths.json"))) {
731
+ return shellDestDir;
732
+ }
733
+ const cliRoot = await getCliRoot();
734
+ const shellTemplateDir = path8.join(cliRoot, "templates/app");
735
+ if (fs6.existsSync(shellTemplateDir)) {
736
+ await fs6.remove(shellDestDir);
737
+ const templateData = {
738
+ name: "chatbi-shell",
739
+ version,
740
+ cliVersion: version,
741
+ theme: DEFAULT_CONFIG.THEME,
742
+ isShell: true,
743
+ isBusiness: true,
744
+ tsconfigPath: "./tsconfig.paths.json"
745
+ };
746
+ await SandboxRenderer.renderDirectory(shellTemplateDir, shellDestDir, templateData);
747
+ await fs6.writeJson(path8.join(shellDestDir, "tsconfig.paths.json"), {
748
+ compilerOptions: { baseUrl: ".", paths: { "@/*": ["./src/*"] } }
749
+ }, { spaces: 2 });
750
+ }
751
+ return shellDestDir;
752
+ }
753
+ /** 注入项目虚拟上下文 */
754
+ static async injectContext(projectRoot, version) {
755
+ return SandboxContext.inject(projectRoot, version, this.CORE_PACKAGES, this.RUNTIME_DEPS);
756
+ }
757
+ /** 获取核心库的 Vite Alias 配置 */
758
+ static getCoreAlias(version) {
759
+ const versionPath = this.getVersionPath(version);
760
+ const sandboxNodeModules = path8.join(versionPath, "node_modules");
761
+ return Object.fromEntries(this.CORE_PACKAGES.map((pkg) => [pkg, path8.join(sandboxNodeModules, pkg)]));
762
+ }
763
+ /** 切换内核版本 */
764
+ static async useVersion(version) {
765
+ const versionRoot = this.getVersionRoot();
766
+ const currentLink = path8.join(versionRoot, SANDBOX_CONFIG.DIRS.CURRENT);
767
+ const targetPath = this.getVersionPath(version);
768
+ if (!fs6.existsSync(targetPath)) throw new Error(`\u7248\u672C ${version} \u4E0D\u5B58\u5728`);
769
+ if (fs6.existsSync(currentLink)) await fs6.remove(currentLink);
770
+ await fs6.ensureSymlink(targetPath, currentLink, "dir");
771
+ }
772
+ /** 解析版本号 */
773
+ static async resolveVersion(version) {
774
+ if (!version || version === "latest" || version === "current") {
775
+ const currentLink = path8.join(this.getVersionRoot(), SANDBOX_CONFIG.DIRS.CURRENT);
776
+ if (fs6.existsSync(currentLink)) {
777
+ return path8.basename(await fs6.readlink(currentLink));
778
+ }
779
+ const versions = await this.listVersions();
780
+ return versions[0] || "1.0.0";
781
+ }
782
+ return version;
783
+ }
784
+ /** 列出所有已安装版本 */
785
+ static async listVersions() {
786
+ const versionRoot = this.getVersionRoot();
787
+ if (!fs6.existsSync(versionRoot)) return [];
788
+ const dirs = await fs6.readdir(versionRoot);
789
+ return dirs.filter((d) => d !== SANDBOX_CONFIG.DIRS.CURRENT && !d.startsWith(".")).sort().reverse();
790
+ }
791
+ /** 可视化展示沙箱状态 */
792
+ static async visualizeStatus(cwd) {
793
+ const workspaceRoot = await this.getWorkspaceRoot(cwd);
794
+ const isMonorepo = workspaceRoot !== cwd;
795
+ const currentVersion = await this.resolveVersion("current");
796
+ const versions = await this.listVersions();
797
+ const statusInfo = [
798
+ `${pc3.bold("\u9879\u76EE\u8DEF\u5F84:")} ${pc3.cyan(cwd)}`,
799
+ `${pc3.bold("\u67B6\u6784\u6A21\u5F0F:")} ${isMonorepo ? pc3.yellow("Monorepo") : pc3.green("Standalone")}`,
800
+ `${pc3.bold("\u5F53\u524D\u5185\u6838:")} ${pc3.green(currentVersion)}`,
801
+ `${pc3.bold("\u5DF2\u88C5\u7248\u672C:")} ${pc3.gray(versions.join(", ") || "none")}`,
802
+ `${pc3.bold("\u6C99\u7BB1\u6839\u76EE\u5F55:")} ${pc3.gray(this.getRoot())}`
803
+ ].join("\n");
804
+ printBox(statusInfo, "ChatBI Sandbox Status");
620
805
  }
621
806
  };
622
807
  }
623
808
  });
624
809
 
625
810
  // src/config.ts
626
- import fs3 from "fs-extra";
627
- import path4 from "path";
811
+ import fs7 from "fs-extra";
812
+ import path9 from "path";
628
813
  import { createJiti } from "jiti";
629
814
  var ConfigManager;
630
815
  var init_config = __esm({
@@ -632,15 +817,11 @@ var init_config = __esm({
632
817
  "use strict";
633
818
  init_esm_shims();
634
819
  init_utils();
820
+ init_constants();
821
+ init_utils();
635
822
  ConfigManager = class {
636
823
  static {
637
- this.CONFIG_FILES = [
638
- "chatbi.config.ts",
639
- "chatbi.config.js",
640
- "chatbi.config.json",
641
- ".chatbirc",
642
- ".chatbirc.json"
643
- ];
824
+ this.CONFIG_FILES = DEFAULT_CONFIG.CONFIG_FILES;
644
825
  }
645
826
  /**
646
827
  * 加载项目配置
@@ -648,10 +829,10 @@ var init_config = __esm({
648
829
  */
649
830
  static async loadConfig(cwd = process.cwd()) {
650
831
  const config = {};
651
- const versionFilePath = path4.join(cwd, ".chatbi-version");
652
- if (fs3.existsSync(versionFilePath)) {
832
+ const versionFilePath = path9.join(cwd, SANDBOX_CONFIG.LOCK_FILE);
833
+ if (fs7.existsSync(versionFilePath)) {
653
834
  try {
654
- const version = (await fs3.readFile(versionFilePath, "utf-8")).trim();
835
+ const version = (await fs7.readFile(versionFilePath, "utf-8")).trim();
655
836
  if (version) {
656
837
  config.coreVersion = version;
657
838
  }
@@ -660,15 +841,15 @@ var init_config = __esm({
660
841
  }
661
842
  const jiti = createJiti(cwd);
662
843
  for (const file of this.CONFIG_FILES) {
663
- const configPath = path4.join(cwd, file);
664
- if (fs3.existsSync(configPath)) {
844
+ const configPath = path9.join(cwd, file);
845
+ if (fs7.existsSync(configPath)) {
665
846
  try {
666
847
  let projectConfig = {};
667
848
  if (file.endsWith(".ts") || file.endsWith(".js")) {
668
849
  const mod = await jiti.import(configPath, { default: true });
669
850
  projectConfig = mod.default || mod || {};
670
851
  } else if (file.endsWith(".json") || file.startsWith(".chatbirc")) {
671
- projectConfig = await fs3.readJson(configPath);
852
+ projectConfig = await fs7.readJson(configPath);
672
853
  }
673
854
  return {
674
855
  ...projectConfig,
@@ -681,16 +862,76 @@ var init_config = __esm({
681
862
  }
682
863
  return config;
683
864
  }
865
+ /**
866
+ * 解析核心依赖的具体路径或版本号
867
+ * @param config 项目配置
868
+ * @param relativeTo 相对路径(用于 local 模式下的 file: 协议生成)
869
+ */
870
+ static async resolveCoreDependency(config, relativeTo = ".") {
871
+ const { coreSource = "local", coreVersion } = config;
872
+ if (coreSource === "npm") {
873
+ if (coreVersion) return coreVersion;
874
+ const cliRoot = await getCliRoot();
875
+ const cliPkg = await fs7.readJson(path9.join(cliRoot, "package.json"));
876
+ return cliPkg.version;
877
+ }
878
+ const sandboxCorePath = path9.join(SANDBOX_CONFIG.DIRS.CACHE, "core");
879
+ const cwd = process.cwd();
880
+ const absoluteRelativeTo = path9.isAbsolute(relativeTo) ? relativeTo : path9.resolve(cwd, relativeTo);
881
+ const absoluteProjectRoot = cwd;
882
+ const relativePath = path9.relative(absoluteRelativeTo, path9.join(absoluteProjectRoot, sandboxCorePath));
883
+ return `file:${relativePath}`;
884
+ }
885
+ /**
886
+ * 加载 tsup 配置
887
+ * @param cwd 项目根目录
888
+ */
889
+ static async loadTsupConfig(cwd = process.cwd()) {
890
+ const TSUP_CONFIG_FILES = [
891
+ "tsup.config.ts",
892
+ "tsup.config.js",
893
+ "tsup.config.cjs",
894
+ "tsup.config.mjs",
895
+ "tsup.config.json"
896
+ ];
897
+ const jiti = createJiti(cwd);
898
+ for (const file of TSUP_CONFIG_FILES) {
899
+ const configPath = path9.join(cwd, file);
900
+ if (fs7.existsSync(configPath)) {
901
+ try {
902
+ if (file.endsWith(".json")) {
903
+ return await fs7.readJson(configPath);
904
+ } else {
905
+ const mod = await jiti.import(configPath, { default: true });
906
+ return mod.default || mod || {};
907
+ }
908
+ } catch (e) {
909
+ logger.warn(`\u52A0\u8F7D tsup \u914D\u7F6E\u6587\u4EF6 ${file} \u5931\u8D25: ${e.message}`);
910
+ }
911
+ }
912
+ }
913
+ const pkgPath = path9.join(cwd, "package.json");
914
+ if (fs7.existsSync(pkgPath)) {
915
+ try {
916
+ const pkg = await fs7.readJson(pkgPath);
917
+ if (pkg.tsup) {
918
+ return pkg.tsup;
919
+ }
920
+ } catch (e) {
921
+ }
922
+ }
923
+ return {};
924
+ }
684
925
  };
685
926
  }
686
927
  });
687
928
 
688
929
  // src/corekit.ts
689
- import path5 from "path";
690
- import fs4 from "fs-extra";
691
- import pc3 from "picocolors";
930
+ import path10 from "path";
931
+ import fs8 from "fs-extra";
932
+ import pc4 from "picocolors";
692
933
  import prompts from "prompts";
693
- import fg2 from "fast-glob";
934
+ import fg3 from "fast-glob";
694
935
  var CoreKit;
695
936
  var init_corekit = __esm({
696
937
  "src/corekit.ts"() {
@@ -704,20 +945,20 @@ var init_corekit = __esm({
704
945
  * 解析项目模式
705
946
  */
706
947
  static async detectMode(cwd) {
707
- const pkgPath = path5.join(cwd, "package.json");
708
- if (!fs4.existsSync(pkgPath)) return "app";
709
- const pkg = await fs4.readJson(pkgPath);
710
- if (pkg.workspaces || fs4.existsSync(path5.join(cwd, "pnpm-workspace.yaml"))) {
948
+ const pkgPath = path10.join(cwd, "package.json");
949
+ if (!fs8.existsSync(pkgPath)) return "app";
950
+ const pkg = await fs8.readJson(pkgPath);
951
+ if (pkg.workspaces || fs8.existsSync(path10.join(cwd, "pnpm-workspace.yaml"))) {
711
952
  return "monorepo";
712
953
  }
713
954
  if (pkg.chatbi?.type === "plugin" || pkg.plugin === true) return "plugin";
714
955
  if (pkg.chatbi?.type === "app") return "app";
715
- const pluginEntries = await fg2(["index.plugin.{ts,js}", "src/index.plugin.{ts,js}"], { cwd });
956
+ const pluginEntries = await fg3(["index.plugin.{ts,js}", "src/index.plugin.{ts,js}"], { cwd });
716
957
  if (pluginEntries.length > 0) return "plugin";
717
- if (fs4.existsSync(path5.join(cwd, "index.html"))) {
958
+ if (fs8.existsSync(path10.join(cwd, "index.html"))) {
718
959
  return "app";
719
960
  }
720
- if (fs4.existsSync(path5.join(cwd, "src/index.ts")) || fs4.existsSync(path5.join(cwd, "src/index.tsx"))) {
961
+ if (fs8.existsSync(path10.join(cwd, "src/index.ts")) || fs8.existsSync(path10.join(cwd, "src/index.tsx"))) {
721
962
  if (pkg.bin) return "lib";
722
963
  if (pkg.peerDependencies?.react && !pkg.dependencies?.react) return "plugin";
723
964
  return "lib";
@@ -735,18 +976,18 @@ var init_corekit = __esm({
735
976
  }
736
977
  try {
737
978
  const cliRoot = await getCliRoot();
738
- const cliVersionFile = path5.join(cliRoot, ".chatbi-version");
739
- if (fs4.existsSync(cliVersionFile)) {
740
- const version = (await fs4.readFile(cliVersionFile, "utf-8")).trim();
979
+ const cliVersionFile = path10.join(cliRoot, ".chatbi-version");
980
+ if (fs8.existsSync(cliVersionFile)) {
981
+ const version = (await fs8.readFile(cliVersionFile, "utf-8")).trim();
741
982
  if (version) return version;
742
983
  }
743
984
  } catch (e) {
744
985
  }
745
- const currentLinkPath = path5.join(Sandbox.getVersionRoot(), "current");
746
- if (fs4.existsSync(currentLinkPath)) {
986
+ const currentLinkPath = path10.join(Sandbox.getVersionRoot(), "current");
987
+ if (fs8.existsSync(currentLinkPath)) {
747
988
  try {
748
- const realPath = await fs4.realpath(currentLinkPath);
749
- return path5.basename(realPath);
989
+ const realPath = await fs8.realpath(currentLinkPath);
990
+ return path10.basename(realPath);
750
991
  } catch (e) {
751
992
  }
752
993
  }
@@ -758,8 +999,8 @@ var init_corekit = __esm({
758
999
  */
759
1000
  static async listVersions() {
760
1001
  const versionsDir = Sandbox.getVersionRoot();
761
- if (!fs4.existsSync(versionsDir)) return [];
762
- const dirs = await fg2("*", {
1002
+ if (!fs8.existsSync(versionsDir)) return [];
1003
+ const dirs = await fg3("*", {
763
1004
  cwd: versionsDir,
764
1005
  onlyDirectories: true,
765
1006
  deep: 1
@@ -770,18 +1011,18 @@ var init_corekit = __esm({
770
1011
  * 自动发现项目中的插件
771
1012
  */
772
1013
  static async discoverPlugins(rootDir) {
773
- const pluginsDir = path5.join(rootDir, "plugins");
774
- if (!fs4.existsSync(pluginsDir)) return [];
775
- const pkgFiles = await fg2("*/package.json", { cwd: pluginsDir, absolute: true });
1014
+ const pluginsDir = path10.join(rootDir, "plugins");
1015
+ if (!fs8.existsSync(pluginsDir)) return [];
1016
+ const pkgFiles = await fg3("*/package.json", { cwd: pluginsDir, absolute: true });
776
1017
  const plugins = [];
777
1018
  for (const pkgPath of pkgFiles) {
778
1019
  try {
779
- const pkg = await fs4.readJson(pkgPath);
780
- const pluginPath = path5.dirname(pkgPath);
1020
+ const pkg = await fs8.readJson(pkgPath);
1021
+ const pluginPath = path10.dirname(pkgPath);
781
1022
  plugins.push({
782
- name: pkg.name || path5.basename(pluginPath),
1023
+ name: pkg.name || path10.basename(pluginPath),
783
1024
  path: pluginPath,
784
- id: path5.basename(pluginPath)
1025
+ id: path10.basename(pluginPath)
785
1026
  });
786
1027
  } catch (e) {
787
1028
  }
@@ -795,19 +1036,19 @@ var init_corekit = __esm({
795
1036
  const mode = await this.detectMode(cwd);
796
1037
  const version = await this.resolveVersion(cwd);
797
1038
  printBox(
798
- `${pc3.cyan(pc3.bold("\u{1F680} ChatBI Dev Server"))}
1039
+ `${pc4.cyan(pc4.bold("\u{1F680} ChatBI Dev Server"))}
799
1040
 
800
- ${pc3.gray("Mode: ")} ${pc3.yellow(mode)}
801
- ${pc3.gray("Kernel: ")} ${pc3.green(version)}
802
- ${pc3.gray("Root: ")} ${pc3.white(cwd)}`,
1041
+ ${pc4.gray("Mode: ")} ${pc4.yellow(mode)}
1042
+ ${pc4.gray("Kernel: ")} ${pc4.green(version)}
1043
+ ${pc4.gray("Root: ")} ${pc4.white(cwd)}`,
803
1044
  "Dev Server"
804
1045
  );
805
1046
  const spinner = createSpinner("\u6B63\u5728\u51C6\u5907\u6C99\u7BB1\u73AF\u5883...").start();
806
1047
  try {
807
1048
  await Sandbox.prepare(version);
808
- spinner.text = pc3.cyan("\u6B63\u5728\u6CE8\u5165\u865A\u62DF\u4E0A\u4E0B\u6587...");
1049
+ spinner.text = pc4.cyan("\u6B63\u5728\u6CE8\u5165\u865A\u62DF\u4E0A\u4E0B\u6587...");
809
1050
  await Sandbox.injectContext(cwd, version);
810
- spinner.succeed(pc3.green("\u73AF\u5883\u5C31\u7EEA"));
1051
+ spinner.succeed(pc4.green("\u73AF\u5883\u5C31\u7EEA"));
811
1052
  if (mode === "plugin") {
812
1053
  await this.startPluginDevServer(cwd, version, options.port);
813
1054
  } else if (mode === "app") {
@@ -816,22 +1057,22 @@ ${pc3.gray("Root: ")} ${pc3.white(cwd)}`,
816
1057
  await this.startMonorepoDevServer(cwd, version, options.port);
817
1058
  }
818
1059
  } catch (e) {
819
- spinner.fail(pc3.red(`\u542F\u52A8\u5931\u8D25: ${e.message}`));
1060
+ spinner.fail(pc4.red(`\u542F\u52A8\u5931\u8D25: ${e.message}`));
820
1061
  }
821
1062
  }
822
1063
  /**
823
1064
  * 启动 Monorepo 开发服务器
824
1065
  */
825
1066
  static async startMonorepoDevServer(rootDir, version, customPort) {
826
- const appsDir = path5.join(rootDir, "apps");
827
- if (!fs4.existsSync(appsDir)) {
1067
+ const appsDir = path10.join(rootDir, "apps");
1068
+ if (!fs8.existsSync(appsDir)) {
828
1069
  logger.warn("\u5F53\u524D Monorepo \u4E0B\u672A\u627E\u5230 apps/ \u76EE\u5F55");
829
1070
  return;
830
1071
  }
831
- const pkgFiles = await fg2("*/package.json", { cwd: appsDir, absolute: true });
1072
+ const pkgFiles = await fg3("*/package.json", { cwd: appsDir, absolute: true });
832
1073
  const apps = pkgFiles.map((pkgPath) => ({
833
- name: path5.basename(path5.dirname(pkgPath)),
834
- path: path5.dirname(pkgPath)
1074
+ name: path10.basename(path10.dirname(pkgPath)),
1075
+ path: path10.dirname(pkgPath)
835
1076
  }));
836
1077
  if (apps.length === 0) {
837
1078
  logger.warn("\u5F53\u524D Monorepo \u4E0B\u672A\u627E\u5230\u4EFB\u4F55\u5E94\u7528 (apps/)");
@@ -858,14 +1099,14 @@ ${pc3.gray("Root: ")} ${pc3.white(cwd)}`,
858
1099
  const shellPort = customPort || 5173;
859
1100
  const sandboxRoot = Sandbox.getRoot();
860
1101
  const versionPath = Sandbox.getVersionPath(version);
861
- const sandboxNodeModules = path5.join(versionPath, "node_modules");
1102
+ const sandboxNodeModules = path10.join(versionPath, "node_modules");
862
1103
  const shellDir = await Sandbox.ensureShell(version);
863
1104
  const coreAlias = Sandbox.getCoreAlias(version);
864
- const tailwindPath = path5.join(sandboxNodeModules, "tailwindcss");
865
- const autoprefixerPath = path5.join(sandboxNodeModules, "autoprefixer");
866
- let pluginEntry = path5.join(pluginDir, "src/index.tsx");
867
- if (!fs4.existsSync(pluginEntry)) {
868
- pluginEntry = path5.join(pluginDir, "src/index.ts");
1105
+ const tailwindPath = path10.join(sandboxNodeModules, "tailwindcss");
1106
+ const autoprefixerPath = path10.join(sandboxNodeModules, "autoprefixer");
1107
+ let pluginEntry = path10.join(pluginDir, "src/index.tsx");
1108
+ if (!fs8.existsSync(pluginEntry)) {
1109
+ pluginEntry = path10.join(pluginDir, "src/index.ts");
869
1110
  }
870
1111
  const define = {
871
1112
  "process.env.CHATBI_PLUGIN_PATH": JSON.stringify(pluginDir)
@@ -879,12 +1120,12 @@ ${pc3.gray("Root: ")} ${pc3.white(cwd)}`,
879
1120
  postcss: {
880
1121
  plugins: [
881
1122
  __require(tailwindPath)({
882
- presets: [__require(path5.join(sandboxNodeModules, "@chatbi-v/tailwind-config"))],
1123
+ presets: [__require(path10.join(sandboxNodeModules, "@chatbi-v/tailwind-config"))],
883
1124
  darkMode: "class",
884
1125
  content: [
885
- path5.join(shellDir, "index.html"),
886
- path5.join(shellDir, "src/**/*.{ts,tsx}"),
887
- path5.join(pluginDir, "src/**/*.{ts,tsx}")
1126
+ path10.join(shellDir, "index.html"),
1127
+ path10.join(shellDir, "src/**/*.{ts,tsx}"),
1128
+ path10.join(pluginDir, "src/**/*.{ts,tsx}")
888
1129
  ]
889
1130
  }),
890
1131
  __require(autoprefixerPath)
@@ -894,25 +1135,33 @@ ${pc3.gray("Root: ")} ${pc3.white(cwd)}`,
894
1135
  server: {
895
1136
  port: shellPort,
896
1137
  host: true,
1138
+ watch: {
1139
+ // 关键:排除沙箱和软链目录,防止 Vite 挂起
1140
+ ignored: [
1141
+ "**/.chatbi/**",
1142
+ "**/node_modules/**",
1143
+ Sandbox.getRoot()
1144
+ ]
1145
+ },
897
1146
  fs: {
898
1147
  allow: [
899
1148
  sandboxRoot,
900
1149
  pluginDir,
901
- path5.resolve(pluginDir, "../../")
1150
+ path10.resolve(pluginDir, "../../")
902
1151
  ]
903
1152
  }
904
1153
  },
905
1154
  resolve: {
906
1155
  alias: {
907
1156
  ...coreAlias,
908
- "react": path5.join(sandboxNodeModules, "react"),
909
- "react-dom": path5.join(sandboxNodeModules, "react-dom"),
910
- "@": path5.join(shellDir, "src"),
1157
+ "react": path10.join(sandboxNodeModules, "react"),
1158
+ "react-dom": path10.join(sandboxNodeModules, "react-dom"),
1159
+ "@": path10.join(shellDir, "src"),
911
1160
  // 关键:将虚拟模块指向用户插件
912
1161
  "virtual:user-plugin": pluginEntry,
913
1162
  // 防止 glob 报错,提供空的映射
914
- "@chatbi-plugins": path5.join(shellDir, "empty"),
915
- "@chatbi-apps": path5.join(shellDir, "empty")
1163
+ "@chatbi-plugins": path10.join(shellDir, "empty"),
1164
+ "@chatbi-apps": path10.join(shellDir, "empty")
916
1165
  }
917
1166
  },
918
1167
  define
@@ -920,9 +1169,9 @@ ${pc3.gray("Root: ")} ${pc3.white(cwd)}`,
920
1169
  await server.listen();
921
1170
  const localUrl = `http://localhost:${shellPort}/`;
922
1171
  printBox(
923
- `${pc3.green(pc3.bold("\u2705 \u6258\u7BA1\u5F0F Shell \u5DF2\u542F\u52A8"))}
1172
+ `${pc4.green(pc4.bold("\u2705 \u6258\u7BA1\u5F0F Shell \u5DF2\u542F\u52A8"))}
924
1173
 
925
- ${pc3.white("Local: ")} ${pc3.cyan(pc3.underline(localUrl))}`,
1174
+ ${pc4.white("Local: ")} ${pc4.cyan(pc4.underline(localUrl))}`,
926
1175
  "Shell Success"
927
1176
  );
928
1177
  } catch (e) {
@@ -935,7 +1184,7 @@ ${pc3.white("Local: ")} ${pc3.cyan(pc3.underline(localUrl))}`,
935
1184
  static async startAppDevServer(appDir, version, customPort) {
936
1185
  logger.info("\u6B63\u5728\u542F\u52A8\u5E94\u7528...");
937
1186
  const versionPath = Sandbox.getVersionPath(version);
938
- const sandboxNodeModules = path5.join(versionPath, "node_modules");
1187
+ const sandboxNodeModules = path10.join(versionPath, "node_modules");
939
1188
  const coreAlias = Sandbox.getCoreAlias(version);
940
1189
  const { createServer, loadConfigFromFile } = await import("vite");
941
1190
  try {
@@ -949,14 +1198,22 @@ ${pc3.white("Local: ")} ${pc3.cyan(pc3.underline(localUrl))}`,
949
1198
  alias: {
950
1199
  ...coreAlias,
951
1200
  // 补充常用的基础依赖映射,防止用户 vite.config.ts 中的插件找不到依赖
952
- "react": path5.join(sandboxNodeModules, "react"),
953
- "react-dom": path5.join(sandboxNodeModules, "react-dom"),
954
- "@vitejs/plugin-react": path5.join(sandboxNodeModules, "@vitejs/plugin-react")
1201
+ "react": path10.join(sandboxNodeModules, "react"),
1202
+ "react-dom": path10.join(sandboxNodeModules, "react-dom"),
1203
+ "@vitejs/plugin-react": path10.join(sandboxNodeModules, "@vitejs/plugin-react")
955
1204
  }
956
1205
  },
957
1206
  server: {
958
1207
  port: customPort || 3e3,
959
1208
  host: true,
1209
+ watch: {
1210
+ // 关键:排除沙箱和软链目录,防止 Vite 挂起
1211
+ ignored: [
1212
+ "**/.chatbi/**",
1213
+ "**/node_modules/**",
1214
+ Sandbox.getRoot()
1215
+ ]
1216
+ },
960
1217
  fs: {
961
1218
  allow: [
962
1219
  Sandbox.getRoot(),
@@ -970,9 +1227,9 @@ ${pc3.white("Local: ")} ${pc3.cyan(pc3.underline(localUrl))}`,
970
1227
  await server.listen();
971
1228
  const localUrl = `http://localhost:${server.config.server.port}/`;
972
1229
  printBox(
973
- `${pc3.green(pc3.bold("\u2705 \u5E94\u7528\u5DF2\u6210\u529F\u542F\u52A8"))}
1230
+ `${pc4.green(pc4.bold("\u2705 \u5E94\u7528\u5DF2\u6210\u529F\u542F\u52A8"))}
974
1231
 
975
- ${pc3.white("Local: ")} ${pc3.cyan(pc3.underline(localUrl))}`,
1232
+ ${pc4.white("Local: ")} ${pc4.cyan(pc4.underline(localUrl))}`,
976
1233
  "App Success"
977
1234
  );
978
1235
  } catch (e) {
@@ -989,18 +1246,18 @@ __export(build_exports, {
989
1246
  build: () => build
990
1247
  });
991
1248
  import { execa as execa2 } from "execa";
992
- import fs5 from "fs-extra";
993
- import path6 from "path";
994
- import pc4 from "picocolors";
1249
+ import fs9 from "fs-extra";
1250
+ import path11 from "path";
1251
+ import pc5 from "picocolors";
995
1252
  import { build as tsupBuild } from "tsup";
996
1253
  async function build(options) {
997
1254
  const cwd = process.cwd();
998
- const pkgPath = path6.join(cwd, "package.json");
999
- if (!fs5.existsSync(pkgPath)) {
1255
+ const pkgPath = path11.join(cwd, "package.json");
1256
+ if (!fs9.existsSync(pkgPath)) {
1000
1257
  logger.error(`\u5728 ${cwd} \u4E2D\u672A\u627E\u5230 package.json`);
1001
1258
  return;
1002
1259
  }
1003
- const pkg = await fs5.readJson(pkgPath);
1260
+ const pkg = await fs9.readJson(pkgPath);
1004
1261
  if (options.force) {
1005
1262
  const spinnerClean = createSpinner("\u6B63\u5728\u6E05\u7406\u7F13\u5B58\u73AF\u5883...").start();
1006
1263
  try {
@@ -1012,24 +1269,24 @@ async function build(options) {
1012
1269
  }
1013
1270
  }
1014
1271
  const version = await CoreKit.resolveVersion(cwd);
1015
- logger.info(`\u6B63\u5728\u6784\u5EFA\u9879\u76EE: ${pc4.bold(pkg.name || "unnamed")} (\u5185\u6838\u7248\u672C: ${version})
1272
+ logger.info(`\u6B63\u5728\u6784\u5EFA\u9879\u76EE: ${pc5.bold(pkg.name || "unnamed")} (\u5185\u6838\u7248\u672C: ${version})
1016
1273
  `);
1017
1274
  const spinner = createSpinner("\u6B63\u5728\u51C6\u5907\u6784\u5EFA\u73AF\u5883...").start();
1018
1275
  spinner.text = "\u6B63\u5728\u540C\u6B65\u6C99\u7BB1\u5185\u6838...";
1019
1276
  await Sandbox.prepare(version, options.force);
1020
- if (pkg.workspaces || fs5.existsSync(path6.join(cwd, "pnpm-workspace.yaml"))) {
1277
+ if (pkg.workspaces || fs9.existsSync(path11.join(cwd, "pnpm-workspace.yaml"))) {
1021
1278
  spinner.text = "\u68C0\u6D4B\u5230 Monorepo\uFF0C\u6B63\u5728\u521D\u59CB\u5316\u6839\u76EE\u5F55\u4E0A\u4E0B\u6587...";
1022
1279
  await Sandbox.injectContext(cwd, version);
1023
1280
  spinner.text = "\u6B63\u5728\u540C\u6B65\u5B50\u5305\u4E0A\u4E0B\u6587...";
1024
1281
  const subDirs = ["apps", "plugins", "packages"];
1025
1282
  for (const dir of subDirs) {
1026
- const dirPath = path6.join(cwd, dir);
1027
- if (fs5.existsSync(dirPath)) {
1028
- const entries = await fs5.readdir(dirPath, { withFileTypes: true });
1283
+ const dirPath = path11.join(cwd, dir);
1284
+ if (fs9.existsSync(dirPath)) {
1285
+ const entries = await fs9.readdir(dirPath, { withFileTypes: true });
1029
1286
  for (const entry2 of entries) {
1030
1287
  if (entry2.isDirectory()) {
1031
- const subPkgPath = path6.join(dirPath, entry2.name);
1032
- if (fs5.existsSync(path6.join(subPkgPath, "package.json"))) {
1288
+ const subPkgPath = path11.join(dirPath, entry2.name);
1289
+ if (fs9.existsSync(path11.join(subPkgPath, "package.json"))) {
1033
1290
  await Sandbox.injectContext(subPkgPath, version);
1034
1291
  }
1035
1292
  }
@@ -1043,7 +1300,7 @@ async function build(options) {
1043
1300
  if (pkg.scripts && pkg.scripts.build && !process.env.CHATBI_CLI_INTERNAL) {
1044
1301
  const buildScript = pkg.scripts.build;
1045
1302
  if (!buildScript.includes("chatbi-cli build") && !buildScript.includes("chatbi build")) {
1046
- logger.info(`\u68C0\u6D4B\u5230\u81EA\u5B9A\u4E49 build \u811A\u672C\uFF0C\u6B63\u5728\u6267\u884C: ${pc4.cyan("npm run build")}`);
1303
+ logger.info(`\u68C0\u6D4B\u5230\u81EA\u5B9A\u4E49 build \u811A\u672C\uFF0C\u6B63\u5728\u6267\u884C: ${pc5.cyan("npm run build")}`);
1047
1304
  const args = ["run", "build"];
1048
1305
  if (options.watch) {
1049
1306
  args.push("--", "--watch");
@@ -1067,7 +1324,7 @@ async function build(options) {
1067
1324
  const coreAlias = Sandbox.getCoreAlias(version);
1068
1325
  await viteBuild({
1069
1326
  root: cwd,
1070
- configFile: fs5.existsSync(path6.join(cwd, "vite.config.ts")) ? void 0 : false,
1327
+ configFile: fs9.existsSync(path11.join(cwd, "vite.config.ts")) ? void 0 : false,
1071
1328
  resolve: {
1072
1329
  alias: {
1073
1330
  ...coreAlias
@@ -1091,45 +1348,32 @@ async function build(options) {
1091
1348
  });
1092
1349
  if (!options.watch) {
1093
1350
  printBox(
1094
- pc4.green(pc4.bold("\u2728 \u5E94\u7528\u6784\u5EFA\u6210\u529F!")) + "\n\n" + pc4.white("\u4EA7\u7269\u76EE\u5F55: ") + pc4.cyan("dist"),
1351
+ pc5.green(pc5.bold("\u2728 \u5E94\u7528\u6784\u5EFA\u6210\u529F!")) + "\n\n" + pc5.white("\u4EA7\u7269\u76EE\u5F55: ") + pc5.cyan("dist"),
1095
1352
  "Build Success"
1096
1353
  );
1097
1354
  }
1098
1355
  return;
1099
1356
  }
1100
1357
  let entry = ["src/index.ts"];
1101
- if (fs5.existsSync(path6.join(cwd, "src/index.tsx"))) {
1358
+ if (fs9.existsSync(path11.join(cwd, "src/index.tsx"))) {
1102
1359
  entry = ["src/index.tsx"];
1103
- } else if (!fs5.existsSync(path6.join(cwd, "src/index.ts"))) {
1360
+ } else if (!fs9.existsSync(path11.join(cwd, "src/index.ts"))) {
1104
1361
  logger.error("\u672A\u627E\u5230\u5165\u53E3\u6587\u4EF6\u3002\u671F\u671B src/index.ts \u6216 src/index.tsx");
1105
1362
  return;
1106
1363
  }
1107
1364
  const isPlugin = mode === "plugin";
1365
+ const userConfig = await ConfigManager.loadTsupConfig(cwd);
1366
+ if (Object.keys(userConfig).length > 0) {
1367
+ logger.info(`\u5DF2\u52A0\u8F7D\u672C\u5730\u6784\u5EFA\u914D\u7F6E`);
1368
+ }
1108
1369
  if (!options.watch) {
1109
- await fs5.remove(path6.join(cwd, "dist"));
1110
- const tsbuildinfo = path6.join(cwd, "tsconfig.tsbuildinfo");
1111
- if (fs5.existsSync(tsbuildinfo)) {
1112
- await fs5.remove(tsbuildinfo);
1370
+ await fs9.remove(path11.join(cwd, "dist"));
1371
+ const tsbuildinfo = path11.join(cwd, "tsconfig.tsbuildinfo");
1372
+ if (fs9.existsSync(tsbuildinfo)) {
1373
+ await fs9.remove(tsbuildinfo);
1113
1374
  }
1114
1375
  }
1115
- const external = [
1116
- "react",
1117
- "react-dom",
1118
- "react/jsx-runtime",
1119
- "react-is",
1120
- "antd",
1121
- "@ant-design/icons",
1122
- "@ant-design/x",
1123
- ...Object.keys(pkg.dependencies || {}),
1124
- ...Object.keys(pkg.peerDependencies || {})
1125
- ];
1126
1376
  if (isPlugin) {
1127
- const coreDeps = Sandbox.CORE_PACKAGES;
1128
- coreDeps.forEach((dep) => {
1129
- if (!external.includes(dep)) {
1130
- external.push(dep);
1131
- }
1132
- });
1133
1377
  }
1134
1378
  const commonConfig = {
1135
1379
  entry,
@@ -1138,16 +1382,32 @@ async function build(options) {
1138
1382
  sourcemap: false,
1139
1383
  target: "esnext",
1140
1384
  platform: isPlugin ? "browser" : "node",
1141
- external,
1142
- minify: !options.watch,
1143
- // 生产环境开启压缩混淆
1385
+ ...userConfig,
1386
+ // 合并用户配置
1387
+ external: [
1388
+ .../* @__PURE__ */ new Set([
1389
+ "react",
1390
+ "react-dom",
1391
+ "react/jsx-runtime",
1392
+ "react-is",
1393
+ "antd",
1394
+ "@ant-design/icons",
1395
+ "@ant-design/x",
1396
+ ...Object.keys(pkg.dependencies || {}),
1397
+ ...Object.keys(pkg.peerDependencies || {}),
1398
+ ...isPlugin ? Sandbox.CORE_PACKAGES : [],
1399
+ ...Array.isArray(userConfig.external) ? userConfig.external : []
1400
+ ])
1401
+ ],
1402
+ minify: userConfig.minify !== void 0 ? userConfig.minify : !options.watch,
1403
+ // 优先使用用户配置
1144
1404
  watch: options.watch,
1145
1405
  silent: true,
1146
- esbuildOptions(options2) {
1147
- options2.logOverride = {
1406
+ esbuildOptions(esbuildOpts) {
1407
+ esbuildOpts.logOverride = {
1148
1408
  "empty-import-meta": "silent"
1149
1409
  };
1150
- options2.external = options2.external || [];
1410
+ esbuildOpts.external = esbuildOpts.external || [];
1151
1411
  const forceExternal = [
1152
1412
  "react",
1153
1413
  "react-dom",
@@ -1157,10 +1417,13 @@ async function build(options) {
1157
1417
  ...Sandbox.CORE_PACKAGES
1158
1418
  ];
1159
1419
  forceExternal.forEach((dep) => {
1160
- if (!options2.external.includes(dep)) {
1161
- options2.external.push(dep);
1420
+ if (!esbuildOpts.external.includes(dep)) {
1421
+ esbuildOpts.external.push(dep);
1162
1422
  }
1163
1423
  });
1424
+ if (typeof userConfig.esbuildOptions === "function") {
1425
+ userConfig.esbuildOptions(esbuildOpts);
1426
+ }
1164
1427
  }
1165
1428
  };
1166
1429
  const buildSpinner = createSpinner("\u6B63\u5728\u7F16\u8BD1\u6E90\u7801...").start();
@@ -1181,25 +1444,7 @@ async function build(options) {
1181
1444
  define: {
1182
1445
  "import.meta.env": "process.env"
1183
1446
  },
1184
- esbuildOptions(options2) {
1185
- options2.logOverride = {
1186
- "empty-import-meta": "silent"
1187
- };
1188
- options2.external = options2.external || [];
1189
- const forceExternal = [
1190
- "react",
1191
- "react-dom",
1192
- "antd",
1193
- "@ant-design/icons",
1194
- "@ant-design/x",
1195
- ...Sandbox.CORE_PACKAGES
1196
- ];
1197
- forceExternal.forEach((dep) => {
1198
- if (!options2.external.includes(dep)) {
1199
- options2.external.push(dep);
1200
- }
1201
- });
1202
- }
1447
+ esbuildOptions: commonConfig.esbuildOptions
1203
1448
  });
1204
1449
  } else {
1205
1450
  buildSpinner.text = "\u6B63\u5728\u7F16\u8BD1\u6E90\u7801...";
@@ -1215,15 +1460,15 @@ async function build(options) {
1215
1460
  if (!options.watch) {
1216
1461
  const dtsSpinner = createSpinner("\u6B63\u5728\u751F\u6210\u7C7B\u578B\u5B9A\u4E49...").start();
1217
1462
  try {
1218
- const localTsc = path6.join(cwd, "node_modules/.bin/tsc");
1219
- const sandboxTsc = path6.join(Sandbox.getVersionPath(version), "node_modules/.bin/tsc");
1463
+ const localTsc = path11.join(cwd, "node_modules/.bin/tsc");
1464
+ const sandboxTsc = path11.join(Sandbox.getVersionPath(version), "node_modules/.bin/tsc");
1220
1465
  let tscBin = "tsc";
1221
- if (fs5.existsSync(localTsc)) {
1466
+ if (fs9.existsSync(localTsc)) {
1222
1467
  tscBin = localTsc;
1223
- } else if (fs5.existsSync(sandboxTsc)) {
1468
+ } else if (fs9.existsSync(sandboxTsc)) {
1224
1469
  tscBin = sandboxTsc;
1225
1470
  } else {
1226
- if (fs5.existsSync(path6.join(cwd, "pnpm-lock.yaml"))) {
1471
+ if (fs9.existsSync(path11.join(cwd, "pnpm-lock.yaml"))) {
1227
1472
  await execa2("pnpm", ["exec", "tsc", "--build", "tsconfig.json"]);
1228
1473
  dtsSpinner.succeed("\u7C7B\u578B\u5B9A\u4E49\u751F\u6210\u5B8C\u6210");
1229
1474
  } else {
@@ -1239,7 +1484,7 @@ async function build(options) {
1239
1484
  dtsSpinner.warn("\u7C7B\u578B\u751F\u6210\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u9879\u76EE\u4E2D\u7684 tsconfig.json \u914D\u7F6E");
1240
1485
  }
1241
1486
  printBox(
1242
- pc4.green(pc4.bold("\u2728 \u6784\u5EFA\u5B8C\u6210!")) + "\n\n" + pc4.white("\u4EA7\u7269\u76EE\u5F55: ") + pc4.cyan("dist") + "\n" + pc4.white("\u6784\u5EFA\u6A21\u5F0F: ") + pc4.cyan(isPlugin ? "Plugin" : "Library"),
1487
+ pc5.green(pc5.bold("\u2728 \u6784\u5EFA\u5B8C\u6210!")) + "\n\n" + pc5.white("\u4EA7\u7269\u76EE\u5F55: ") + pc5.cyan("dist") + "\n" + pc5.white("\u6784\u5EFA\u6A21\u5F0F: ") + pc5.cyan(isPlugin ? "Plugin" : "Library"),
1243
1488
  "Build Success"
1244
1489
  );
1245
1490
  }
@@ -1250,6 +1495,7 @@ var init_build = __esm({
1250
1495
  init_esm_shims();
1251
1496
  init_sandbox();
1252
1497
  init_corekit();
1498
+ init_config();
1253
1499
  init_utils();
1254
1500
  }
1255
1501
  });
@@ -1259,17 +1505,17 @@ var sync_exports = {};
1259
1505
  __export(sync_exports, {
1260
1506
  sync: () => sync
1261
1507
  });
1262
- import fs6 from "fs-extra";
1263
- import path7 from "path";
1264
- import pc5 from "picocolors";
1508
+ import fs10 from "fs-extra";
1509
+ import path12 from "path";
1510
+ import pc6 from "picocolors";
1265
1511
  async function sync(options = {}) {
1266
1512
  const cwd = options.cwd || process.cwd();
1267
- const pkgPath = path7.join(cwd, "package.json");
1268
- if (!fs6.existsSync(pkgPath)) {
1513
+ const pkgPath = path12.join(cwd, "package.json");
1514
+ if (!fs10.existsSync(pkgPath)) {
1269
1515
  logger.error("\u672A\u627E\u5230 package.json\u3002\u8BF7\u5728\u9879\u76EE\u6839\u76EE\u5F55\u4E0B\u8FD0\u884C\u3002");
1270
1516
  return;
1271
1517
  }
1272
- const pkg = await fs6.readJson(pkgPath);
1518
+ const pkg = await fs10.readJson(pkgPath);
1273
1519
  let version = options.version;
1274
1520
  if (!version) {
1275
1521
  version = await CoreKit.resolveVersion(cwd);
@@ -1284,8 +1530,8 @@ async function sync(options = {}) {
1284
1530
  await Sandbox.prepare(version, options.force);
1285
1531
  mainSpinner.text = "\u6B63\u5728\u751F\u6210\u865A\u62DF\u4E0A\u4E0B\u6587...";
1286
1532
  if (options.version) {
1287
- const versionFilePath = path7.join(cwd, ".chatbi-version");
1288
- await fs6.writeFile(versionFilePath, version, "utf-8");
1533
+ const versionFilePath = path12.join(cwd, ".chatbi-version");
1534
+ await fs10.writeFile(versionFilePath, version, "utf-8");
1289
1535
  }
1290
1536
  await Sandbox.injectContext(cwd, version);
1291
1537
  let modified = false;
@@ -1307,19 +1553,19 @@ async function sync(options = {}) {
1307
1553
  if (modified) {
1308
1554
  pkg.dependencies = deps;
1309
1555
  pkg.devDependencies = devDeps;
1310
- await fs6.writeJson(pkgPath, pkg, { spaces: 2 });
1556
+ await fs10.writeJson(pkgPath, pkg, { spaces: 2 });
1311
1557
  mainSpinner.succeed("\u5185\u6838\u540C\u6B65\u5B8C\u6210 (\u4F9D\u8D56\u5DF2\u4F18\u5316)");
1312
1558
  } else {
1313
1559
  mainSpinner.succeed("\u5185\u6838\u540C\u6B65\u5B8C\u6210");
1314
1560
  }
1315
1561
  if (!options.silent) {
1316
1562
  printBox(
1317
- `${pc5.green(pc5.bold("\u2728 \u5185\u6838\u540C\u6B65\u6210\u529F!"))}
1563
+ `${pc6.green(pc6.bold("\u2728 \u5185\u6838\u540C\u6B65\u6210\u529F!"))}
1318
1564
 
1319
- ${pc5.white("\u5F53\u524D\u7248\u672C: ")} ${pc5.cyan(version)}
1320
- ${pc5.white("\u6C99\u7BB1\u8DEF\u5F84: ")} ${pc5.gray(Sandbox.getVersionPath(version))}
1565
+ ${pc6.white("\u5F53\u524D\u7248\u672C: ")} ${pc6.cyan(version)}
1566
+ ${pc6.white("\u6C99\u7BB1\u8DEF\u5F84: ")} ${pc6.gray(Sandbox.getVersionPath(version))}
1321
1567
 
1322
- ${pc5.white("\u63D0\u793A: ")} \u9879\u76EE\u73B0\u5728\u901A\u8FC7\u865A\u62DF\u522B\u540D\u5F15\u7528\u6838\u5FC3\u5305\uFF0C\u65E0\u9700\u663E\u5F0F\u5B89\u88C5\u4F9D\u8D56\u3002`,
1568
+ ${pc6.white("\u63D0\u793A: ")} \u9879\u76EE\u73B0\u5728\u901A\u8FC7\u865A\u62DF\u522B\u540D\u5F15\u7528\u6838\u5FC3\u5305\uFF0C\u65E0\u9700\u663E\u5F0F\u5B89\u88C5\u4F9D\u8D56\u3002`,
1323
1569
  "Sync Success"
1324
1570
  );
1325
1571
  }
@@ -1339,13 +1585,12 @@ var init_exports = {};
1339
1585
  __export(init_exports, {
1340
1586
  init: () => init
1341
1587
  });
1342
- import fs7 from "fs-extra";
1343
- import path8 from "path";
1344
- import pc6 from "picocolors";
1345
- import Handlebars2 from "handlebars";
1588
+ import fs11 from "fs-extra";
1589
+ import path13 from "path";
1590
+ import pc7 from "picocolors";
1346
1591
  import prompts2 from "prompts";
1347
1592
  async function init(options) {
1348
- let { name, template, pluginType = "business", theme = "standard", projectType, includeApp, includePlugin } = options;
1593
+ let { name, pluginType = "business", theme = DEFAULT_CONFIG.THEME, projectType, includeApp, includePlugin, cwd } = options;
1349
1594
  const response = await prompts2([
1350
1595
  // ... (prompts remain the same)
1351
1596
  {
@@ -1415,32 +1660,33 @@ async function init(options) {
1415
1660
  const finalIncludePlugin = includePlugin !== void 0 ? includePlugin : response.includePlugin !== void 0 ? response.includePlugin : true;
1416
1661
  const isAppIncluded = finalIncludeApp;
1417
1662
  const isPluginIncluded = finalIncludePlugin;
1418
- const rootDir = process.cwd();
1419
- const targetDir = path8.resolve(rootDir, name);
1420
- if (fs7.existsSync(targetDir)) {
1663
+ const rootDir = cwd || process.cwd();
1664
+ const targetDir = path13.resolve(rootDir, name);
1665
+ if (fs11.existsSync(targetDir)) {
1421
1666
  logger.error(`\u76EE\u5F55 ${name} \u5DF2\u5B58\u5728\u3002`);
1422
1667
  return;
1423
1668
  }
1424
- logger.info(`\u6B63\u5728\u521D\u59CB\u5316\u9879\u76EE: ${pc6.bold(name)}...`);
1425
- logger.info(pc6.gray(`\u7C7B\u578B: ${projectType} | \u4E3B\u9898: ${theme || "N/A"}`));
1669
+ logger.info(`\u6B63\u5728\u521D\u59CB\u5316\u9879\u76EE: ${pc7.bold(name)}...`);
1670
+ logger.info(pc7.gray(`\u7C7B\u578B: ${projectType} | \u4E3B\u9898: ${theme || "N/A"}`));
1426
1671
  console.log("");
1427
1672
  try {
1428
1673
  const myCliRoot = await getCliRoot();
1429
- const cliPkg = await fs7.readJson(path8.join(myCliRoot, "package.json"));
1674
+ const cliPkg = await fs11.readJson(path13.join(myCliRoot, "package.json"));
1430
1675
  const cliVersion = cliPkg.version;
1431
- let kernelVersion = cliVersion;
1432
- const kernelVersionFile = path8.join(myCliRoot, ".chatbi-version");
1433
- if (fs7.existsSync(kernelVersionFile)) {
1434
- kernelVersion = (await fs7.readFile(kernelVersionFile, "utf-8")).trim();
1676
+ const config = { coreSource: "npm" };
1677
+ const kernelVersionFile = path13.join(myCliRoot, SANDBOX_CONFIG.LOCK_FILE);
1678
+ if (fs11.existsSync(kernelVersionFile)) {
1679
+ config.coreVersion = (await fs11.readFile(kernelVersionFile, "utf-8")).trim();
1435
1680
  }
1681
+ const kernelVersion = await ConfigManager.resolveCoreDependency(config);
1436
1682
  const renderTemplate = async (templateName, destDir, extraData = {}) => {
1437
- const srcDir = path8.join(myCliRoot, "templates", templateName);
1438
- if (!fs7.existsSync(srcDir)) {
1683
+ const srcDir = path13.join(myCliRoot, "templates", templateName);
1684
+ if (!fs11.existsSync(srcDir)) {
1439
1685
  throw new Error(`\u627E\u4E0D\u5230\u6A21\u677F: ${templateName}\uFF0C\u8BF7\u68C0\u67E5\u8DEF\u5F84 ${srcDir} \u662F\u5426\u6B63\u786E\u3002`);
1440
1686
  }
1441
- await fs7.ensureDir(destDir);
1687
+ await fs11.ensureDir(destDir);
1442
1688
  const templateData = {
1443
- name: path8.basename(destDir),
1689
+ name: path13.basename(destDir),
1444
1690
  projectName: name,
1445
1691
  // Original project name from CLI argument
1446
1692
  projectTitle: name.split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" "),
@@ -1457,45 +1703,10 @@ async function init(options) {
1457
1703
  isApp: projectType === "app" || isAppIncluded,
1458
1704
  // Zero Dependency Mode: No explicit core path needed in package.json
1459
1705
  // tsconfig extends the virtual paths config
1460
- tsconfigPath: projectType === "monorepo" ? "../../.chatbi/tsconfig.json" : "./.chatbi/tsconfig.json",
1706
+ tsconfigPath: projectType === "monorepo" ? `../../${SANDBOX_CONFIG.DIRS.CACHE}/tsconfig.json` : `./${SANDBOX_CONFIG.DIRS.CACHE}/tsconfig.json`,
1461
1707
  ...extraData
1462
1708
  };
1463
- const processFiles = async (currentSrc, currentDest) => {
1464
- const files = await fs7.readdir(currentSrc);
1465
- for (const file of files) {
1466
- if (file === ".DS_Store") continue;
1467
- const srcPath = path8.join(currentSrc, file);
1468
- const stats = await fs7.stat(srcPath);
1469
- if (stats.isDirectory()) {
1470
- const destPath = path8.join(currentDest, file);
1471
- await fs7.ensureDir(destPath);
1472
- await processFiles(srcPath, destPath);
1473
- } else if (file.endsWith(".hbs")) {
1474
- const content = await fs7.readFile(srcPath, "utf-8");
1475
- let result = content;
1476
- const hasHandlebarsVars = content.includes("{{");
1477
- if (hasHandlebarsVars) {
1478
- try {
1479
- const safeContent = content.replace(/\{\{(?!\s*([#/]?(?:if|else|each|with|unless|name|version|projectName|projectTitle|projectVersion|cliVersion|tsconfigPath|theme|isNebula|isGlass|isBusiness|isApp|isShell|pluginName|pluginPackageName|pluginVersion|pluginDisplayName|pluginDescription|pluginClassName|pluginPath|pluginFolderName|pluginType|pluginId|className))\b)/g, "\\{{");
1480
- const templateFn = Handlebars2.compile(safeContent);
1481
- result = templateFn(templateData);
1482
- } catch (e) {
1483
- result = content;
1484
- for (const [key, val] of Object.entries(templateData)) {
1485
- const regex = new RegExp(`\\{\\{\\s*${key}\\s*\\}\\}`, "g");
1486
- result = result.replace(regex, String(val));
1487
- }
1488
- }
1489
- }
1490
- const destPath = path8.join(currentDest, file.replace(/\.hbs$/, ""));
1491
- await fs7.outputFile(destPath, result);
1492
- } else {
1493
- const destPath = path8.join(currentDest, file);
1494
- await fs7.copy(srcPath, destPath);
1495
- }
1496
- }
1497
- };
1498
- await processFiles(srcDir, destDir);
1709
+ await SandboxRenderer.renderDirectory(srcDir, destDir, templateData);
1499
1710
  };
1500
1711
  const spinner = createSpinner("\u6B63\u5728\u751F\u6210\u9879\u76EE\u7ED3\u6784...").start();
1501
1712
  if (projectType === "monorepo") {
@@ -1503,12 +1714,12 @@ async function init(options) {
1503
1714
  await renderTemplate("monorepo", targetDir, { name });
1504
1715
  if (isAppIncluded) {
1505
1716
  spinner.text = "\u6B63\u5728\u751F\u6210 Host App...";
1506
- const appDir = path8.join(targetDir, "apps", "main");
1717
+ const appDir = path13.join(targetDir, "apps", "main");
1507
1718
  await renderTemplate("app", appDir, { name: "@chatbi-v/main" });
1508
1719
  }
1509
1720
  if (isPluginIncluded) {
1510
1721
  spinner.text = "\u6B63\u5728\u751F\u6210 Demo Plugin...";
1511
- const pluginDir = path8.join(targetDir, "plugins", "plugin-demo");
1722
+ const pluginDir = path13.join(targetDir, "plugins", "plugin-demo");
1512
1723
  await renderTemplate("plugin", pluginDir, {
1513
1724
  name: "@chatbi-v/plugin-demo",
1514
1725
  pluginId: "demo",
@@ -1550,15 +1761,15 @@ async function init(options) {
1550
1761
  const cliConfig = {
1551
1762
  coreVersion: kernelVersion
1552
1763
  };
1553
- await fs7.writeJson(path8.join(targetDir, "chatbi.config.json"), cliConfig, { spaces: 2 });
1764
+ await fs11.writeJson(path13.join(targetDir, "chatbi.config.json"), cliConfig, { spaces: 2 });
1554
1765
  configSpinner.succeed("\u9879\u76EE\u914D\u7F6E\u751F\u6210\u5B8C\u6210");
1555
1766
  printBox(
1556
- `${pc6.green(pc6.bold("\u2728 \u9879\u76EE\u5DF2\u6210\u529F\u521B\u5EFA!"))}
1767
+ `${pc7.green(pc7.bold("\u2728 \u9879\u76EE\u5DF2\u6210\u529F\u521B\u5EFA!"))}
1557
1768
 
1558
- ${pc6.white("\u63A5\u4E0B\u6765\u4F60\u53EF\u4EE5:")}
1559
- ${pc6.cyan(` cd ${name}`)}
1560
- ${pc6.cyan(" pnpm install")}
1561
- ${pc6.cyan(" pnpm dev")}`,
1769
+ ${pc7.white("\u63A5\u4E0B\u6765\u4F60\u53EF\u4EE5:")}
1770
+ ${pc7.cyan(` cd ${name}`)}
1771
+ ${pc7.cyan(" pnpm install")}
1772
+ ${pc7.cyan(" pnpm dev")}`,
1562
1773
  "Success"
1563
1774
  );
1564
1775
  } catch (error) {
@@ -1572,6 +1783,9 @@ var init_init = __esm({
1572
1783
  init_esm_shims();
1573
1784
  init_utils();
1574
1785
  init_sync();
1786
+ init_constants();
1787
+ init_config();
1788
+ init_SandboxRenderer();
1575
1789
  }
1576
1790
  });
1577
1791
 
@@ -1580,34 +1794,34 @@ var fetch_exports = {};
1580
1794
  __export(fetch_exports, {
1581
1795
  fetch: () => fetch
1582
1796
  });
1583
- import path13 from "path";
1584
- import pc12 from "picocolors";
1797
+ import path18 from "path";
1798
+ import pc13 from "picocolors";
1585
1799
  import { execa as execa4 } from "execa";
1586
1800
  async function fetch(version, options = {}) {
1587
- const fetchSpinner = createSpinner(`\u6B63\u5728\u83B7\u53D6\u5185\u6838\u7248\u672C ${pc12.cyan(version)}...`).start();
1801
+ const fetchSpinner = createSpinner(`\u6B63\u5728\u83B7\u53D6\u5185\u6838\u7248\u672C ${pc13.cyan(version)}...`).start();
1588
1802
  const versionPath = await Sandbox.prepare(version, true);
1589
- fetchSpinner.succeed(`\u5185\u6838\u7248\u672C ${pc12.cyan(version)} \u83B7\u53D6\u6210\u529F`);
1803
+ fetchSpinner.succeed(`\u5185\u6838\u7248\u672C ${pc13.cyan(version)} \u83B7\u53D6\u6210\u529F`);
1590
1804
  if (options.pack) {
1591
1805
  const packSpinner = createSpinner("\u6B63\u5728\u6253\u5305\u79BB\u7EBF\u8D44\u6E90...").start();
1592
1806
  const tgzName = `chatbi-core-${version}.tgz`;
1593
- const tgzPath = path13.resolve(process.cwd(), tgzName);
1807
+ const tgzPath = path18.resolve(process.cwd(), tgzName);
1594
1808
  await execa4("tar", [
1595
1809
  "-czf",
1596
1810
  tgzPath,
1597
1811
  "-C",
1598
- path13.dirname(versionPath),
1599
- path13.basename(versionPath)
1812
+ path18.dirname(versionPath),
1813
+ path18.basename(versionPath)
1600
1814
  ]);
1601
1815
  packSpinner.succeed("\u79BB\u7EBF\u8D44\u6E90\u6253\u5305\u5B8C\u6210");
1602
1816
  printBox(
1603
- `${pc12.green(pc12.bold("\u2728 \u79BB\u7EBF\u5305\u5DF2\u751F\u6210!"))}
1817
+ `${pc13.green(pc13.bold("\u2728 \u79BB\u7EBF\u5305\u5DF2\u751F\u6210!"))}
1604
1818
 
1605
- ${pc12.white("\u6587\u4EF6\u8DEF\u5F84: ")} ${pc12.cyan(tgzPath)}
1606
- ${pc12.white("\u5B89\u88C5\u547D\u4EE4: ")} ${pc12.gray(`chatbi install ${tgzName}`)}`,
1819
+ ${pc13.white("\u6587\u4EF6\u8DEF\u5F84: ")} ${pc13.cyan(tgzPath)}
1820
+ ${pc13.white("\u5B89\u88C5\u547D\u4EE4: ")} ${pc13.gray(`chatbi install ${tgzName}`)}`,
1607
1821
  "Pack Success"
1608
1822
  );
1609
1823
  } else {
1610
- logger.success(`\u5185\u6838\u7248\u672C ${pc12.cyan(version)} \u5DF2\u51C6\u5907\u5C31\u7EEA\u3002`);
1824
+ logger.success(`\u5185\u6838\u7248\u672C ${pc13.cyan(version)} \u5DF2\u51C6\u5907\u5C31\u7EEA\u3002`);
1611
1825
  }
1612
1826
  }
1613
1827
  var init_fetch = __esm({
@@ -1624,24 +1838,24 @@ var bench_exports = {};
1624
1838
  __export(bench_exports, {
1625
1839
  bench: () => bench
1626
1840
  });
1627
- import pc14 from "picocolors";
1628
- import fs13 from "fs-extra";
1629
- import path15 from "path";
1841
+ import pc15 from "picocolors";
1842
+ import fs17 from "fs-extra";
1843
+ import path20 from "path";
1630
1844
  import os2 from "os";
1631
1845
  async function bench() {
1632
1846
  logger.info("\u6B63\u5728\u542F\u52A8 CLI \u6027\u80FD\u57FA\u51C6\u6D4B\u8BD5...");
1633
1847
  const results = [];
1634
- const tmpDir = path15.join(os2.tmpdir(), `chatbi-bench-${Date.now()}`);
1635
- await fs13.ensureDir(tmpDir);
1848
+ const tmpDir = path20.join(os2.tmpdir(), `chatbi-bench-${Date.now()}`);
1849
+ await fs17.ensureDir(tmpDir);
1636
1850
  try {
1637
1851
  const initSpinner = createSpinner("\u6B63\u5728\u6D4B\u8BD5: \u521D\u59CB\u5316\u63D2\u4EF6\u9879\u76EE (init)...").start();
1638
1852
  const startInit = Date.now();
1639
1853
  const { init: init2 } = await Promise.resolve().then(() => (init_init(), init_exports));
1640
- const testProjDir = path15.join(tmpDir, "bench-proj");
1854
+ const testProjDir = path20.join(tmpDir, "bench-proj");
1641
1855
  await init2({ name: "bench-proj", projectType: "plugin", cwd: tmpDir });
1642
1856
  const endInit = Date.now();
1643
1857
  const initTime = (endInit - startInit) / 1e3;
1644
- initSpinner.succeed(`\u521D\u59CB\u5316\u5B8C\u6210: ${pc14.cyan(initTime.toFixed(2) + "s")}`);
1858
+ initSpinner.succeed(`\u521D\u59CB\u5316\u5B8C\u6210: ${pc15.cyan(initTime.toFixed(2) + "s")}`);
1645
1859
  results.push({
1646
1860
  scene: "\u521D\u59CB\u5316\u63D2\u4EF6\u9879\u76EE (init)",
1647
1861
  target: "\u2264 5.0 s",
@@ -1653,7 +1867,7 @@ async function bench() {
1653
1867
  await sync2({ cwd: testProjDir });
1654
1868
  const endSync = Date.now();
1655
1869
  const syncTime = (endSync - startSync) / 1e3;
1656
- syncSpinner.succeed(`\u540C\u6B65\u5B8C\u6210: ${pc14.cyan(syncTime.toFixed(2) + "s")}`);
1870
+ syncSpinner.succeed(`\u540C\u6B65\u5B8C\u6210: ${pc15.cyan(syncTime.toFixed(2) + "s")}`);
1657
1871
  results.push({
1658
1872
  scene: "\u6C99\u7BB1\u73AF\u5883\u540C\u6B65 (sync)",
1659
1873
  target: "\u2264 2.0 s",
@@ -1668,20 +1882,20 @@ async function bench() {
1668
1882
  process.chdir(originalCwd);
1669
1883
  const endBuild = Date.now();
1670
1884
  const buildTime = (endBuild - startBuild) / 1e3;
1671
- buildSpinner.succeed(`\u6784\u5EFA\u5B8C\u6210: ${pc14.cyan(buildTime.toFixed(2) + "s")}`);
1885
+ buildSpinner.succeed(`\u6784\u5EFA\u5B8C\u6210: ${pc15.cyan(buildTime.toFixed(2) + "s")}`);
1672
1886
  results.push({
1673
1887
  scene: "\u63D2\u4EF6\u9879\u76EE\u6784\u5EFA (build)",
1674
1888
  target: "\u2264 10.0 s",
1675
1889
  actual: `${buildTime.toFixed(2)} s`
1676
1890
  });
1677
1891
  printBox(
1678
- `${pc14.green(pc14.bold("\u2728 \u57FA\u51C6\u6D4B\u8BD5\u5B8C\u6210!"))}
1892
+ `${pc15.green(pc15.bold("\u2728 \u57FA\u51C6\u6D4B\u8BD5\u5B8C\u6210!"))}
1679
1893
 
1680
- ` + results.map((r) => `${pc14.white(r.scene.padEnd(20))}: ${pc14.cyan(r.actual)} (\u76EE\u6807 ${r.target})`).join("\n"),
1894
+ ` + results.map((r) => `${pc15.white(r.scene.padEnd(20))}: ${pc15.cyan(r.actual)} (\u76EE\u6807 ${r.target})`).join("\n"),
1681
1895
  "Benchmark Results"
1682
1896
  );
1683
1897
  } finally {
1684
- await fs13.remove(tmpDir);
1898
+ await fs17.remove(tmpDir);
1685
1899
  }
1686
1900
  }
1687
1901
  var init_bench = __esm({
@@ -1696,7 +1910,7 @@ var init_bench = __esm({
1696
1910
  init_esm_shims();
1697
1911
  init_build();
1698
1912
  import cac from "cac";
1699
- import pc15 from "picocolors";
1913
+ import pc16 from "picocolors";
1700
1914
  import figlet from "figlet";
1701
1915
  import gradient from "gradient-string";
1702
1916
  import boxen2 from "boxen";
@@ -1716,20 +1930,21 @@ init_init();
1716
1930
  init_esm_shims();
1717
1931
  init_utils();
1718
1932
  init_config();
1719
- import fs8 from "fs-extra";
1720
- import path9 from "path";
1721
- import pc7 from "picocolors";
1933
+ init_constants();
1934
+ init_SandboxRenderer();
1935
+ import fs12 from "fs-extra";
1936
+ import path14 from "path";
1937
+ import pc8 from "picocolors";
1722
1938
  import prompts3 from "prompts";
1723
- import Handlebars3 from "handlebars";
1724
1939
  async function add(options) {
1725
1940
  let { name, type, displayName, description } = options;
1726
1941
  const cwd = process.cwd();
1727
- const pkgPath = path9.join(cwd, "package.json");
1728
- if (!fs8.existsSync(pkgPath)) {
1942
+ const pkgPath = path14.join(cwd, "package.json");
1943
+ if (!fs12.existsSync(pkgPath)) {
1729
1944
  logger.error("\u672A\u627E\u5230 package.json\u3002\u8BF7\u5728 ChatBI-V \u9879\u76EE\u6839\u76EE\u5F55\u4E0B\u8FD0\u884C\u6B64\u547D\u4EE4\u3002");
1730
1945
  return;
1731
1946
  }
1732
- const pkg = await fs8.readJson(pkgPath);
1947
+ const pkg = await fs12.readJson(pkgPath);
1733
1948
  const isMonorepo = pkg.workspaces && (pkg.workspaces.includes("plugins/*") || pkg.workspaces.packages?.includes("plugins/*"));
1734
1949
  if (!isMonorepo) {
1735
1950
  logger.error("\u5F53\u524D\u4E0D\u662F Monorepo \u9879\u76EE\uFF0C\u65E0\u6CD5\u6DFB\u52A0\u63D2\u4EF6 (\u8BF7\u5728\u5305\u542B plugins/ \u5DE5\u4F5C\u7A7A\u95F4\u7684\u9879\u76EE\u4E2D\u8FD0\u884C)");
@@ -1780,31 +1995,22 @@ async function add(options) {
1780
1995
  cleanName = cleanName.replace(/-plugin$/, "");
1781
1996
  }
1782
1997
  const folderName = cleanName.startsWith("plugin-") ? cleanName : `plugin-${cleanName}`;
1783
- const pluginDir = path9.resolve(cwd, "plugins", folderName);
1784
- if (fs8.existsSync(pluginDir)) {
1998
+ const pluginDir = path14.resolve(cwd, "plugins", folderName);
1999
+ if (fs12.existsSync(pluginDir)) {
1785
2000
  logger.error(`\u63D2\u4EF6\u76EE\u5F55 ${folderName} \u5DF2\u5B58\u5728\u3002`);
1786
2001
  return;
1787
2002
  }
1788
- const spinner = createSpinner(`\u6B63\u5728\u6DFB\u52A0\u65B0\u63D2\u4EF6: ${pc7.bold(name)}...`).start();
2003
+ const spinner = createSpinner(`\u6B63\u5728\u6DFB\u52A0\u65B0\u63D2\u4EF6: ${pc8.bold(name)}...`).start();
1789
2004
  const myCliRoot = await getCliRoot();
1790
- const templateDir = path9.join(myCliRoot, "templates/plugin");
1791
- if (!fs8.existsSync(templateDir)) {
2005
+ const templateDir = path14.join(myCliRoot, "templates/plugin");
2006
+ if (!fs12.existsSync(templateDir)) {
1792
2007
  spinner.fail("\u627E\u4E0D\u5230\u63D2\u4EF6\u6A21\u677F");
1793
2008
  throw new Error(`\u627E\u4E0D\u5230\u63D2\u4EF6\u6A21\u677F: ${templateDir}`);
1794
2009
  }
1795
2010
  const targetDir = pluginDir;
1796
2011
  spinner.text = "\u6B63\u5728\u52A0\u8F7D\u914D\u7F6E...";
1797
2012
  const config = await ConfigManager.loadConfig(cwd);
1798
- const coreSource = config.coreSource || "local";
1799
- let corePath = "file:../../.chatbi/core";
1800
- if (coreSource === "npm") {
1801
- if (!config.coreVersion) {
1802
- const cliPkg = await fs8.readJson(path9.join(myCliRoot, "package.json"));
1803
- corePath = cliPkg.version;
1804
- } else {
1805
- corePath = config.coreVersion;
1806
- }
1807
- }
2013
+ const corePath = await ConfigManager.resolveCoreDependency(config, pluginDir);
1808
2014
  const data = {
1809
2015
  // 兼容旧模板
1810
2016
  name: `@chatbi-v/${folderName}`,
@@ -1823,55 +2029,20 @@ async function add(options) {
1823
2029
  pluginPath: folderName,
1824
2030
  pluginFolderName: folderName,
1825
2031
  // Fix: 注入 tsconfigPath,确保 tsconfig.json 能正确 extend
1826
- tsconfigPath: "../../.chatbi/tsconfig.json"
1827
- };
1828
- const renderFile = async (src, dest) => {
1829
- const content = await fs8.readFile(src, "utf-8");
1830
- let result = content;
1831
- const hasHandlebarsVars = content.includes("{{");
1832
- if (hasHandlebarsVars) {
1833
- try {
1834
- const safeContent = content.replace(/\{\{(?!\s*([#/]?(?:if|else|each|with|unless|name|version|projectName|projectTitle|projectVersion|cliVersion|tsconfigPath|theme|isNebula|isGlass|isBusiness|isApp|isShell|pluginName|pluginPackageName|pluginVersion|pluginDisplayName|pluginDescription|pluginClassName|pluginPath|pluginFolderName|pluginType|pluginId|className))\b)/g, "\\{{");
1835
- const templateFn = Handlebars3.compile(safeContent);
1836
- result = templateFn(data);
1837
- } catch (e) {
1838
- result = content;
1839
- for (const [key, val] of Object.entries(data)) {
1840
- const regex = new RegExp(`\\{\\{\\s*${key}\\s*\\}\\}`, "g");
1841
- result = result.replace(regex, String(val));
1842
- }
1843
- }
1844
- }
1845
- await fs8.outputFile(dest.replace(".hbs", ""), result);
1846
- };
1847
- const processTemplates = async (currentSrc, currentDest) => {
1848
- const files = await fs8.readdir(currentSrc);
1849
- for (const file of files) {
1850
- const srcPath = path9.join(currentSrc, file);
1851
- const destPath = path9.join(currentDest, file);
1852
- const stats = await fs8.stat(srcPath);
1853
- if (stats.isDirectory()) {
1854
- await fs8.ensureDir(destPath);
1855
- await processTemplates(srcPath, destPath);
1856
- } else if (file.endsWith(".hbs")) {
1857
- await renderFile(srcPath, destPath);
1858
- } else {
1859
- await fs8.copy(srcPath, destPath);
1860
- }
1861
- }
2032
+ tsconfigPath: `../../${SANDBOX_CONFIG.DIRS.CACHE}/tsconfig.json`
1862
2033
  };
1863
2034
  spinner.text = "\u6B63\u5728\u751F\u6210\u63D2\u4EF6\u6587\u4EF6...";
1864
- await processTemplates(templateDir, targetDir);
1865
- spinner.succeed(`\u63D2\u4EF6 ${pc7.bold(name)} \u6DFB\u52A0\u6210\u529F\uFF01`);
2035
+ await SandboxRenderer.renderDirectory(templateDir, targetDir, data);
2036
+ spinner.succeed(`\u63D2\u4EF6 ${pc8.bold(name)} \u6DFB\u52A0\u6210\u529F\uFF01`);
1866
2037
  printBox(
1867
- `${pc7.green(pc7.bold("\u2728 \u63D2\u4EF6\u521B\u5EFA\u6210\u529F!"))}
2038
+ `${pc8.green(pc8.bold("\u2728 \u63D2\u4EF6\u521B\u5EFA\u6210\u529F!"))}
1868
2039
 
1869
- ${pc7.white("\u9879\u76EE\u8DEF\u5F84: ")} ${pc7.cyan(`plugins/${folderName}`)}
1870
- ${pc7.white("\u63D2\u4EF6 ID: ")} ${pc7.cyan(cleanName)}
1871
- ${pc7.white("\u63D2\u4EF6\u7C7B\u578B: ")} ${pc7.cyan(type)}
2040
+ ${pc8.white("\u9879\u76EE\u8DEF\u5F84: ")} ${pc8.cyan(`plugins/${folderName}`)}
2041
+ ${pc8.white("\u63D2\u4EF6 ID: ")} ${pc8.cyan(cleanName)}
2042
+ ${pc8.white("\u63D2\u4EF6\u7C7B\u578B: ")} ${pc8.cyan(type)}
1872
2043
 
1873
- ${pc7.white("\u63A5\u4E0B\u6765\u4F60\u53EF\u4EE5:")}
1874
- ${pc7.cyan(" pnpm dev")} \u542F\u52A8\u5F00\u53D1\u73AF\u5883\u67E5\u770B\u6548\u679C`,
2044
+ ${pc8.white("\u63A5\u4E0B\u6765\u4F60\u53EF\u4EE5:")}
2045
+ ${pc8.cyan(" pnpm dev")} \u542F\u52A8\u5F00\u53D1\u73AF\u5883\u67E5\u770B\u6548\u679C`,
1875
2046
  "Plugin Created"
1876
2047
  );
1877
2048
  }
@@ -1879,16 +2050,16 @@ ${pc7.cyan(" pnpm dev")} \u542F\u52A8\u5F00\u53D1\u73AF\u5883\u67E5\u770B\u654
1879
2050
  // src/commands/gl.ts
1880
2051
  init_esm_shims();
1881
2052
  init_utils();
1882
- import fs9 from "fs-extra";
1883
- import path10 from "path";
1884
- import pc8 from "picocolors";
2053
+ import fs13 from "fs-extra";
2054
+ import path15 from "path";
2055
+ import pc9 from "picocolors";
1885
2056
  import prompts4 from "prompts";
1886
2057
  import { execa as execa3 } from "execa";
1887
2058
  async function gl(options) {
1888
2059
  const { type: initialType, prompt: initialPrompt } = options;
1889
2060
  const cwd = process.cwd();
1890
- const pkgPath = path10.join(cwd, "package.json");
1891
- if (!fs9.existsSync(pkgPath)) {
2061
+ const pkgPath = path15.join(cwd, "package.json");
2062
+ if (!fs13.existsSync(pkgPath)) {
1892
2063
  logger.error("\u672A\u627E\u5230 package.json\u3002\u8BF7\u5728 ChatBI-V \u9879\u76EE\u6839\u76EE\u5F55\u4E0B\u8FD0\u884C\u6B64\u547D\u4EE4\u3002");
1893
2064
  return;
1894
2065
  }
@@ -1927,7 +2098,7 @@ async function gl(options) {
1927
2098
  logger.error("\u9700\u6C42\u63CF\u8FF0\u4E0D\u80FD\u4E3A\u7A7A\u3002");
1928
2099
  return;
1929
2100
  }
1930
- logger.info(`\u6B63\u5728\u547C\u53EB AI \u5F15\u64CE [\u6A21\u5F0F: ${pc8.bold(genType)}]...`);
2101
+ logger.info(`\u6B63\u5728\u547C\u53EB AI \u5F15\u64CE [\u6A21\u5F0F: ${pc9.bold(genType)}]...`);
1931
2102
  const toolsContext = `
1932
2103
  [Available Tools & Capabilities]
1933
2104
  - Tool: plugin -> For creating new feature modules. Requires: name, type (business|system).
@@ -1990,7 +2161,7 @@ If not, use the SLOT_REQUIRED format.`.trim();
1990
2161
  }
1991
2162
  }
1992
2163
  if (slots.length > 0) {
1993
- logger.info(pc8.yellow("\n\u{1F4DD} AI \u9700\u8981\u66F4\u591A\u4FE1\u606F\u4EE5\u7EE7\u7EED:"));
2164
+ logger.info(pc9.yellow("\n\u{1F4DD} AI \u9700\u8981\u66F4\u591A\u4FE1\u606F\u4EE5\u7EE7\u7EED:"));
1994
2165
  const answers = await prompts4(slots, {
1995
2166
  onCancel: () => process.exit(0)
1996
2167
  });
@@ -2012,7 +2183,7 @@ Please proceed with generation based on these answers.`;
2012
2183
  spinner.stop();
2013
2184
  if (error.code === "ENOENT") {
2014
2185
  logger.error("\u672A\u5728\u7CFB\u7EDF\u4E2D\u627E\u5230 `gemini-cli` \u547D\u4EE4\u3002");
2015
- logger.info(pc8.yellow("\u8BF7\u786E\u4FDD\u5DF2\u5B89\u88C5 gemini-cli \u5E76\u5C06\u5176\u6DFB\u52A0\u5230\u7CFB\u7EDF PATH \u4E2D\u3002"));
2186
+ logger.info(pc9.yellow("\u8BF7\u786E\u4FDD\u5DF2\u5B89\u88C5 gemini-cli \u5E76\u5C06\u5176\u6DFB\u52A0\u5230\u7CFB\u7EDF PATH \u4E2D\u3002"));
2016
2187
  } else {
2017
2188
  logger.error(`AI \u751F\u6210\u8FC7\u7A0B\u4E2D\u51FA\u9519: ${error.message}`);
2018
2189
  }
@@ -2029,16 +2200,16 @@ init_esm_shims();
2029
2200
  init_utils();
2030
2201
  init_sandbox();
2031
2202
  init_corekit();
2032
- import fs10 from "fs-extra";
2033
- import path11 from "path";
2034
- import pc9 from "picocolors";
2203
+ import fs14 from "fs-extra";
2204
+ import path16 from "path";
2205
+ import pc10 from "picocolors";
2035
2206
  import glob from "fast-glob";
2036
2207
  async function doctor(options = {}) {
2037
2208
  logger.info("\u6B63\u5728\u8BCA\u65AD\u9879\u76EE\u5065\u5EB7\u72B6\u51B5...\n");
2038
2209
  const cwd = process.cwd();
2039
2210
  let hasIssues = false;
2040
2211
  const issues = [];
2041
- logger.info(pc9.bold("Step 1: \u68C0\u67E5\u57FA\u7840\u73AF\u5883..."));
2212
+ logger.info(pc10.bold("Step 1: \u68C0\u67E5\u57FA\u7840\u73AF\u5883..."));
2042
2213
  const nodeVersion = process.version;
2043
2214
  if (parseInt(nodeVersion.slice(1).split(".")[0]) < 18) {
2044
2215
  logger.error(`Node.js \u7248\u672C\u592A\u4F4E: ${nodeVersion} (\u8981\u6C42 >=18)`);
@@ -2054,41 +2225,41 @@ async function doctor(options = {}) {
2054
2225
  } catch (e) {
2055
2226
  logger.warn("\u672A\u68C0\u6D4B\u5230 pnpm\uFF0C\u5EFA\u8BAE\u5B89\u88C5\u4EE5\u83B7\u5F97\u6700\u4F73\u4F53\u9A8C");
2056
2227
  }
2057
- logger.info(pc9.bold("\nStep 2: \u68C0\u67E5\u5185\u6838\u6C99\u7BB1\u73AF\u5883..."));
2228
+ logger.info(pc10.bold("\nStep 2: \u68C0\u67E5\u5185\u6838\u6C99\u7BB1\u73AF\u5883..."));
2058
2229
  const version = await CoreKit.resolveVersion(cwd);
2059
2230
  const versionPath = Sandbox.getVersionPath(version);
2060
- if (!fs10.existsSync(versionPath)) {
2231
+ if (!fs14.existsSync(versionPath)) {
2061
2232
  logger.error(`\u5185\u6838\u6C99\u7BB1\u7248\u672C ${version} \u5C1A\u672A\u5B89\u88C5`);
2062
- console.log(pc9.gray(` \u5EFA\u8BAE\u8FD0\u884C 'chatbi sync' \u6216 'chatbi use ${version}' \u8FDB\u884C\u5B89\u88C5\u3002`));
2233
+ console.log(pc10.gray(` \u5EFA\u8BAE\u8FD0\u884C 'chatbi sync' \u6216 'chatbi use ${version}' \u8FDB\u884C\u5B89\u88C5\u3002`));
2063
2234
  hasIssues = true;
2064
2235
  issues.push(`\u5185\u6838\u6C99\u7BB1\u672A\u5B89\u88C5 (${version})`);
2065
2236
  } else {
2066
2237
  logger.success(`\u5185\u6838\u6C99\u7BB1\u5C31\u7EEA (\u7248\u672C: ${version})`);
2067
- const sandboxNm = path11.join(versionPath, "node_modules");
2068
- if (!fs10.existsSync(sandboxNm)) {
2238
+ const sandboxNm = path16.join(versionPath, "node_modules");
2239
+ if (!fs14.existsSync(sandboxNm)) {
2069
2240
  logger.warn("\u6C99\u7BB1\u4F9D\u8D56\u672A\u5B89\u88C5\uFF0C\u5C06\u5BFC\u81F4\u6784\u5EFA\u5931\u8D25");
2070
2241
  hasIssues = true;
2071
2242
  issues.push("\u6C99\u7BB1\u4F9D\u8D56\u7F3A\u5931");
2072
2243
  }
2073
2244
  const missingDeps = [];
2074
2245
  for (const dep of Sandbox.CORE_PACKAGES) {
2075
- if (!fs10.existsSync(path11.join(sandboxNm, dep))) {
2246
+ if (!fs14.existsSync(path16.join(sandboxNm, dep))) {
2076
2247
  missingDeps.push(dep);
2077
2248
  }
2078
2249
  }
2079
2250
  if (missingDeps.length > 0) {
2080
2251
  }
2081
2252
  }
2082
- logger.info(pc9.bold("\nStep 3: \u68C0\u67E5\u865A\u62DF\u4F9D\u8D56\u6620\u5C04..."));
2083
- const chatbiDir = path11.join(cwd, ".chatbi");
2084
- if (!fs10.existsSync(chatbiDir)) {
2253
+ logger.info(pc10.bold("\nStep 3: \u68C0\u67E5\u865A\u62DF\u4F9D\u8D56\u6620\u5C04..."));
2254
+ const chatbiDir = path16.join(cwd, ".chatbi");
2255
+ if (!fs14.existsSync(chatbiDir)) {
2085
2256
  logger.warn("\u672A\u53D1\u73B0\u865A\u62DF\u4F9D\u8D56\u914D\u7F6E (.chatbi \u76EE\u5F55)");
2086
2257
  hasIssues = true;
2087
2258
  issues.push("\u7F3A\u5931 .chatbi \u76EE\u5F55");
2088
2259
  } else {
2089
- const pathsJson = path11.join(chatbiDir, "tsconfig.paths.json");
2090
- const aliasJson = path11.join(chatbiDir, "vite.alias.json");
2091
- if (!fs10.existsSync(pathsJson) || !fs10.existsSync(aliasJson)) {
2260
+ const pathsJson = path16.join(chatbiDir, "tsconfig.paths.json");
2261
+ const aliasJson = path16.join(chatbiDir, "vite.alias.json");
2262
+ if (!fs14.existsSync(pathsJson) || !fs14.existsSync(aliasJson)) {
2092
2263
  logger.warn("\u865A\u62DF\u4F9D\u8D56\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B8C\u6574");
2093
2264
  hasIssues = true;
2094
2265
  issues.push("\u865A\u62DF\u4F9D\u8D56\u914D\u7F6E\u4E0D\u5B8C\u6574");
@@ -2096,10 +2267,10 @@ async function doctor(options = {}) {
2096
2267
  logger.success("\u865A\u62DF\u4F9D\u8D56\u914D\u7F6E\u5DF2\u751F\u6210");
2097
2268
  }
2098
2269
  }
2099
- const tsConfigPath = path11.join(cwd, "tsconfig.json");
2100
- if (fs10.existsSync(tsConfigPath)) {
2270
+ const tsConfigPath = path16.join(cwd, "tsconfig.json");
2271
+ if (fs14.existsSync(tsConfigPath)) {
2101
2272
  try {
2102
- const tsConfig = await fs10.readJson(tsConfigPath);
2273
+ const tsConfig = await fs14.readJson(tsConfigPath);
2103
2274
  const extendsPath = tsConfig.extends;
2104
2275
  const expectedPaths = ["./.chatbi/tsconfig.paths.json", ".chatbi/tsconfig.paths.json"];
2105
2276
  let hasExtend = false;
@@ -2119,14 +2290,14 @@ async function doctor(options = {}) {
2119
2290
  logger.error("\u89E3\u6790 tsconfig.json \u5931\u8D25");
2120
2291
  }
2121
2292
  }
2122
- const viteConfigPath = path11.join(cwd, "vite.config.ts");
2123
- if (fs10.existsSync(viteConfigPath)) {
2124
- const content = await fs10.readFile(viteConfigPath, "utf-8");
2293
+ const viteConfigPath = path16.join(cwd, "vite.config.ts");
2294
+ if (fs14.existsSync(viteConfigPath)) {
2295
+ const content = await fs14.readFile(viteConfigPath, "utf-8");
2125
2296
  if (!content.includes("vite.alias.json")) {
2126
2297
  logger.warn("vite.config.ts \u53EF\u80FD\u672A\u52A0\u8F7D\u865A\u62DF\u522B\u540D\u914D\u7F6E");
2127
2298
  }
2128
2299
  }
2129
- logger.info(pc9.bold("\nStep 4: \u626B\u63CF\u4EE3\u7801\u89C4\u5219..."));
2300
+ logger.info(pc10.bold("\nStep 4: \u626B\u63CF\u4EE3\u7801\u89C4\u5219..."));
2130
2301
  const rules = [
2131
2302
  {
2132
2303
  id: "plugin-lifecycle-init",
@@ -2146,7 +2317,7 @@ async function doctor(options = {}) {
2146
2317
  if (files.length > 0) {
2147
2318
  const scanSpinner = createSpinner(`\u6B63\u5728\u626B\u63CF ${files.length} \u4E2A\u6587\u4EF6...`).start();
2148
2319
  for (const file of files) {
2149
- const content = await fs10.readFile(file, "utf-8");
2320
+ const content = await fs14.readFile(file, "utf-8");
2150
2321
  for (const rule of rules) {
2151
2322
  if (rule.pattern.test(content)) {
2152
2323
  codeIssues.push({
@@ -2162,7 +2333,7 @@ async function doctor(options = {}) {
2162
2333
  scanSpinner.fail(`\u53D1\u73B0 ${codeIssues.length} \u4E2A\u4EE3\u7801\u89C4\u8303\u95EE\u9898`);
2163
2334
  hasIssues = true;
2164
2335
  codeIssues.forEach((issue) => {
2165
- const relativePath = path11.relative(cwd, issue.file);
2336
+ const relativePath = path16.relative(cwd, issue.file);
2166
2337
  issues.push(`\u4EE3\u7801\u89C4\u8303 [${issue.ruleId}]: ${relativePath}`);
2167
2338
  });
2168
2339
  } else {
@@ -2171,26 +2342,26 @@ async function doctor(options = {}) {
2171
2342
  }
2172
2343
  if (!hasIssues) {
2173
2344
  printBox(
2174
- pc9.green(pc9.bold("\u2728 \u8BCA\u65AD\u5B8C\u6210\uFF1A\u9879\u76EE\u4E00\u5207\u6B63\u5E38\uFF01")),
2345
+ pc10.green(pc10.bold("\u2728 \u8BCA\u65AD\u5B8C\u6210\uFF1A\u9879\u76EE\u4E00\u5207\u6B63\u5E38\uFF01")),
2175
2346
  "Doctor Report"
2176
2347
  );
2177
2348
  } else {
2178
- const issueList = issues.map((i) => pc9.red(`\u2022 ${i}`)).join("\n");
2349
+ const issueList = issues.map((i) => pc10.red(`\u2022 ${i}`)).join("\n");
2179
2350
  printBox(
2180
- `${pc9.yellow(pc9.bold("\u26A0\uFE0F \u8BCA\u65AD\u5B8C\u6210\uFF1A\u53D1\u73B0\u4EE5\u4E0B\u95EE\u9898"))}
2351
+ `${pc10.yellow(pc10.bold("\u26A0\uFE0F \u8BCA\u65AD\u5B8C\u6210\uFF1A\u53D1\u73B0\u4EE5\u4E0B\u95EE\u9898"))}
2181
2352
 
2182
2353
  ${issueList}
2183
2354
 
2184
- ${pc9.cyan("\u5EFA\u8BAE\u6839\u636E\u63D0\u793A\u8FDB\u884C\u4FEE\u590D")}`,
2355
+ ${pc10.cyan("\u5EFA\u8BAE\u6839\u636E\u63D0\u793A\u8FDB\u884C\u4FEE\u590D")}`,
2185
2356
  "Doctor Report"
2186
2357
  );
2187
2358
  if (options.fix) {
2188
2359
  if (codeIssues.length > 0) {
2189
2360
  const fixSpinner = createSpinner("\u6B63\u5728\u4FEE\u590D\u4EE3\u7801\u89C4\u8303\u95EE\u9898...").start();
2190
2361
  for (const issue of codeIssues) {
2191
- const content = await fs10.readFile(issue.file, "utf-8");
2362
+ const content = await fs14.readFile(issue.file, "utf-8");
2192
2363
  const newContent = issue.rule.fix(content);
2193
- await fs10.writeFile(issue.file, newContent);
2364
+ await fs14.writeFile(issue.file, newContent);
2194
2365
  }
2195
2366
  fixSpinner.succeed("\u4EE3\u7801\u4FEE\u590D\u5B8C\u6210");
2196
2367
  }
@@ -2206,20 +2377,20 @@ ${pc9.cyan("\u5EFA\u8BAE\u6839\u636E\u63D0\u793A\u8FDB\u884C\u4FEE\u590D")}`,
2206
2377
  init_esm_shims();
2207
2378
  init_corekit();
2208
2379
  init_utils();
2209
- import pc10 from "picocolors";
2380
+ import pc11 from "picocolors";
2210
2381
  async function discover() {
2211
2382
  const cwd = process.cwd();
2212
2383
  const spinner = createSpinner("\u6B63\u5728\u626B\u63CF\u9879\u76EE\u4E2D\u7684\u63D2\u4EF6...").start();
2213
2384
  const plugins = await CoreKit.discoverPlugins(cwd);
2214
2385
  if (plugins.length === 0) {
2215
2386
  spinner.warn("\u672A\u53D1\u73B0\u4EFB\u4F55\u63D2\u4EF6");
2216
- logger.info(pc10.gray("\u8BF7\u786E\u4FDD\u63D2\u4EF6\u4F4D\u4E8E plugins/ \u76EE\u5F55\u4E0B\uFF0C\u4E14\u5305\u542B package.json\u3002"));
2387
+ logger.info(pc11.gray("\u8BF7\u786E\u4FDD\u63D2\u4EF6\u4F4D\u4E8E plugins/ \u76EE\u5F55\u4E0B\uFF0C\u4E14\u5305\u542B package.json\u3002"));
2217
2388
  } else {
2218
- spinner.succeed(`\u53D1\u73B0\u4E86 ${pc10.cyan(plugins.length)} \u4E2A\u63D2\u4EF6`);
2219
- const pluginList = plugins.map((p) => `- ${pc10.bold(p.name)} ${pc10.gray(`(${p.id})`)}
2220
- ${pc10.gray(p.path)}`).join("\n\n");
2389
+ spinner.succeed(`\u53D1\u73B0\u4E86 ${pc11.cyan(plugins.length)} \u4E2A\u63D2\u4EF6`);
2390
+ const pluginList = plugins.map((p) => `- ${pc11.bold(p.name)} ${pc11.gray(`(${p.id})`)}
2391
+ ${pc11.gray(p.path)}`).join("\n\n");
2221
2392
  printBox(
2222
- `${pc10.green(pc10.bold("\u2728 \u63D2\u4EF6\u626B\u63CF\u7ED3\u679C"))}
2393
+ `${pc11.green(pc11.bold("\u2728 \u63D2\u4EF6\u626B\u63CF\u7ED3\u679C"))}
2223
2394
 
2224
2395
  ${pluginList}`,
2225
2396
  "Plugin Discovery"
@@ -2232,7 +2403,7 @@ init_esm_shims();
2232
2403
  init_corekit();
2233
2404
  init_sandbox();
2234
2405
  init_utils();
2235
- import pc11 from "picocolors";
2406
+ import pc12 from "picocolors";
2236
2407
  async function ls() {
2237
2408
  const versions = await CoreKit.listVersions();
2238
2409
  const currentVersion = await CoreKit.resolveVersion(process.cwd());
@@ -2246,22 +2417,22 @@ async function ls() {
2246
2417
  const isProjectCurrent = v === currentVersion;
2247
2418
  let prefix = " ";
2248
2419
  if (isProjectCurrent) {
2249
- prefix = pc11.green(" *");
2420
+ prefix = pc12.green(" *");
2250
2421
  } else if (isGlobalCurrent) {
2251
- prefix = pc11.blue(" >");
2422
+ prefix = pc12.blue(" >");
2252
2423
  }
2253
2424
  let suffix = "";
2254
2425
  if (isProjectCurrent && isGlobalCurrent) {
2255
- suffix = pc11.gray(" (\u5F53\u524D\u9879\u76EE & \u5168\u5C40)");
2426
+ suffix = pc12.gray(" (\u5F53\u524D\u9879\u76EE & \u5168\u5C40)");
2256
2427
  } else if (isProjectCurrent) {
2257
- suffix = pc11.gray(" (\u5F53\u524D\u9879\u76EE)");
2428
+ suffix = pc12.gray(" (\u5F53\u524D\u9879\u76EE)");
2258
2429
  } else if (isGlobalCurrent) {
2259
- suffix = pc11.gray(" (\u5168\u5C40)");
2430
+ suffix = pc12.gray(" (\u5168\u5C40)");
2260
2431
  }
2261
- return `${prefix} ${pc11.white(v)}${suffix}`;
2432
+ return `${prefix} ${pc12.white(v)}${suffix}`;
2262
2433
  }).join("\n");
2263
2434
  printBox(
2264
- list + pc11.gray("\n\n\u63D0\u793A: * \u5F53\u524D\u9879\u76EE\u4F7F\u7528, > \u5168\u5C40\u9ED8\u8BA4\u4F7F\u7528"),
2435
+ list + pc12.gray("\n\n\u63D0\u793A: * \u5F53\u524D\u9879\u76EE\u4F7F\u7528, > \u5168\u5C40\u9ED8\u8BA4\u4F7F\u7528"),
2265
2436
  "Kernel Versions"
2266
2437
  );
2267
2438
  }
@@ -2274,8 +2445,8 @@ init_sandbox();
2274
2445
  init_corekit();
2275
2446
  init_sync();
2276
2447
  init_utils();
2277
- import fs11 from "fs-extra";
2278
- import path12 from "path";
2448
+ import fs15 from "fs-extra";
2449
+ import path17 from "path";
2279
2450
  async function use(version, options = {}) {
2280
2451
  const versions = await CoreKit.listVersions();
2281
2452
  if (!versions.includes(version)) {
@@ -2287,8 +2458,8 @@ async function use(version, options = {}) {
2287
2458
  logger.success(`\u5DF2\u5207\u6362\u5168\u5C40\u5185\u6838\u7248\u672C\u81F3: ${version}`);
2288
2459
  } else {
2289
2460
  const cwd = process.cwd();
2290
- const versionFilePath = path12.join(cwd, ".chatbi-version");
2291
- await fs11.writeFile(versionFilePath, version, "utf-8");
2461
+ const versionFilePath = path17.join(cwd, ".chatbi-version");
2462
+ await fs15.writeFile(versionFilePath, version, "utf-8");
2292
2463
  logger.success(`\u5DF2\u5207\u6362\u5F53\u524D\u9879\u76EE\u5185\u6838\u7248\u672C\u4E3A: ${version}`);
2293
2464
  }
2294
2465
  await sync({ version, force: false });
@@ -2301,16 +2472,16 @@ init_fetch();
2301
2472
  init_esm_shims();
2302
2473
  init_sandbox();
2303
2474
  init_utils();
2304
- import fs12 from "fs-extra";
2305
- import path14 from "path";
2306
- import pc13 from "picocolors";
2475
+ import fs16 from "fs-extra";
2476
+ import path19 from "path";
2477
+ import pc14 from "picocolors";
2307
2478
  import { execa as execa5 } from "execa";
2308
2479
  async function install(target) {
2309
- if (target.endsWith(".tgz") && fs12.existsSync(target)) {
2310
- const spinner = createSpinner(`\u6B63\u5728\u5B89\u88C5\u672C\u5730\u79BB\u7EBF\u5305: ${pc13.bold(target)}...`).start();
2480
+ if (target.endsWith(".tgz") && fs16.existsSync(target)) {
2481
+ const spinner = createSpinner(`\u6B63\u5728\u5B89\u88C5\u672C\u5730\u79BB\u7EBF\u5305: ${pc14.bold(target)}...`).start();
2311
2482
  try {
2312
- const tgzPath = path14.resolve(target);
2313
- const match = path14.basename(target).match(/chatbi-core-(.+)\.tgz/);
2483
+ const tgzPath = path19.resolve(target);
2484
+ const match = path19.basename(target).match(/chatbi-core-(.+)\.tgz/);
2314
2485
  let version = "unknown";
2315
2486
  if (match) {
2316
2487
  version = match[1];
@@ -2320,7 +2491,7 @@ async function install(target) {
2320
2491
  version = `local-${Date.now()}`;
2321
2492
  }
2322
2493
  const versionRoot = Sandbox.getVersionRoot();
2323
- await fs12.ensureDir(versionRoot);
2494
+ await fs16.ensureDir(versionRoot);
2324
2495
  spinner.text = "\u6B63\u5728\u89E3\u538B\u6587\u4EF6...";
2325
2496
  await execa5("tar", ["-xzf", tgzPath, "-C", versionRoot]);
2326
2497
  spinner.succeed(`\u672C\u5730\u5305\u5DF2\u5B89\u88C5\u81F3\u6C99\u7BB1: ${version}`);
@@ -2328,7 +2499,7 @@ async function install(target) {
2328
2499
  await Sandbox.prepare(version);
2329
2500
  spinner.succeed("\u5185\u6838\u73AF\u5883\u5C31\u7EEA");
2330
2501
  printBox(
2331
- pc13.green(pc13.bold("\u2728 \u672C\u5730\u5185\u6838\u5B89\u88C5\u6210\u529F!")) + "\n\n" + pc13.white("\u7248\u672C: ") + pc13.cyan(version) + "\n" + pc13.white("\u8DEF\u5F84: ") + pc13.gray(Sandbox.getVersionPath(version)),
2502
+ pc14.green(pc14.bold("\u2728 \u672C\u5730\u5185\u6838\u5B89\u88C5\u6210\u529F!")) + "\n\n" + pc14.white("\u7248\u672C: ") + pc14.cyan(version) + "\n" + pc14.white("\u8DEF\u5F84: ") + pc14.gray(Sandbox.getVersionPath(version)),
2332
2503
  "Install Success"
2333
2504
  );
2334
2505
  } catch (e) {
@@ -2341,10 +2512,18 @@ async function install(target) {
2341
2512
  }
2342
2513
  }
2343
2514
 
2515
+ // src/commands/clean.ts
2516
+ init_esm_shims();
2517
+ init_sandbox();
2518
+ async function clean(options) {
2519
+ const cwd = process.cwd();
2520
+ await Sandbox.clean(cwd, options.deep);
2521
+ }
2522
+
2344
2523
  // package.json
2345
2524
  var package_default = {
2346
2525
  name: "@chatbi-v/cli",
2347
- version: "2.0.4",
2526
+ version: "2.0.5",
2348
2527
  description: "Standardized CLI tooling for ChatBI Monorepo",
2349
2528
  type: "module",
2350
2529
  main: "dist/index.js",
@@ -2403,7 +2582,7 @@ var showHeader = () => {
2403
2582
  const title = figlet.textSync("ChatBI-V CLI", { font: "Standard" });
2404
2583
  console.log(gradient.pastel.multiline(title));
2405
2584
  console.log(
2406
- boxen2(pc15.cyan(`ChatBI-V \u7EDF\u4E00\u5F00\u53D1\u5DE5\u5177 v${package_default.version}`), {
2585
+ boxen2(pc16.cyan(`ChatBI-V \u7EDF\u4E00\u5F00\u53D1\u5DE5\u5177 v${package_default.version}`), {
2407
2586
  padding: 0,
2408
2587
  margin: { top: 1, bottom: 1 },
2409
2588
  borderStyle: "round",
@@ -2461,13 +2640,14 @@ cli.command("list", "\u5217\u51FA\u6240\u6709\u5DF2\u5B89\u88C5\u7684\u5185\u683
2461
2640
  cli.command("use <version>", "\u5207\u6362\u5F53\u524D\u9879\u76EE\u4F7F\u7528\u7684\u5185\u6838\u7248\u672C").alias("u").option("--global", "\u5207\u6362\u5168\u5C40\u9ED8\u8BA4\u5185\u6838\u7248\u672C").action(wrapAction(use, "use"));
2462
2641
  cli.command("gl", "\u4F7F\u7528 AI \u811A\u624B\u67B6\u751F\u6210\u4EE3\u7801\u6216\u6587\u6863").option("-t, --type <type>", "\u751F\u6210\u7C7B\u578B (plugin|util|doc|component)").option("-p, --prompt <prompt>", "AI \u9700\u6C42\u63CF\u8FF0").action(wrapAction(gl, "gl"));
2463
2642
  cli.command("discover", "\u626B\u63CF\u5E76\u53D1\u73B0\u5F53\u524D\u9879\u76EE\u4E2D\u7684\u63D2\u4EF6").action(wrapAction(discover, "discover"));
2643
+ cli.command("clean", "\u6E05\u7406 CLI \u4EA7\u751F\u7684\u7F13\u5B58\u76EE\u5F55").alias("cl").option("--deep", "\u6DF1\u5EA6\u6E05\u7406 (\u5305\u542B\u6240\u6709\u5B50\u5305\u7684 .chatbi \u76EE\u5F55\u53CA\u5168\u5C40\u6C99\u7BB1\u6839\u76EE\u5F55)").action(wrapAction(clean, "clean"));
2464
2644
  cli.command("bench", "\u8FD0\u884C CLI \u6027\u80FD\u57FA\u51C6\u6D4B\u8BD5").action(wrapAction(async () => {
2465
2645
  const { bench: bench2 } = await Promise.resolve().then(() => (init_bench(), bench_exports));
2466
2646
  await bench2();
2467
2647
  }, "bench"));
2468
2648
  cli.help((sections) => {
2469
2649
  showHeader();
2470
- sections[0].body = pc15.white("ChatBI-V \u5F00\u53D1\u8005\u547D\u4EE4\u884C\u5DE5\u5177\uFF0C\u52A9\u529B\u5FEB\u901F\u5F00\u53D1\u548C\u6784\u5EFA\u63D2\u4EF6\u3002");
2650
+ sections[0].body = pc16.white("ChatBI-V \u5F00\u53D1\u8005\u547D\u4EE4\u884C\u5DE5\u5177\uFF0C\u52A9\u529B\u5FEB\u901F\u5F00\u53D1\u548C\u6784\u5EFA\u63D2\u4EF6\u3002");
2471
2651
  });
2472
2652
  cli.version(package_default.version);
2473
2653
  if (process.argv.length <= 2) {