@chatbi-v/cli 2.0.4 → 2.0.6

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
@@ -1,11 +1,5 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
3
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
4
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
5
- }) : x)(function(x) {
6
- if (typeof require !== "undefined") return require.apply(this, arguments);
7
- throw Error('Dynamic require of "' + x + '" is not supported');
8
- });
9
3
  var __esm = (fn, res) => function __init() {
10
4
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
5
  };
@@ -17,29 +11,28 @@ var __export = (target, all) => {
17
11
  // ../../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
12
  import path from "path";
19
13
  import { fileURLToPath } from "url";
20
- var getFilename, getDirname, __dirname;
21
14
  var init_esm_shims = __esm({
22
15
  "../../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
16
  "use strict";
24
- getFilename = () => fileURLToPath(import.meta.url);
25
- getDirname = () => path.dirname(getFilename());
26
- __dirname = /* @__PURE__ */ getDirname();
27
17
  }
28
18
  });
29
19
 
30
20
  // src/utils.ts
31
21
  import path2 from "path";
22
+ import { createRequire } from "module";
23
+ import { fileURLToPath as fileURLToPath2 } from "url";
32
24
  import fs from "fs-extra";
33
25
  import pc from "picocolors";
34
26
  import ora from "ora";
35
27
  import boxen from "boxen";
36
- var _require, _dirname, logger, createSpinner, printBox, findPackageRoot, getCliRoot;
28
+ var _require, _filename, _dirname, logger, createSpinner, printBox, findPackageRoot, getCliRoot;
37
29
  var init_utils = __esm({
38
30
  "src/utils.ts"() {
39
31
  "use strict";
40
32
  init_esm_shims();
41
- _require = __require;
42
- _dirname = __dirname;
33
+ _require = createRequire(import.meta.url);
34
+ _filename = fileURLToPath2(import.meta.url);
35
+ _dirname = path2.dirname(_filename);
43
36
  logger = {
44
37
  info: (msg) => console.log(pc.cyan(`\u2139 ${msg}`)),
45
38
  success: (msg) => console.log(pc.green(`\u2714 ${msg}`)),
@@ -84,11 +77,7 @@ var init_utils = __esm({
84
77
  if (pkgName === "@chatbi-v/cli") {
85
78
  return path2.resolve(_dirname, "../../");
86
79
  }
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`);
80
+ return null;
92
81
  }
93
82
  };
94
83
  getCliRoot = async () => {
@@ -105,269 +94,299 @@ var init_utils = __esm({
105
94
  checkDir = path2.dirname(checkDir);
106
95
  }
107
96
  if (!myCliRoot) {
108
- myCliRoot = findPackageRoot("@chatbi-v/cli");
97
+ myCliRoot = findPackageRoot("@chatbi-v/cli") || "";
109
98
  }
110
99
  return myCliRoot;
111
100
  };
112
101
  }
113
102
  });
114
103
 
115
- // src/sandbox.ts
116
- import fs2 from "fs-extra";
104
+ // src/constants.ts
117
105
  import path3 from "path";
118
106
  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"() {
107
+ var SANDBOX_CONFIG, GLOBAL_PATHS, DEPENDENCY_VERSIONS, DEFAULT_CONFIG;
108
+ var init_constants = __esm({
109
+ "src/constants.ts"() {
126
110
  "use strict";
127
111
  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
- }
112
+ SANDBOX_CONFIG = {
113
+ /** 用户主目录下的沙箱根目录名 */
114
+ BASE_NAME: ".chatbi-v-core",
115
+ /** 内部子目录结构 */
116
+ DIRS: {
117
+ VERSIONS: "versions",
118
+ CURRENT: "current",
119
+ CACHE: ".chatbi"
120
+ },
121
+ /** 关键标识文件 */
122
+ LOCK_FILE: ".chatbi-version"
123
+ };
124
+ GLOBAL_PATHS = {
125
+ /** 沙箱根路径 */
126
+ BASE_DIR: path3.join(os.homedir(), SANDBOX_CONFIG.BASE_NAME),
127
+ /** Monorepo 扫描目录 */
128
+ MONOREPO_ROOTS: ["apps", "plugins", "packages"]
129
+ };
130
+ DEPENDENCY_VERSIONS = {
131
+ // 基础框架
132
+ "react": "^18.3.1",
133
+ "react-dom": "^18.3.1",
134
+ "antd": "^5.20.0",
135
+ // 构建工具
136
+ "vite": "^5.0.8",
137
+ "typescript": "^5.3.3",
138
+ "tailwindcss": "^3.4.1",
139
+ "autoprefixer": "^10.4.17",
140
+ "postcss": "^8.4.35",
141
+ "less": "^4.2.0",
142
+ // 类型定义
143
+ "@types/react": "^18.3.1",
144
+ "@types/react-dom": "^18.3.1",
145
+ "@types/node": "^20.11.20",
146
+ "@vitejs/plugin-react": "^4.2.1"
147
+ };
148
+ DEFAULT_CONFIG = {
149
+ /** 支持的配置文件名 */
150
+ CONFIG_FILES: [
151
+ "chatbi.config.ts",
152
+ "chatbi.config.js",
153
+ "chatbi.config.json",
154
+ ".chatbirc"
155
+ ],
156
+ /** 默认 UI 主题 */
157
+ THEME: "standard"
158
+ };
159
+ }
160
+ });
161
+
162
+ // src/sandbox/SandboxPath.ts
163
+ import path4 from "path";
164
+ import fs2 from "fs-extra";
165
+ var SandboxPath;
166
+ var init_SandboxPath = __esm({
167
+ "src/sandbox/SandboxPath.ts"() {
168
+ "use strict";
169
+ init_esm_shims();
170
+ init_constants();
171
+ SandboxPath = class {
144
172
  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
- ];
173
+ /**
174
+ * 沙箱的基础存储目录,通常位于用户主目录下的 .chatbi-v-core
175
+ */
176
+ this.BASE_DIR = GLOBAL_PATHS.BASE_DIR;
161
177
  }
178
+ /**
179
+ * 获取沙箱根目录
180
+ * @returns 沙箱根目录的绝对路径
181
+ */
162
182
  static getRoot() {
163
183
  return this.BASE_DIR;
164
184
  }
185
+ /**
186
+ * 获取存储所有版本的根目录
187
+ * @returns versions 目录的绝对路径
188
+ */
165
189
  static getVersionRoot() {
166
- return path3.join(this.BASE_DIR, "versions");
190
+ return path4.join(this.BASE_DIR, SANDBOX_CONFIG.DIRS.VERSIONS);
167
191
  }
192
+ /**
193
+ * 获取指定版本的沙箱路径
194
+ * @param version 版本号,或者 'current' 表示当前激活版本
195
+ * @returns 对应版本的绝对路径
196
+ */
168
197
  static getVersionPath(version) {
169
- if (version === "current") {
170
- return path3.join(this.BASE_DIR, "versions", "current");
198
+ if (version === SANDBOX_CONFIG.DIRS.CURRENT) {
199
+ return path4.join(this.BASE_DIR, SANDBOX_CONFIG.DIRS.VERSIONS, SANDBOX_CONFIG.DIRS.CURRENT);
171
200
  }
172
- return path3.join(this.BASE_DIR, "versions", version);
201
+ return path4.join(this.BASE_DIR, SANDBOX_CONFIG.DIRS.VERSIONS, version);
173
202
  }
174
203
  /**
175
- * 优雅清理缓存环境
176
- * @param cwd 当前工作目录
177
- * @param deep 是否递归清理子包
204
+ * 递归向上查找工作区根目录
205
+ * 识别标准:包含 pnpm-workspace.yaml 或 package.json 中定义了 workspaces
206
+ * @param cwd 起始查找目录
207
+ * @returns 查找到的工作区根目录路径,若未找到则返回 cwd
178
208
  */
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");
209
+ static async getWorkspaceRoot(cwd) {
210
+ let current = cwd;
211
+ while (current !== path4.parse(current).root) {
212
+ const pkgPath = path4.join(current, "package.json");
213
+ const pnpmWorkspacePath = path4.join(current, "pnpm-workspace.yaml");
214
+ if (fs2.existsSync(pnpmWorkspacePath)) return current;
186
215
  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
- }
216
+ try {
217
+ const pkg = await fs2.readJson(pkgPath);
218
+ if (pkg.workspaces) return current;
219
+ } catch (e) {
204
220
  }
205
221
  }
222
+ current = path4.dirname(current);
206
223
  }
207
- if (fs2.existsSync(this.BASE_DIR)) {
208
- await fs2.remove(this.BASE_DIR);
209
- }
224
+ return cwd;
225
+ }
226
+ };
227
+ }
228
+ });
229
+
230
+ // src/sandbox/SandboxRenderer.ts
231
+ import fs3 from "fs-extra";
232
+ import path5 from "path";
233
+ import Handlebars from "handlebars";
234
+ var SandboxRenderer;
235
+ var init_SandboxRenderer = __esm({
236
+ "src/sandbox/SandboxRenderer.ts"() {
237
+ "use strict";
238
+ init_esm_shims();
239
+ Handlebars.registerHelper("json", (obj) => JSON.stringify(obj, null, 2));
240
+ Handlebars.registerHelper("eq", (a, b) => a === b);
241
+ SandboxRenderer = class {
242
+ static {
243
+ /**
244
+ * 需要转义的模板关键字列表
245
+ * 只有匹配这些关键字的 {{ }} 才会被 Handlebars 处理,其余(如 React 的 {{ style }})将被转义跳过
246
+ */
247
+ this.TEMPLATE_KEYWORDS = [
248
+ "name",
249
+ "version",
250
+ "projectName",
251
+ "projectTitle",
252
+ "projectVersion",
253
+ "cliVersion",
254
+ "tsconfigPath",
255
+ "theme",
256
+ "isNebula",
257
+ "isGlass",
258
+ "isBusiness",
259
+ "isApp",
260
+ "isShell",
261
+ "pluginName",
262
+ "pluginPackageName",
263
+ "pluginVersion",
264
+ "pluginDisplayName",
265
+ "pluginDescription",
266
+ "pluginClassName",
267
+ "pluginPath",
268
+ "pluginFolderName",
269
+ "pluginType",
270
+ "pluginId",
271
+ "className",
272
+ "dependencies",
273
+ "devDependencies",
274
+ "json"
275
+ ];
210
276
  }
211
277
  /**
212
- * 准备内核沙箱环境 (安装/更新)
278
+ * 递归渲染目录模板
279
+ * @param srcDir 模板源目录
280
+ * @param destDir 目标输出目录
281
+ * @param data 渲染模板所需的变量上下文
213
282
  */
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");
283
+ static async renderDirectory(srcDir, destDir, data) {
284
+ if (!fs3.existsSync(srcDir)) return;
285
+ const entries = await fs3.readdir(srcDir, { withFileTypes: true });
286
+ for (const entry of entries) {
287
+ const srcPath = path5.join(srcDir, entry.name);
288
+ const destPath = path5.join(destDir, entry.name.replace(/\.hbs$/, ""));
289
+ if (entry.isDirectory()) {
290
+ await fs3.ensureDir(destPath);
291
+ await this.renderDirectory(srcPath, destPath, data);
292
+ } else {
293
+ await this.renderFile(srcPath, destPath, data);
263
294
  }
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
295
  }
273
- return versionPath;
274
296
  }
275
297
  /**
276
- * 确保 Shell 模板已同步到沙箱
298
+ * 渲染单个文件
277
299
  */
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;
300
+ static async renderFile(srcPath, destPath, data) {
301
+ const isTemplate = srcPath.endsWith(".hbs");
302
+ if (!isTemplate) {
303
+ await fs3.copy(srcPath, destPath);
304
+ return;
284
305
  }
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 });
306
+ const content = await fs3.readFile(srcPath, "utf-8");
307
+ if (!content.includes("{{")) {
308
+ await fs3.outputFile(destPath, content);
309
+ return;
310
+ }
311
+ try {
312
+ const keywordsPattern = this.TEMPLATE_KEYWORDS.join("|");
313
+ const safeContent = content.replace(
314
+ new RegExp(`\\{\\{(?!\\{)(?!\\s*([#/]?(?:${keywordsPattern}))\\b)`, "g"),
315
+ "\\{{"
316
+ );
317
+ const template = Handlebars.compile(safeContent);
318
+ const result = template(data);
319
+ await fs3.outputFile(destPath, result);
320
+ } catch (e) {
321
+ let result = content;
322
+ for (const [key, val] of Object.entries(data)) {
323
+ const regex = new RegExp(`\\{\\{\\s*${key}\\s*\\}\\}`, "g");
324
+ result = result.replace(regex, typeof val === "object" ? JSON.stringify(val) : String(val));
325
+ }
326
+ await fs3.outputFile(destPath, result);
316
327
  }
317
- return shellDestDir;
318
328
  }
329
+ };
330
+ }
331
+ });
332
+
333
+ // src/sandbox/SandboxContext.ts
334
+ import fs4 from "fs-extra";
335
+ import path6 from "path";
336
+ var SandboxContext;
337
+ var init_SandboxContext = __esm({
338
+ "src/sandbox/SandboxContext.ts"() {
339
+ "use strict";
340
+ init_esm_shims();
341
+ init_SandboxPath();
342
+ init_constants();
343
+ SandboxContext = class {
319
344
  /**
320
- * 递归渲染目录模板
345
+ * 注入项目虚拟上下文
346
+ * 包括:
347
+ * 1. 确定集中式 .chatbi 存放位置并处理子包软链
348
+ * 2. 生成适配沙箱的 tsconfig.json (配置路径别名)
349
+ * 3. 软链接沙箱的 node_modules 到项目 .chatbi 目录下
350
+ * 4. 确保 .chatbi 被加入 .gitignore
351
+ *
352
+ * @param projectRoot 当前项目根目录
353
+ * @param version 内核版本号
354
+ * @param corePackages 核心包名列表
355
+ * @param runtimeDeps 运行时依赖包名列表
321
356
  */
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);
357
+ static async inject(projectRoot, version, corePackages, runtimeDeps) {
358
+ const workspaceRoot = await SandboxPath.getWorkspaceRoot(projectRoot);
359
+ const chatbiDir = path6.join(workspaceRoot, SANDBOX_CONFIG.DIRS.CACHE);
360
+ await fs4.ensureDir(chatbiDir);
361
+ if (workspaceRoot !== projectRoot) {
362
+ const localChatbi = path6.join(projectRoot, SANDBOX_CONFIG.DIRS.CACHE);
363
+ try {
364
+ const stats = await fs4.lstat(localChatbi).catch(() => null);
365
+ if (stats) {
366
+ if (stats.isSymbolicLink()) {
367
+ const linkTarget = await fs4.readlink(localChatbi);
368
+ if (linkTarget !== chatbiDir) {
369
+ await fs4.remove(localChatbi);
370
+ await fs4.symlink(chatbiDir, localChatbi, "dir");
347
371
  }
348
372
  } else {
349
- await fs2.outputFile(destPath, content);
373
+ await fs4.remove(localChatbi);
374
+ await fs4.symlink(chatbiDir, localChatbi, "dir");
350
375
  }
351
376
  } else {
352
- await fs2.copy(srcPath, destPath);
377
+ await fs4.symlink(chatbiDir, localChatbi, "dir");
353
378
  }
379
+ } catch (e) {
354
380
  }
355
381
  }
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");
382
+ const versionPath = SandboxPath.getVersionPath(version);
383
+ const sandboxNodeModules = path6.join(versionPath, "node_modules");
365
384
  const corePaths = {};
366
- for (const pkgName of _Sandbox.CORE_PACKAGES) {
385
+ for (const pkgName of corePackages) {
367
386
  corePaths[pkgName] = [`./node_modules/${pkgName}`];
368
387
  corePaths[`${pkgName}/*`] = [`./node_modules/${pkgName}/*`];
369
388
  }
370
- for (const pkgName of _Sandbox.RUNTIME_DEPS) {
389
+ for (const pkgName of runtimeDeps) {
371
390
  corePaths[pkgName] = [`./node_modules/${pkgName}`];
372
391
  corePaths[`${pkgName}/*`] = [`./node_modules/${pkgName}/*`];
373
392
  if (["react", "react-dom", "node"].includes(pkgName)) {
@@ -378,152 +397,119 @@ var init_sandbox = __esm({
378
397
  }
379
398
  corePaths["@types/*"] = ["./node_modules/@types/*"];
380
399
  corePaths["vite/client"] = ["./node_modules/vite/client.d.ts"];
381
- const baseConfigPath = path3.join(sandboxNodeModules, "@chatbi-v/tsconfig/base.json");
382
- let projectPaths = { "@/*": ["../src/*"] };
400
+ const baseConfigPath = path6.join(sandboxNodeModules, "@chatbi-v/tsconfig/base.json");
383
401
  const tsConfig = {
384
402
  extends: baseConfigPath,
385
403
  compilerOptions: {
386
404
  baseUrl: ".",
387
405
  moduleResolution: "bundler",
388
406
  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
407
+ typeRoots: ["./node_modules/@types"],
395
408
  preserveSymlinks: true,
396
409
  paths: {
397
410
  ...corePaths,
398
- ...projectPaths,
399
- // Fallback: Try to find anything else in the virtual node_modules
411
+ "@/*": ["../src/*"],
400
412
  "*": ["./node_modules/*"]
401
413
  }
402
414
  }
403
415
  };
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 {
416
+ await fs4.writeJson(path6.join(chatbiDir, "tsconfig.json"), tsConfig, { spaces: 2 });
417
+ await fs4.writeJson(path6.join(chatbiDir, "tsconfig.paths.json"), { compilerOptions: tsConfig.compilerOptions }, { spaces: 2 });
418
+ const virtualNodeModules = path6.join(chatbiDir, "node_modules");
419
+ if (fs4.existsSync(sandboxNodeModules)) {
411
420
  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);
421
+ await fs4.remove(virtualNodeModules).catch(() => {
422
+ });
423
+ await fs4.ensureSymlink(sandboxNodeModules, virtualNodeModules, "dir");
423
424
  } catch (e) {
424
425
  }
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
426
  }
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");
427
+ const gitignorePath = path6.join(workspaceRoot, ".gitignore");
428
+ if (fs4.existsSync(gitignorePath)) {
429
+ let content = await fs4.readFile(gitignorePath, "utf-8");
430
+ if (!content.includes(SANDBOX_CONFIG.DIRS.CACHE)) {
431
+ await fs4.appendFile(gitignorePath, `
432
+ # ChatBI
433
+ ${SANDBOX_CONFIG.DIRS.CACHE}
434
+ `);
449
435
  }
450
436
  }
451
437
  }
438
+ };
439
+ }
440
+ });
441
+
442
+ // src/sandbox/SandboxPkgManager.ts
443
+ import fs5 from "fs-extra";
444
+ import path7 from "path";
445
+ import pc2 from "picocolors";
446
+ import fg from "fast-glob";
447
+ var SandboxPkgManager;
448
+ var init_SandboxPkgManager = __esm({
449
+ "src/sandbox/SandboxPkgManager.ts"() {
450
+ "use strict";
451
+ init_esm_shims();
452
+ init_utils();
453
+ init_SandboxPath();
454
+ SandboxPkgManager = class {
452
455
  /**
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 到沙箱中 (模拟安装)
456
+ * 尝试将本地开发环境的包 Link 到沙箱中 (模拟安装)
457
+ * 用于 Monorepo 场景下,让沙箱直接使用最新的本地源码
458
+ * @param versionPath 沙箱版本的存储路径
459
+ * @param corePackages 核心包名列表
460
+ * @param runtimeDeps 运行时依赖包名列表
461
+ * @returns 是否成功 Link 了至少一个包
466
462
  */
467
- static async tryLinkLocalPackages(versionPath) {
463
+ static async tryLinkLocalPackages(versionPath, corePackages, runtimeDeps) {
468
464
  try {
469
465
  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);
466
+ const projectRoot = await SandboxPath.getWorkspaceRoot(cliRoot);
467
+ if (projectRoot === cliRoot && !fs5.existsSync(path7.join(projectRoot, "pnpm-workspace.yaml"))) {
468
+ return false;
478
469
  }
479
470
  const possiblePackagesDirs = [
480
- path3.join(projectRoot, "packages"),
481
- // 兼容某些特殊结构,比如 chatbi-v 仓库
482
- path3.resolve(projectRoot, "../chatbi-v/packages")
471
+ path7.join(projectRoot, "packages"),
472
+ // 兼容旧版或其他可能的路径结构
473
+ path7.resolve(projectRoot, "../chatbi-v/packages")
483
474
  ];
484
475
  const localPackages = {};
485
- const patterns = possiblePackagesDirs.filter((dir) => fs2.existsSync(dir)).map((dir) => path3.join(dir, "**/package.json"));
476
+ const patterns = possiblePackagesDirs.filter((dir) => fs5.existsSync(dir)).map((dir) => path7.join(dir, "**/package.json"));
486
477
  if (patterns.length > 0) {
487
478
  const pkgJsonFiles = await fg(patterns, {
488
479
  ignore: ["**/node_modules/**", "**/dist/**", "**/.git/**"],
489
480
  absolute: true,
490
481
  deep: 5
491
- // 允许一定的嵌套深度
492
482
  });
493
483
  for (const pkgJsonPath of pkgJsonFiles) {
494
484
  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);
485
+ const pkgJson = await fs5.readJson(pkgJsonPath);
486
+ if (corePackages.includes(pkgJson.name) || runtimeDeps.includes(pkgJson.name)) {
487
+ localPackages[pkgJson.name] = path7.dirname(pkgJsonPath);
498
488
  }
499
489
  } catch (e) {
500
490
  }
501
491
  }
502
492
  }
503
- if (Object.keys(localPackages).length === 0) {
504
- return false;
505
- }
493
+ if (Object.keys(localPackages).length === 0) return false;
506
494
  const linkSummary = Object.entries(localPackages).map(([name, pkgPath]) => `${pc2.cyan(name.padEnd(30))} -> ${pc2.gray(pkgPath)}`).join("\n");
507
495
  printBox(
508
496
  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
497
  "Monorepo Link Mode"
510
498
  );
511
- const sandboxNodeModules = path3.join(versionPath, "node_modules");
512
- await fs2.ensureDir(sandboxNodeModules);
499
+ const sandboxNodeModules = path7.join(versionPath, "node_modules");
500
+ await fs5.ensureDir(sandboxNodeModules);
513
501
  for (const [name, pkgPath] of Object.entries(localPackages)) {
514
- const targetLinkPath = path3.join(sandboxNodeModules, name);
515
- await fs2.ensureDir(path3.dirname(targetLinkPath));
502
+ const targetLinkPath = path7.join(sandboxNodeModules, name);
503
+ await fs5.ensureDir(path7.dirname(targetLinkPath));
516
504
  try {
517
- const stats = await fs2.lstat(targetLinkPath).catch(() => null);
518
- if (stats) {
519
- await fs2.remove(targetLinkPath);
520
- }
505
+ const stats = await fs5.lstat(targetLinkPath).catch(() => null);
506
+ if (stats) await fs5.remove(targetLinkPath);
521
507
  } catch (e) {
522
508
  }
523
- await fs2.symlink(pkgPath, targetLinkPath, "dir");
509
+ await fs5.symlink(pkgPath, targetLinkPath, "dir");
524
510
  }
525
- const sandboxPkgJsonPath = path3.join(versionPath, "package.json");
526
- const sandboxPkgJson = await fs2.readJson(sandboxPkgJsonPath);
511
+ const sandboxPkgJsonPath = path7.join(versionPath, "package.json");
512
+ const sandboxPkgJson = await fs5.readJson(sandboxPkgJsonPath);
527
513
  const sections = ["dependencies", "devDependencies"];
528
514
  let modified = false;
529
515
  for (const section of sections) {
@@ -536,95 +522,287 @@ var init_sandbox = __esm({
536
522
  }
537
523
  }
538
524
  }
539
- if (modified) {
540
- await fs2.writeJson(sandboxPkgJsonPath, sandboxPkgJson, { spaces: 2 });
541
- }
525
+ if (modified) await fs5.writeJson(sandboxPkgJsonPath, sandboxPkgJson, { spaces: 2 });
542
526
  return true;
543
527
  } catch (e) {
544
528
  console.warn(pc2.yellow(` \u26A0\uFE0F Link \u672C\u5730\u6E90\u7801\u5931\u8D25\uFF0C\u5C06\u5C1D\u8BD5\u4ECE NPM \u5B89\u88C5: ${e.message}`));
545
529
  return false;
546
530
  }
547
531
  }
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);
532
+ };
533
+ }
534
+ });
535
+
536
+ // src/sandbox.ts
537
+ import fs6 from "fs-extra";
538
+ import path8 from "path";
539
+ import pc3 from "picocolors";
540
+ import fg2 from "fast-glob";
541
+ import { execa } from "execa";
542
+ var Sandbox;
543
+ var init_sandbox = __esm({
544
+ "src/sandbox.ts"() {
545
+ "use strict";
546
+ init_esm_shims();
547
+ init_utils();
548
+ init_SandboxPath();
549
+ init_SandboxRenderer();
550
+ init_SandboxContext();
551
+ init_SandboxPkgManager();
552
+ init_constants();
553
+ Sandbox = class {
554
+ static {
555
+ /** 核心源码包列表 */
556
+ this.CORE_PACKAGES = [
557
+ "@chatbi-v/core",
558
+ "@chatbi-v/mocks",
559
+ "@chatbi-v/tsconfig",
560
+ "@chatbi-v/tailwind-config",
561
+ "@chatbi-v/plugin-theme-manager",
562
+ "@chatbi-v/plugin-layout-transform",
563
+ "@chatbi-v/plugin-system-monitor"
564
+ ];
565
+ }
566
+ static {
567
+ /** 运行时必须依赖 */
568
+ this.RUNTIME_DEPS = [
569
+ "@ant-design/x",
570
+ "@ant-design/icons",
571
+ "antd",
572
+ "react",
573
+ "react-dom",
574
+ "lucide-react",
575
+ "framer-motion",
576
+ "clsx",
577
+ "tailwind-merge",
578
+ "react-router-dom",
579
+ "zustand",
580
+ "axios",
581
+ "less",
582
+ "vite"
583
+ ];
584
+ }
585
+ /** 获取沙箱根目录 */
586
+ static getRoot() {
587
+ return SandboxPath.getRoot();
588
+ }
589
+ /** 获取所有版本的根目录 */
590
+ static getVersionRoot() {
591
+ return SandboxPath.getVersionRoot();
592
+ }
593
+ /** 获取指定版本的沙箱路径 */
594
+ static getVersionPath(version) {
595
+ return SandboxPath.getVersionPath(version);
596
+ }
597
+ /** 获取项目的工作区根目录 (Monorepo 支持) */
598
+ static async getWorkspaceRoot(cwd) {
599
+ return SandboxPath.getWorkspaceRoot(cwd);
600
+ }
601
+ /** 清理指定版本的沙箱物理文件 */
602
+ static async cleanVersion(version) {
603
+ const versionPath = this.getVersionPath(version);
604
+ if (fs6.existsSync(versionPath)) {
605
+ await fs6.remove(versionPath);
560
606
  }
561
- await fs2.symlink(targetPath, currentLinkPath, "dir");
562
607
  }
563
608
  /**
564
- * 可视化展示沙箱状态
609
+ * 优雅清理缓存环境
610
+ * @param cwd 当前项目路径
611
+ * @param deep 是否执行深度清理
565
612
  */
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))}`;
613
+ static async clean(cwd, deep = false) {
614
+ const spinner = createSpinner("\u6B63\u5728\u6E05\u7406\u7F13\u5B58\u76EE\u5F55...").start();
615
+ try {
616
+ const workspaceRoot = await this.getWorkspaceRoot(cwd);
617
+ let cleanedCount = 0;
618
+ const cachePatterns = [
619
+ path8.join(workspaceRoot, `**/${SANDBOX_CONFIG.DIRS.CACHE}`)
620
+ ];
621
+ const cacheDirs = await fg2(cachePatterns, {
622
+ onlyFiles: false,
623
+ // 允许匹配文件和符号链接
624
+ absolute: true,
625
+ ignore: ["**/node_modules/**"],
626
+ dot: true
627
+ // 确保能匹配到以 . 开头的目录
628
+ });
629
+ for (const dir of cacheDirs) {
630
+ const stats = await fs6.lstat(dir).catch(() => null);
631
+ if (stats) {
632
+ await fs6.remove(dir);
633
+ cleanedCount++;
634
+ }
635
+ }
636
+ if (deep) {
637
+ const distPatterns = [
638
+ path8.join(workspaceRoot, "**/dist")
639
+ ];
640
+ const distDirs = await fg2(distPatterns, {
641
+ onlyFiles: false,
642
+ absolute: true,
643
+ ignore: [
644
+ "**/node_modules/**",
645
+ "**/packages/cli/dist/**",
646
+ // 保护 CLI 自身构建产物
647
+ "**/.git/**"
648
+ ]
649
+ });
650
+ for (const dir of distDirs) {
651
+ const stats = await fs6.lstat(dir).catch(() => null);
652
+ if (stats) {
653
+ const hasPkg = fs6.existsSync(path8.join(path8.dirname(dir), "package.json"));
654
+ if (hasPkg) {
655
+ await fs6.remove(dir);
656
+ cleanedCount++;
588
657
  }
589
- } catch (e) {
590
658
  }
591
659
  }
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}`;
660
+ const globalRoot = this.getRoot();
661
+ if (fs6.existsSync(globalRoot)) {
662
+ await fs6.remove(globalRoot);
663
+ cleanedCount++;
664
+ }
665
+ }
666
+ spinner.succeed(pc3.green(`\u6E05\u7406\u5B8C\u6210\uFF0C\u5171\u6E05\u7406 ${cleanedCount} \u4E2A\u9879\u76EE/\u7F13\u5B58\u76EE\u5F55`));
667
+ } catch (e) {
668
+ spinner.fail(pc3.red(`\u6E05\u7406\u5931\u8D25: ${e.message}`));
597
669
  }
598
- printBox(statusText, "ChatBI \u5185\u6838\u53EF\u89C2\u6D4B\u6027\u9762\u677F");
599
670
  }
600
671
  /**
601
- * 获取当前项目使用的内核版本
672
+ * 准备内核沙箱环境 (安装/更新)
602
673
  */
603
- static async getCurrentVersion(projectRoot) {
674
+ static async prepare(version, force = false) {
675
+ const versionPath = this.getVersionPath(version);
676
+ if (!force && fs6.existsSync(path8.join(versionPath, "node_modules/tailwindcss"))) {
677
+ return versionPath;
678
+ }
679
+ if (fs6.existsSync(versionPath)) {
680
+ await fs6.remove(path8.join(versionPath, "node_modules"));
681
+ }
682
+ await fs6.ensureDir(versionPath);
683
+ const spinner = createSpinner(`\u6B63\u5728\u521D\u59CB\u5316\u5185\u6838\u6C99\u7BB1 ${pc3.cyan(version)}...`).start();
604
684
  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
- }
685
+ const cliRoot = await getCliRoot();
686
+ const dependencies = {};
687
+ for (const pkg of this.RUNTIME_DEPS) {
688
+ dependencies[pkg] = DEPENDENCY_VERSIONS[pkg] || "latest";
616
689
  }
690
+ for (const pkg of this.CORE_PACKAGES) {
691
+ dependencies[pkg] = pkg === "@chatbi-v/core" ? version : "latest";
692
+ }
693
+ const templateData = {
694
+ version,
695
+ dependencies,
696
+ devDependencies: DEPENDENCY_VERSIONS
697
+ };
698
+ await SandboxRenderer.renderDirectory(
699
+ path8.join(cliRoot, "templates/sandbox"),
700
+ versionPath,
701
+ templateData
702
+ );
703
+ spinner.text = pc3.gray(" \u{1F50D} \u68C0\u67E5\u672C\u5730\u5F00\u53D1\u73AF\u5883...");
704
+ const isLocalDev = await SandboxPkgManager.tryLinkLocalPackages(versionPath, this.CORE_PACKAGES, this.RUNTIME_DEPS);
705
+ const installArgs = isLocalDev ? ["install", "--no-frozen-lockfile"] : ["install"];
706
+ spinner.text = pc3.gray(` \u23F3 \u6267\u884C pnpm ${installArgs.join(" ")}...`);
707
+ await execa("pnpm", installArgs, { cwd: versionPath });
708
+ spinner.text = pc3.gray(" \u{1F3A8} \u540C\u6B65 Shell \u6A21\u677F...");
709
+ await this.ensureShell(version, force);
710
+ spinner.succeed(pc3.green(`\u5185\u6838\u6C99\u7BB1 ${version} \u521D\u59CB\u5316\u6210\u529F`));
617
711
  } catch (e) {
712
+ spinner.fail(pc3.red(`\u5185\u6838\u6C99\u7BB1\u521D\u59CB\u5316\u5931\u8D25: ${e.message}`));
713
+ throw e;
714
+ }
715
+ return versionPath;
716
+ }
717
+ /**
718
+ * 确保 Shell 模板已同步到沙箱
719
+ */
720
+ static async ensureShell(version, force = false) {
721
+ const versionPath = this.getVersionPath(version);
722
+ const shellDestDir = path8.join(versionPath, "shell");
723
+ if (!force && fs6.existsSync(path8.join(shellDestDir, "tsconfig.paths.json"))) {
724
+ return shellDestDir;
725
+ }
726
+ const cliRoot = await getCliRoot();
727
+ const shellTemplateDir = path8.join(cliRoot, "templates/app");
728
+ if (fs6.existsSync(shellTemplateDir)) {
729
+ await fs6.remove(shellDestDir);
730
+ const templateData = {
731
+ name: "chatbi-shell",
732
+ version,
733
+ cliVersion: version,
734
+ theme: DEFAULT_CONFIG.THEME,
735
+ isShell: true,
736
+ isBusiness: true,
737
+ tsconfigPath: "./tsconfig.paths.json"
738
+ };
739
+ await SandboxRenderer.renderDirectory(shellTemplateDir, shellDestDir, templateData);
740
+ await fs6.writeJson(path8.join(shellDestDir, "tsconfig.paths.json"), {
741
+ compilerOptions: { baseUrl: ".", paths: { "@/*": ["./src/*"] } }
742
+ }, { spaces: 2 });
618
743
  }
619
- return "current";
744
+ return shellDestDir;
745
+ }
746
+ /** 注入项目虚拟上下文 */
747
+ static async injectContext(projectRoot, version) {
748
+ return SandboxContext.inject(projectRoot, version, this.CORE_PACKAGES, this.RUNTIME_DEPS);
749
+ }
750
+ /** 获取核心库的 Vite Alias 配置 */
751
+ static getCoreAlias(version) {
752
+ const versionPath = this.getVersionPath(version);
753
+ const sandboxNodeModules = path8.join(versionPath, "node_modules");
754
+ return Object.fromEntries(this.CORE_PACKAGES.map((pkg) => [pkg, path8.join(sandboxNodeModules, pkg)]));
755
+ }
756
+ /** 切换内核版本 */
757
+ static async useVersion(version) {
758
+ const versionRoot = this.getVersionRoot();
759
+ const currentLink = path8.join(versionRoot, SANDBOX_CONFIG.DIRS.CURRENT);
760
+ const targetPath = this.getVersionPath(version);
761
+ if (!fs6.existsSync(targetPath)) throw new Error(`\u7248\u672C ${version} \u4E0D\u5B58\u5728`);
762
+ if (fs6.existsSync(currentLink)) await fs6.remove(currentLink);
763
+ await fs6.ensureSymlink(targetPath, currentLink, "dir");
764
+ }
765
+ /** 解析版本号 */
766
+ static async resolveVersion(version) {
767
+ if (!version || version === "latest" || version === "current") {
768
+ const currentLink = path8.join(this.getVersionRoot(), SANDBOX_CONFIG.DIRS.CURRENT);
769
+ if (fs6.existsSync(currentLink)) {
770
+ return path8.basename(await fs6.readlink(currentLink));
771
+ }
772
+ const versions = await this.listVersions();
773
+ return versions[0] || "1.0.0";
774
+ }
775
+ return version;
776
+ }
777
+ /** 列出所有已安装版本 */
778
+ static async listVersions() {
779
+ const versionRoot = this.getVersionRoot();
780
+ if (!fs6.existsSync(versionRoot)) return [];
781
+ const dirs = await fs6.readdir(versionRoot);
782
+ return dirs.filter((d) => d !== SANDBOX_CONFIG.DIRS.CURRENT && !d.startsWith(".")).sort().reverse();
783
+ }
784
+ /** 可视化展示沙箱状态 */
785
+ static async visualizeStatus(cwd) {
786
+ const workspaceRoot = await this.getWorkspaceRoot(cwd);
787
+ const isMonorepo = workspaceRoot !== cwd;
788
+ const currentVersion = await this.resolveVersion("current");
789
+ const versions = await this.listVersions();
790
+ const statusInfo = [
791
+ `${pc3.bold("\u9879\u76EE\u8DEF\u5F84:")} ${pc3.cyan(cwd)}`,
792
+ `${pc3.bold("\u67B6\u6784\u6A21\u5F0F:")} ${isMonorepo ? pc3.yellow("Monorepo") : pc3.green("Standalone")}`,
793
+ `${pc3.bold("\u5F53\u524D\u5185\u6838:")} ${pc3.green(currentVersion)}`,
794
+ `${pc3.bold("\u5DF2\u88C5\u7248\u672C:")} ${pc3.gray(versions.join(", ") || "none")}`,
795
+ `${pc3.bold("\u6C99\u7BB1\u6839\u76EE\u5F55:")} ${pc3.gray(this.getRoot())}`
796
+ ].join("\n");
797
+ printBox(statusInfo, "ChatBI Sandbox Status");
620
798
  }
621
799
  };
622
800
  }
623
801
  });
624
802
 
625
803
  // src/config.ts
626
- import fs3 from "fs-extra";
627
- import path4 from "path";
804
+ import fs7 from "fs-extra";
805
+ import path9 from "path";
628
806
  import { createJiti } from "jiti";
629
807
  var ConfigManager;
630
808
  var init_config = __esm({
@@ -632,15 +810,11 @@ var init_config = __esm({
632
810
  "use strict";
633
811
  init_esm_shims();
634
812
  init_utils();
813
+ init_constants();
814
+ init_utils();
635
815
  ConfigManager = class {
636
816
  static {
637
- this.CONFIG_FILES = [
638
- "chatbi.config.ts",
639
- "chatbi.config.js",
640
- "chatbi.config.json",
641
- ".chatbirc",
642
- ".chatbirc.json"
643
- ];
817
+ this.CONFIG_FILES = DEFAULT_CONFIG.CONFIG_FILES;
644
818
  }
645
819
  /**
646
820
  * 加载项目配置
@@ -648,10 +822,10 @@ var init_config = __esm({
648
822
  */
649
823
  static async loadConfig(cwd = process.cwd()) {
650
824
  const config = {};
651
- const versionFilePath = path4.join(cwd, ".chatbi-version");
652
- if (fs3.existsSync(versionFilePath)) {
825
+ const versionFilePath = path9.join(cwd, SANDBOX_CONFIG.LOCK_FILE);
826
+ if (fs7.existsSync(versionFilePath)) {
653
827
  try {
654
- const version = (await fs3.readFile(versionFilePath, "utf-8")).trim();
828
+ const version = (await fs7.readFile(versionFilePath, "utf-8")).trim();
655
829
  if (version) {
656
830
  config.coreVersion = version;
657
831
  }
@@ -660,15 +834,15 @@ var init_config = __esm({
660
834
  }
661
835
  const jiti = createJiti(cwd);
662
836
  for (const file of this.CONFIG_FILES) {
663
- const configPath = path4.join(cwd, file);
664
- if (fs3.existsSync(configPath)) {
837
+ const configPath = path9.join(cwd, file);
838
+ if (fs7.existsSync(configPath)) {
665
839
  try {
666
840
  let projectConfig = {};
667
841
  if (file.endsWith(".ts") || file.endsWith(".js")) {
668
842
  const mod = await jiti.import(configPath, { default: true });
669
843
  projectConfig = mod.default || mod || {};
670
844
  } else if (file.endsWith(".json") || file.startsWith(".chatbirc")) {
671
- projectConfig = await fs3.readJson(configPath);
845
+ projectConfig = await fs7.readJson(configPath);
672
846
  }
673
847
  return {
674
848
  ...projectConfig,
@@ -681,17 +855,78 @@ var init_config = __esm({
681
855
  }
682
856
  return config;
683
857
  }
858
+ /**
859
+ * 解析核心依赖的具体路径或版本号
860
+ * @param config 项目配置
861
+ * @param relativeTo 相对路径(用于 local 模式下的 file: 协议生成)
862
+ */
863
+ static async resolveCoreDependency(config, relativeTo = ".") {
864
+ const { coreSource = "local", coreVersion } = config;
865
+ if (coreSource === "npm") {
866
+ if (coreVersion) return coreVersion;
867
+ const cliRoot = await getCliRoot();
868
+ const cliPkg = await fs7.readJson(path9.join(cliRoot, "package.json"));
869
+ return cliPkg.version;
870
+ }
871
+ const sandboxCorePath = path9.join(SANDBOX_CONFIG.DIRS.CACHE, "core");
872
+ const cwd = process.cwd();
873
+ const absoluteRelativeTo = path9.isAbsolute(relativeTo) ? relativeTo : path9.resolve(cwd, relativeTo);
874
+ const absoluteProjectRoot = cwd;
875
+ const relativePath = path9.relative(absoluteRelativeTo, path9.join(absoluteProjectRoot, sandboxCorePath));
876
+ return `file:${relativePath}`;
877
+ }
878
+ /**
879
+ * 加载 tsup 配置
880
+ * @param cwd 项目根目录
881
+ */
882
+ static async loadTsupConfig(cwd = process.cwd()) {
883
+ const TSUP_CONFIG_FILES = [
884
+ "tsup.config.ts",
885
+ "tsup.config.js",
886
+ "tsup.config.cjs",
887
+ "tsup.config.mjs",
888
+ "tsup.config.json"
889
+ ];
890
+ const jiti = createJiti(cwd);
891
+ for (const file of TSUP_CONFIG_FILES) {
892
+ const configPath = path9.join(cwd, file);
893
+ if (fs7.existsSync(configPath)) {
894
+ try {
895
+ if (file.endsWith(".json")) {
896
+ return await fs7.readJson(configPath);
897
+ } else {
898
+ const mod = await jiti.import(configPath, { default: true });
899
+ return mod.default || mod || {};
900
+ }
901
+ } catch (e) {
902
+ logger.warn(`\u52A0\u8F7D tsup \u914D\u7F6E\u6587\u4EF6 ${file} \u5931\u8D25: ${e.message}`);
903
+ }
904
+ }
905
+ }
906
+ const pkgPath = path9.join(cwd, "package.json");
907
+ if (fs7.existsSync(pkgPath)) {
908
+ try {
909
+ const pkg = await fs7.readJson(pkgPath);
910
+ if (pkg.tsup) {
911
+ return pkg.tsup;
912
+ }
913
+ } catch (e) {
914
+ }
915
+ }
916
+ return {};
917
+ }
684
918
  };
685
919
  }
686
920
  });
687
921
 
688
922
  // src/corekit.ts
689
- import path5 from "path";
690
- import fs4 from "fs-extra";
691
- import pc3 from "picocolors";
923
+ import { createRequire as createRequire2 } from "module";
924
+ import path10 from "path";
925
+ import fs8 from "fs-extra";
926
+ import pc4 from "picocolors";
692
927
  import prompts from "prompts";
693
- import fg2 from "fast-glob";
694
- var CoreKit;
928
+ import fg3 from "fast-glob";
929
+ var nativeRequire, CoreKit;
695
930
  var init_corekit = __esm({
696
931
  "src/corekit.ts"() {
697
932
  "use strict";
@@ -699,25 +934,26 @@ var init_corekit = __esm({
699
934
  init_utils();
700
935
  init_sandbox();
701
936
  init_config();
937
+ nativeRequire = createRequire2(import.meta.url);
702
938
  CoreKit = class {
703
939
  /**
704
940
  * 解析项目模式
705
941
  */
706
942
  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"))) {
943
+ const pkgPath = path10.join(cwd, "package.json");
944
+ if (!fs8.existsSync(pkgPath)) return "app";
945
+ const pkg = await fs8.readJson(pkgPath);
946
+ if (pkg.workspaces || fs8.existsSync(path10.join(cwd, "pnpm-workspace.yaml"))) {
711
947
  return "monorepo";
712
948
  }
713
949
  if (pkg.chatbi?.type === "plugin" || pkg.plugin === true) return "plugin";
714
950
  if (pkg.chatbi?.type === "app") return "app";
715
- const pluginEntries = await fg2(["index.plugin.{ts,js}", "src/index.plugin.{ts,js}"], { cwd });
951
+ const pluginEntries = await fg3(["index.plugin.{ts,js}", "src/index.plugin.{ts,js}"], { cwd });
716
952
  if (pluginEntries.length > 0) return "plugin";
717
- if (fs4.existsSync(path5.join(cwd, "index.html"))) {
953
+ if (fs8.existsSync(path10.join(cwd, "index.html"))) {
718
954
  return "app";
719
955
  }
720
- if (fs4.existsSync(path5.join(cwd, "src/index.ts")) || fs4.existsSync(path5.join(cwd, "src/index.tsx"))) {
956
+ if (fs8.existsSync(path10.join(cwd, "src/index.ts")) || fs8.existsSync(path10.join(cwd, "src/index.tsx"))) {
721
957
  if (pkg.bin) return "lib";
722
958
  if (pkg.peerDependencies?.react && !pkg.dependencies?.react) return "plugin";
723
959
  return "lib";
@@ -735,18 +971,18 @@ var init_corekit = __esm({
735
971
  }
736
972
  try {
737
973
  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();
974
+ const cliVersionFile = path10.join(cliRoot, ".chatbi-version");
975
+ if (fs8.existsSync(cliVersionFile)) {
976
+ const version = (await fs8.readFile(cliVersionFile, "utf-8")).trim();
741
977
  if (version) return version;
742
978
  }
743
979
  } catch (e) {
744
980
  }
745
- const currentLinkPath = path5.join(Sandbox.getVersionRoot(), "current");
746
- if (fs4.existsSync(currentLinkPath)) {
981
+ const currentLinkPath = path10.join(Sandbox.getVersionRoot(), "current");
982
+ if (fs8.existsSync(currentLinkPath)) {
747
983
  try {
748
- const realPath = await fs4.realpath(currentLinkPath);
749
- return path5.basename(realPath);
984
+ const realPath = await fs8.realpath(currentLinkPath);
985
+ return path10.basename(realPath);
750
986
  } catch (e) {
751
987
  }
752
988
  }
@@ -758,8 +994,8 @@ var init_corekit = __esm({
758
994
  */
759
995
  static async listVersions() {
760
996
  const versionsDir = Sandbox.getVersionRoot();
761
- if (!fs4.existsSync(versionsDir)) return [];
762
- const dirs = await fg2("*", {
997
+ if (!fs8.existsSync(versionsDir)) return [];
998
+ const dirs = await fg3("*", {
763
999
  cwd: versionsDir,
764
1000
  onlyDirectories: true,
765
1001
  deep: 1
@@ -770,18 +1006,18 @@ var init_corekit = __esm({
770
1006
  * 自动发现项目中的插件
771
1007
  */
772
1008
  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 });
1009
+ const pluginsDir = path10.join(rootDir, "plugins");
1010
+ if (!fs8.existsSync(pluginsDir)) return [];
1011
+ const pkgFiles = await fg3("*/package.json", { cwd: pluginsDir, absolute: true });
776
1012
  const plugins = [];
777
1013
  for (const pkgPath of pkgFiles) {
778
1014
  try {
779
- const pkg = await fs4.readJson(pkgPath);
780
- const pluginPath = path5.dirname(pkgPath);
1015
+ const pkg = await fs8.readJson(pkgPath);
1016
+ const pluginPath = path10.dirname(pkgPath);
781
1017
  plugins.push({
782
- name: pkg.name || path5.basename(pluginPath),
1018
+ name: pkg.name || path10.basename(pluginPath),
783
1019
  path: pluginPath,
784
- id: path5.basename(pluginPath)
1020
+ id: path10.basename(pluginPath)
785
1021
  });
786
1022
  } catch (e) {
787
1023
  }
@@ -795,19 +1031,19 @@ var init_corekit = __esm({
795
1031
  const mode = await this.detectMode(cwd);
796
1032
  const version = await this.resolveVersion(cwd);
797
1033
  printBox(
798
- `${pc3.cyan(pc3.bold("\u{1F680} ChatBI Dev Server"))}
1034
+ `${pc4.cyan(pc4.bold("\u{1F680} ChatBI Dev Server"))}
799
1035
 
800
- ${pc3.gray("Mode: ")} ${pc3.yellow(mode)}
801
- ${pc3.gray("Kernel: ")} ${pc3.green(version)}
802
- ${pc3.gray("Root: ")} ${pc3.white(cwd)}`,
1036
+ ${pc4.gray("Mode: ")} ${pc4.yellow(mode)}
1037
+ ${pc4.gray("Kernel: ")} ${pc4.green(version)}
1038
+ ${pc4.gray("Root: ")} ${pc4.white(cwd)}`,
803
1039
  "Dev Server"
804
1040
  );
805
1041
  const spinner = createSpinner("\u6B63\u5728\u51C6\u5907\u6C99\u7BB1\u73AF\u5883...").start();
806
1042
  try {
807
1043
  await Sandbox.prepare(version);
808
- spinner.text = pc3.cyan("\u6B63\u5728\u6CE8\u5165\u865A\u62DF\u4E0A\u4E0B\u6587...");
1044
+ spinner.text = pc4.cyan("\u6B63\u5728\u6CE8\u5165\u865A\u62DF\u4E0A\u4E0B\u6587...");
809
1045
  await Sandbox.injectContext(cwd, version);
810
- spinner.succeed(pc3.green("\u73AF\u5883\u5C31\u7EEA"));
1046
+ spinner.succeed(pc4.green("\u73AF\u5883\u5C31\u7EEA"));
811
1047
  if (mode === "plugin") {
812
1048
  await this.startPluginDevServer(cwd, version, options.port);
813
1049
  } else if (mode === "app") {
@@ -816,22 +1052,22 @@ ${pc3.gray("Root: ")} ${pc3.white(cwd)}`,
816
1052
  await this.startMonorepoDevServer(cwd, version, options.port);
817
1053
  }
818
1054
  } catch (e) {
819
- spinner.fail(pc3.red(`\u542F\u52A8\u5931\u8D25: ${e.message}`));
1055
+ spinner.fail(pc4.red(`\u542F\u52A8\u5931\u8D25: ${e.message}`));
820
1056
  }
821
1057
  }
822
1058
  /**
823
1059
  * 启动 Monorepo 开发服务器
824
1060
  */
825
1061
  static async startMonorepoDevServer(rootDir, version, customPort) {
826
- const appsDir = path5.join(rootDir, "apps");
827
- if (!fs4.existsSync(appsDir)) {
1062
+ const appsDir = path10.join(rootDir, "apps");
1063
+ if (!fs8.existsSync(appsDir)) {
828
1064
  logger.warn("\u5F53\u524D Monorepo \u4E0B\u672A\u627E\u5230 apps/ \u76EE\u5F55");
829
1065
  return;
830
1066
  }
831
- const pkgFiles = await fg2("*/package.json", { cwd: appsDir, absolute: true });
1067
+ const pkgFiles = await fg3("*/package.json", { cwd: appsDir, absolute: true });
832
1068
  const apps = pkgFiles.map((pkgPath) => ({
833
- name: path5.basename(path5.dirname(pkgPath)),
834
- path: path5.dirname(pkgPath)
1069
+ name: path10.basename(path10.dirname(pkgPath)),
1070
+ path: path10.dirname(pkgPath)
835
1071
  }));
836
1072
  if (apps.length === 0) {
837
1073
  logger.warn("\u5F53\u524D Monorepo \u4E0B\u672A\u627E\u5230\u4EFB\u4F55\u5E94\u7528 (apps/)");
@@ -858,14 +1094,14 @@ ${pc3.gray("Root: ")} ${pc3.white(cwd)}`,
858
1094
  const shellPort = customPort || 5173;
859
1095
  const sandboxRoot = Sandbox.getRoot();
860
1096
  const versionPath = Sandbox.getVersionPath(version);
861
- const sandboxNodeModules = path5.join(versionPath, "node_modules");
1097
+ const sandboxNodeModules = path10.join(versionPath, "node_modules");
862
1098
  const shellDir = await Sandbox.ensureShell(version);
863
1099
  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");
1100
+ const tailwindPath = path10.join(sandboxNodeModules, "tailwindcss");
1101
+ const autoprefixerPath = path10.join(sandboxNodeModules, "autoprefixer");
1102
+ let pluginEntry = path10.join(pluginDir, "src/index.tsx");
1103
+ if (!fs8.existsSync(pluginEntry)) {
1104
+ pluginEntry = path10.join(pluginDir, "src/index.ts");
869
1105
  }
870
1106
  const define = {
871
1107
  "process.env.CHATBI_PLUGIN_PATH": JSON.stringify(pluginDir)
@@ -878,41 +1114,49 @@ ${pc3.gray("Root: ")} ${pc3.white(cwd)}`,
878
1114
  css: {
879
1115
  postcss: {
880
1116
  plugins: [
881
- __require(tailwindPath)({
882
- presets: [__require(path5.join(sandboxNodeModules, "@chatbi-v/tailwind-config"))],
1117
+ nativeRequire(tailwindPath)({
1118
+ presets: [nativeRequire(path10.join(sandboxNodeModules, "@chatbi-v/tailwind-config"))],
883
1119
  darkMode: "class",
884
1120
  content: [
885
- path5.join(shellDir, "index.html"),
886
- path5.join(shellDir, "src/**/*.{ts,tsx}"),
887
- path5.join(pluginDir, "src/**/*.{ts,tsx}")
1121
+ path10.join(shellDir, "index.html"),
1122
+ path10.join(shellDir, "src/**/*.{ts,tsx}"),
1123
+ path10.join(pluginDir, "src/**/*.{ts,tsx}")
888
1124
  ]
889
1125
  }),
890
- __require(autoprefixerPath)
1126
+ nativeRequire(autoprefixerPath)
891
1127
  ]
892
1128
  }
893
1129
  },
894
1130
  server: {
895
1131
  port: shellPort,
896
1132
  host: true,
1133
+ watch: {
1134
+ // 关键:排除沙箱和软链目录,防止 Vite 挂起
1135
+ ignored: [
1136
+ "**/.chatbi/**",
1137
+ "**/node_modules/**",
1138
+ Sandbox.getRoot()
1139
+ ]
1140
+ },
897
1141
  fs: {
898
1142
  allow: [
899
1143
  sandboxRoot,
900
1144
  pluginDir,
901
- path5.resolve(pluginDir, "../../")
1145
+ path10.resolve(pluginDir, "../../")
902
1146
  ]
903
1147
  }
904
1148
  },
905
1149
  resolve: {
906
1150
  alias: {
907
1151
  ...coreAlias,
908
- "react": path5.join(sandboxNodeModules, "react"),
909
- "react-dom": path5.join(sandboxNodeModules, "react-dom"),
910
- "@": path5.join(shellDir, "src"),
1152
+ "react": path10.join(sandboxNodeModules, "react"),
1153
+ "react-dom": path10.join(sandboxNodeModules, "react-dom"),
1154
+ "@": path10.join(shellDir, "src"),
911
1155
  // 关键:将虚拟模块指向用户插件
912
1156
  "virtual:user-plugin": pluginEntry,
913
1157
  // 防止 glob 报错,提供空的映射
914
- "@chatbi-plugins": path5.join(shellDir, "empty"),
915
- "@chatbi-apps": path5.join(shellDir, "empty")
1158
+ "@chatbi-plugins": path10.join(shellDir, "empty"),
1159
+ "@chatbi-apps": path10.join(shellDir, "empty")
916
1160
  }
917
1161
  },
918
1162
  define
@@ -920,9 +1164,9 @@ ${pc3.gray("Root: ")} ${pc3.white(cwd)}`,
920
1164
  await server.listen();
921
1165
  const localUrl = `http://localhost:${shellPort}/`;
922
1166
  printBox(
923
- `${pc3.green(pc3.bold("\u2705 \u6258\u7BA1\u5F0F Shell \u5DF2\u542F\u52A8"))}
1167
+ `${pc4.green(pc4.bold("\u2705 \u6258\u7BA1\u5F0F Shell \u5DF2\u542F\u52A8"))}
924
1168
 
925
- ${pc3.white("Local: ")} ${pc3.cyan(pc3.underline(localUrl))}`,
1169
+ ${pc4.white("Local: ")} ${pc4.cyan(pc4.underline(localUrl))}`,
926
1170
  "Shell Success"
927
1171
  );
928
1172
  } catch (e) {
@@ -935,7 +1179,7 @@ ${pc3.white("Local: ")} ${pc3.cyan(pc3.underline(localUrl))}`,
935
1179
  static async startAppDevServer(appDir, version, customPort) {
936
1180
  logger.info("\u6B63\u5728\u542F\u52A8\u5E94\u7528...");
937
1181
  const versionPath = Sandbox.getVersionPath(version);
938
- const sandboxNodeModules = path5.join(versionPath, "node_modules");
1182
+ const sandboxNodeModules = path10.join(versionPath, "node_modules");
939
1183
  const coreAlias = Sandbox.getCoreAlias(version);
940
1184
  const { createServer, loadConfigFromFile } = await import("vite");
941
1185
  try {
@@ -949,14 +1193,22 @@ ${pc3.white("Local: ")} ${pc3.cyan(pc3.underline(localUrl))}`,
949
1193
  alias: {
950
1194
  ...coreAlias,
951
1195
  // 补充常用的基础依赖映射,防止用户 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")
1196
+ "react": path10.join(sandboxNodeModules, "react"),
1197
+ "react-dom": path10.join(sandboxNodeModules, "react-dom"),
1198
+ "@vitejs/plugin-react": path10.join(sandboxNodeModules, "@vitejs/plugin-react")
955
1199
  }
956
1200
  },
957
1201
  server: {
958
1202
  port: customPort || 3e3,
959
1203
  host: true,
1204
+ watch: {
1205
+ // 关键:排除沙箱和软链目录,防止 Vite 挂起
1206
+ ignored: [
1207
+ "**/.chatbi/**",
1208
+ "**/node_modules/**",
1209
+ Sandbox.getRoot()
1210
+ ]
1211
+ },
960
1212
  fs: {
961
1213
  allow: [
962
1214
  Sandbox.getRoot(),
@@ -970,9 +1222,9 @@ ${pc3.white("Local: ")} ${pc3.cyan(pc3.underline(localUrl))}`,
970
1222
  await server.listen();
971
1223
  const localUrl = `http://localhost:${server.config.server.port}/`;
972
1224
  printBox(
973
- `${pc3.green(pc3.bold("\u2705 \u5E94\u7528\u5DF2\u6210\u529F\u542F\u52A8"))}
1225
+ `${pc4.green(pc4.bold("\u2705 \u5E94\u7528\u5DF2\u6210\u529F\u542F\u52A8"))}
974
1226
 
975
- ${pc3.white("Local: ")} ${pc3.cyan(pc3.underline(localUrl))}`,
1227
+ ${pc4.white("Local: ")} ${pc4.cyan(pc4.underline(localUrl))}`,
976
1228
  "App Success"
977
1229
  );
978
1230
  } catch (e) {
@@ -989,18 +1241,18 @@ __export(build_exports, {
989
1241
  build: () => build
990
1242
  });
991
1243
  import { execa as execa2 } from "execa";
992
- import fs5 from "fs-extra";
993
- import path6 from "path";
994
- import pc4 from "picocolors";
1244
+ import fs9 from "fs-extra";
1245
+ import path11 from "path";
1246
+ import pc5 from "picocolors";
995
1247
  import { build as tsupBuild } from "tsup";
996
1248
  async function build(options) {
997
1249
  const cwd = process.cwd();
998
- const pkgPath = path6.join(cwd, "package.json");
999
- if (!fs5.existsSync(pkgPath)) {
1250
+ const pkgPath = path11.join(cwd, "package.json");
1251
+ if (!fs9.existsSync(pkgPath)) {
1000
1252
  logger.error(`\u5728 ${cwd} \u4E2D\u672A\u627E\u5230 package.json`);
1001
1253
  return;
1002
1254
  }
1003
- const pkg = await fs5.readJson(pkgPath);
1255
+ const pkg = await fs9.readJson(pkgPath);
1004
1256
  if (options.force) {
1005
1257
  const spinnerClean = createSpinner("\u6B63\u5728\u6E05\u7406\u7F13\u5B58\u73AF\u5883...").start();
1006
1258
  try {
@@ -1012,24 +1264,24 @@ async function build(options) {
1012
1264
  }
1013
1265
  }
1014
1266
  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})
1267
+ logger.info(`\u6B63\u5728\u6784\u5EFA\u9879\u76EE: ${pc5.bold(pkg.name || "unnamed")} (\u5185\u6838\u7248\u672C: ${version})
1016
1268
  `);
1017
1269
  const spinner = createSpinner("\u6B63\u5728\u51C6\u5907\u6784\u5EFA\u73AF\u5883...").start();
1018
1270
  spinner.text = "\u6B63\u5728\u540C\u6B65\u6C99\u7BB1\u5185\u6838...";
1019
1271
  await Sandbox.prepare(version, options.force);
1020
- if (pkg.workspaces || fs5.existsSync(path6.join(cwd, "pnpm-workspace.yaml"))) {
1272
+ if (pkg.workspaces || fs9.existsSync(path11.join(cwd, "pnpm-workspace.yaml"))) {
1021
1273
  spinner.text = "\u68C0\u6D4B\u5230 Monorepo\uFF0C\u6B63\u5728\u521D\u59CB\u5316\u6839\u76EE\u5F55\u4E0A\u4E0B\u6587...";
1022
1274
  await Sandbox.injectContext(cwd, version);
1023
1275
  spinner.text = "\u6B63\u5728\u540C\u6B65\u5B50\u5305\u4E0A\u4E0B\u6587...";
1024
1276
  const subDirs = ["apps", "plugins", "packages"];
1025
1277
  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 });
1278
+ const dirPath = path11.join(cwd, dir);
1279
+ if (fs9.existsSync(dirPath)) {
1280
+ const entries = await fs9.readdir(dirPath, { withFileTypes: true });
1029
1281
  for (const entry2 of entries) {
1030
1282
  if (entry2.isDirectory()) {
1031
- const subPkgPath = path6.join(dirPath, entry2.name);
1032
- if (fs5.existsSync(path6.join(subPkgPath, "package.json"))) {
1283
+ const subPkgPath = path11.join(dirPath, entry2.name);
1284
+ if (fs9.existsSync(path11.join(subPkgPath, "package.json"))) {
1033
1285
  await Sandbox.injectContext(subPkgPath, version);
1034
1286
  }
1035
1287
  }
@@ -1043,7 +1295,7 @@ async function build(options) {
1043
1295
  if (pkg.scripts && pkg.scripts.build && !process.env.CHATBI_CLI_INTERNAL) {
1044
1296
  const buildScript = pkg.scripts.build;
1045
1297
  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")}`);
1298
+ logger.info(`\u68C0\u6D4B\u5230\u81EA\u5B9A\u4E49 build \u811A\u672C\uFF0C\u6B63\u5728\u6267\u884C: ${pc5.cyan("npm run build")}`);
1047
1299
  const args = ["run", "build"];
1048
1300
  if (options.watch) {
1049
1301
  args.push("--", "--watch");
@@ -1067,7 +1319,7 @@ async function build(options) {
1067
1319
  const coreAlias = Sandbox.getCoreAlias(version);
1068
1320
  await viteBuild({
1069
1321
  root: cwd,
1070
- configFile: fs5.existsSync(path6.join(cwd, "vite.config.ts")) ? void 0 : false,
1322
+ configFile: fs9.existsSync(path11.join(cwd, "vite.config.ts")) ? void 0 : false,
1071
1323
  resolve: {
1072
1324
  alias: {
1073
1325
  ...coreAlias
@@ -1091,45 +1343,32 @@ async function build(options) {
1091
1343
  });
1092
1344
  if (!options.watch) {
1093
1345
  printBox(
1094
- pc4.green(pc4.bold("\u2728 \u5E94\u7528\u6784\u5EFA\u6210\u529F!")) + "\n\n" + pc4.white("\u4EA7\u7269\u76EE\u5F55: ") + pc4.cyan("dist"),
1346
+ pc5.green(pc5.bold("\u2728 \u5E94\u7528\u6784\u5EFA\u6210\u529F!")) + "\n\n" + pc5.white("\u4EA7\u7269\u76EE\u5F55: ") + pc5.cyan("dist"),
1095
1347
  "Build Success"
1096
1348
  );
1097
1349
  }
1098
1350
  return;
1099
1351
  }
1100
1352
  let entry = ["src/index.ts"];
1101
- if (fs5.existsSync(path6.join(cwd, "src/index.tsx"))) {
1353
+ if (fs9.existsSync(path11.join(cwd, "src/index.tsx"))) {
1102
1354
  entry = ["src/index.tsx"];
1103
- } else if (!fs5.existsSync(path6.join(cwd, "src/index.ts"))) {
1355
+ } else if (!fs9.existsSync(path11.join(cwd, "src/index.ts"))) {
1104
1356
  logger.error("\u672A\u627E\u5230\u5165\u53E3\u6587\u4EF6\u3002\u671F\u671B src/index.ts \u6216 src/index.tsx");
1105
1357
  return;
1106
1358
  }
1107
1359
  const isPlugin = mode === "plugin";
1360
+ const userConfig = await ConfigManager.loadTsupConfig(cwd);
1361
+ if (Object.keys(userConfig).length > 0) {
1362
+ logger.info(`\u5DF2\u52A0\u8F7D\u672C\u5730\u6784\u5EFA\u914D\u7F6E`);
1363
+ }
1108
1364
  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);
1365
+ await fs9.remove(path11.join(cwd, "dist"));
1366
+ const tsbuildinfo = path11.join(cwd, "tsconfig.tsbuildinfo");
1367
+ if (fs9.existsSync(tsbuildinfo)) {
1368
+ await fs9.remove(tsbuildinfo);
1113
1369
  }
1114
1370
  }
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
1371
  if (isPlugin) {
1127
- const coreDeps = Sandbox.CORE_PACKAGES;
1128
- coreDeps.forEach((dep) => {
1129
- if (!external.includes(dep)) {
1130
- external.push(dep);
1131
- }
1132
- });
1133
1372
  }
1134
1373
  const commonConfig = {
1135
1374
  entry,
@@ -1138,16 +1377,32 @@ async function build(options) {
1138
1377
  sourcemap: false,
1139
1378
  target: "esnext",
1140
1379
  platform: isPlugin ? "browser" : "node",
1141
- external,
1142
- minify: !options.watch,
1143
- // 生产环境开启压缩混淆
1380
+ ...userConfig,
1381
+ // 合并用户配置
1382
+ external: [
1383
+ .../* @__PURE__ */ new Set([
1384
+ "react",
1385
+ "react-dom",
1386
+ "react/jsx-runtime",
1387
+ "react-is",
1388
+ "antd",
1389
+ "@ant-design/icons",
1390
+ "@ant-design/x",
1391
+ ...Object.keys(pkg.dependencies || {}),
1392
+ ...Object.keys(pkg.peerDependencies || {}),
1393
+ ...isPlugin ? Sandbox.CORE_PACKAGES : [],
1394
+ ...Array.isArray(userConfig.external) ? userConfig.external : []
1395
+ ])
1396
+ ],
1397
+ minify: userConfig.minify !== void 0 ? userConfig.minify : !options.watch,
1398
+ // 优先使用用户配置
1144
1399
  watch: options.watch,
1145
1400
  silent: true,
1146
- esbuildOptions(options2) {
1147
- options2.logOverride = {
1401
+ esbuildOptions(esbuildOpts) {
1402
+ esbuildOpts.logOverride = {
1148
1403
  "empty-import-meta": "silent"
1149
1404
  };
1150
- options2.external = options2.external || [];
1405
+ esbuildOpts.external = esbuildOpts.external || [];
1151
1406
  const forceExternal = [
1152
1407
  "react",
1153
1408
  "react-dom",
@@ -1157,10 +1412,13 @@ async function build(options) {
1157
1412
  ...Sandbox.CORE_PACKAGES
1158
1413
  ];
1159
1414
  forceExternal.forEach((dep) => {
1160
- if (!options2.external.includes(dep)) {
1161
- options2.external.push(dep);
1415
+ if (!esbuildOpts.external.includes(dep)) {
1416
+ esbuildOpts.external.push(dep);
1162
1417
  }
1163
1418
  });
1419
+ if (typeof userConfig.esbuildOptions === "function") {
1420
+ userConfig.esbuildOptions(esbuildOpts);
1421
+ }
1164
1422
  }
1165
1423
  };
1166
1424
  const buildSpinner = createSpinner("\u6B63\u5728\u7F16\u8BD1\u6E90\u7801...").start();
@@ -1181,25 +1439,7 @@ async function build(options) {
1181
1439
  define: {
1182
1440
  "import.meta.env": "process.env"
1183
1441
  },
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
- }
1442
+ esbuildOptions: commonConfig.esbuildOptions
1203
1443
  });
1204
1444
  } else {
1205
1445
  buildSpinner.text = "\u6B63\u5728\u7F16\u8BD1\u6E90\u7801...";
@@ -1215,15 +1455,15 @@ async function build(options) {
1215
1455
  if (!options.watch) {
1216
1456
  const dtsSpinner = createSpinner("\u6B63\u5728\u751F\u6210\u7C7B\u578B\u5B9A\u4E49...").start();
1217
1457
  try {
1218
- const localTsc = path6.join(cwd, "node_modules/.bin/tsc");
1219
- const sandboxTsc = path6.join(Sandbox.getVersionPath(version), "node_modules/.bin/tsc");
1458
+ const localTsc = path11.join(cwd, "node_modules/.bin/tsc");
1459
+ const sandboxTsc = path11.join(Sandbox.getVersionPath(version), "node_modules/.bin/tsc");
1220
1460
  let tscBin = "tsc";
1221
- if (fs5.existsSync(localTsc)) {
1461
+ if (fs9.existsSync(localTsc)) {
1222
1462
  tscBin = localTsc;
1223
- } else if (fs5.existsSync(sandboxTsc)) {
1463
+ } else if (fs9.existsSync(sandboxTsc)) {
1224
1464
  tscBin = sandboxTsc;
1225
1465
  } else {
1226
- if (fs5.existsSync(path6.join(cwd, "pnpm-lock.yaml"))) {
1466
+ if (fs9.existsSync(path11.join(cwd, "pnpm-lock.yaml"))) {
1227
1467
  await execa2("pnpm", ["exec", "tsc", "--build", "tsconfig.json"]);
1228
1468
  dtsSpinner.succeed("\u7C7B\u578B\u5B9A\u4E49\u751F\u6210\u5B8C\u6210");
1229
1469
  } else {
@@ -1239,7 +1479,7 @@ async function build(options) {
1239
1479
  dtsSpinner.warn("\u7C7B\u578B\u751F\u6210\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u9879\u76EE\u4E2D\u7684 tsconfig.json \u914D\u7F6E");
1240
1480
  }
1241
1481
  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"),
1482
+ 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
1483
  "Build Success"
1244
1484
  );
1245
1485
  }
@@ -1250,6 +1490,7 @@ var init_build = __esm({
1250
1490
  init_esm_shims();
1251
1491
  init_sandbox();
1252
1492
  init_corekit();
1493
+ init_config();
1253
1494
  init_utils();
1254
1495
  }
1255
1496
  });
@@ -1259,17 +1500,17 @@ var sync_exports = {};
1259
1500
  __export(sync_exports, {
1260
1501
  sync: () => sync
1261
1502
  });
1262
- import fs6 from "fs-extra";
1263
- import path7 from "path";
1264
- import pc5 from "picocolors";
1503
+ import fs10 from "fs-extra";
1504
+ import path12 from "path";
1505
+ import pc6 from "picocolors";
1265
1506
  async function sync(options = {}) {
1266
1507
  const cwd = options.cwd || process.cwd();
1267
- const pkgPath = path7.join(cwd, "package.json");
1268
- if (!fs6.existsSync(pkgPath)) {
1508
+ const pkgPath = path12.join(cwd, "package.json");
1509
+ if (!fs10.existsSync(pkgPath)) {
1269
1510
  logger.error("\u672A\u627E\u5230 package.json\u3002\u8BF7\u5728\u9879\u76EE\u6839\u76EE\u5F55\u4E0B\u8FD0\u884C\u3002");
1270
1511
  return;
1271
1512
  }
1272
- const pkg = await fs6.readJson(pkgPath);
1513
+ const pkg = await fs10.readJson(pkgPath);
1273
1514
  let version = options.version;
1274
1515
  if (!version) {
1275
1516
  version = await CoreKit.resolveVersion(cwd);
@@ -1284,8 +1525,8 @@ async function sync(options = {}) {
1284
1525
  await Sandbox.prepare(version, options.force);
1285
1526
  mainSpinner.text = "\u6B63\u5728\u751F\u6210\u865A\u62DF\u4E0A\u4E0B\u6587...";
1286
1527
  if (options.version) {
1287
- const versionFilePath = path7.join(cwd, ".chatbi-version");
1288
- await fs6.writeFile(versionFilePath, version, "utf-8");
1528
+ const versionFilePath = path12.join(cwd, ".chatbi-version");
1529
+ await fs10.writeFile(versionFilePath, version, "utf-8");
1289
1530
  }
1290
1531
  await Sandbox.injectContext(cwd, version);
1291
1532
  let modified = false;
@@ -1307,19 +1548,19 @@ async function sync(options = {}) {
1307
1548
  if (modified) {
1308
1549
  pkg.dependencies = deps;
1309
1550
  pkg.devDependencies = devDeps;
1310
- await fs6.writeJson(pkgPath, pkg, { spaces: 2 });
1551
+ await fs10.writeJson(pkgPath, pkg, { spaces: 2 });
1311
1552
  mainSpinner.succeed("\u5185\u6838\u540C\u6B65\u5B8C\u6210 (\u4F9D\u8D56\u5DF2\u4F18\u5316)");
1312
1553
  } else {
1313
1554
  mainSpinner.succeed("\u5185\u6838\u540C\u6B65\u5B8C\u6210");
1314
1555
  }
1315
1556
  if (!options.silent) {
1316
1557
  printBox(
1317
- `${pc5.green(pc5.bold("\u2728 \u5185\u6838\u540C\u6B65\u6210\u529F!"))}
1558
+ `${pc6.green(pc6.bold("\u2728 \u5185\u6838\u540C\u6B65\u6210\u529F!"))}
1318
1559
 
1319
- ${pc5.white("\u5F53\u524D\u7248\u672C: ")} ${pc5.cyan(version)}
1320
- ${pc5.white("\u6C99\u7BB1\u8DEF\u5F84: ")} ${pc5.gray(Sandbox.getVersionPath(version))}
1560
+ ${pc6.white("\u5F53\u524D\u7248\u672C: ")} ${pc6.cyan(version)}
1561
+ ${pc6.white("\u6C99\u7BB1\u8DEF\u5F84: ")} ${pc6.gray(Sandbox.getVersionPath(version))}
1321
1562
 
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`,
1563
+ ${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
1564
  "Sync Success"
1324
1565
  );
1325
1566
  }
@@ -1339,13 +1580,12 @@ var init_exports = {};
1339
1580
  __export(init_exports, {
1340
1581
  init: () => init
1341
1582
  });
1342
- import fs7 from "fs-extra";
1343
- import path8 from "path";
1344
- import pc6 from "picocolors";
1345
- import Handlebars2 from "handlebars";
1583
+ import fs11 from "fs-extra";
1584
+ import path13 from "path";
1585
+ import pc7 from "picocolors";
1346
1586
  import prompts2 from "prompts";
1347
1587
  async function init(options) {
1348
- let { name, template, pluginType = "business", theme = "standard", projectType, includeApp, includePlugin } = options;
1588
+ let { name, pluginType = "business", theme = DEFAULT_CONFIG.THEME, projectType, includeApp, includePlugin, cwd } = options;
1349
1589
  const response = await prompts2([
1350
1590
  // ... (prompts remain the same)
1351
1591
  {
@@ -1415,32 +1655,33 @@ async function init(options) {
1415
1655
  const finalIncludePlugin = includePlugin !== void 0 ? includePlugin : response.includePlugin !== void 0 ? response.includePlugin : true;
1416
1656
  const isAppIncluded = finalIncludeApp;
1417
1657
  const isPluginIncluded = finalIncludePlugin;
1418
- const rootDir = process.cwd();
1419
- const targetDir = path8.resolve(rootDir, name);
1420
- if (fs7.existsSync(targetDir)) {
1658
+ const rootDir = cwd || process.cwd();
1659
+ const targetDir = path13.resolve(rootDir, name);
1660
+ if (fs11.existsSync(targetDir)) {
1421
1661
  logger.error(`\u76EE\u5F55 ${name} \u5DF2\u5B58\u5728\u3002`);
1422
1662
  return;
1423
1663
  }
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"}`));
1664
+ logger.info(`\u6B63\u5728\u521D\u59CB\u5316\u9879\u76EE: ${pc7.bold(name)}...`);
1665
+ logger.info(pc7.gray(`\u7C7B\u578B: ${projectType} | \u4E3B\u9898: ${theme || "N/A"}`));
1426
1666
  console.log("");
1427
1667
  try {
1428
1668
  const myCliRoot = await getCliRoot();
1429
- const cliPkg = await fs7.readJson(path8.join(myCliRoot, "package.json"));
1669
+ const cliPkg = await fs11.readJson(path13.join(myCliRoot, "package.json"));
1430
1670
  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();
1671
+ const config = { coreSource: "npm" };
1672
+ const kernelVersionFile = path13.join(myCliRoot, SANDBOX_CONFIG.LOCK_FILE);
1673
+ if (fs11.existsSync(kernelVersionFile)) {
1674
+ config.coreVersion = (await fs11.readFile(kernelVersionFile, "utf-8")).trim();
1435
1675
  }
1676
+ const kernelVersion = await ConfigManager.resolveCoreDependency(config);
1436
1677
  const renderTemplate = async (templateName, destDir, extraData = {}) => {
1437
- const srcDir = path8.join(myCliRoot, "templates", templateName);
1438
- if (!fs7.existsSync(srcDir)) {
1678
+ const srcDir = path13.join(myCliRoot, "templates", templateName);
1679
+ if (!fs11.existsSync(srcDir)) {
1439
1680
  throw new Error(`\u627E\u4E0D\u5230\u6A21\u677F: ${templateName}\uFF0C\u8BF7\u68C0\u67E5\u8DEF\u5F84 ${srcDir} \u662F\u5426\u6B63\u786E\u3002`);
1440
1681
  }
1441
- await fs7.ensureDir(destDir);
1682
+ await fs11.ensureDir(destDir);
1442
1683
  const templateData = {
1443
- name: path8.basename(destDir),
1684
+ name: path13.basename(destDir),
1444
1685
  projectName: name,
1445
1686
  // Original project name from CLI argument
1446
1687
  projectTitle: name.split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" "),
@@ -1457,45 +1698,10 @@ async function init(options) {
1457
1698
  isApp: projectType === "app" || isAppIncluded,
1458
1699
  // Zero Dependency Mode: No explicit core path needed in package.json
1459
1700
  // tsconfig extends the virtual paths config
1460
- tsconfigPath: projectType === "monorepo" ? "../../.chatbi/tsconfig.json" : "./.chatbi/tsconfig.json",
1701
+ tsconfigPath: projectType === "monorepo" ? `../../${SANDBOX_CONFIG.DIRS.CACHE}/tsconfig.json` : `./${SANDBOX_CONFIG.DIRS.CACHE}/tsconfig.json`,
1461
1702
  ...extraData
1462
1703
  };
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);
1704
+ await SandboxRenderer.renderDirectory(srcDir, destDir, templateData);
1499
1705
  };
1500
1706
  const spinner = createSpinner("\u6B63\u5728\u751F\u6210\u9879\u76EE\u7ED3\u6784...").start();
1501
1707
  if (projectType === "monorepo") {
@@ -1503,12 +1709,12 @@ async function init(options) {
1503
1709
  await renderTemplate("monorepo", targetDir, { name });
1504
1710
  if (isAppIncluded) {
1505
1711
  spinner.text = "\u6B63\u5728\u751F\u6210 Host App...";
1506
- const appDir = path8.join(targetDir, "apps", "main");
1712
+ const appDir = path13.join(targetDir, "apps", "main");
1507
1713
  await renderTemplate("app", appDir, { name: "@chatbi-v/main" });
1508
1714
  }
1509
1715
  if (isPluginIncluded) {
1510
1716
  spinner.text = "\u6B63\u5728\u751F\u6210 Demo Plugin...";
1511
- const pluginDir = path8.join(targetDir, "plugins", "plugin-demo");
1717
+ const pluginDir = path13.join(targetDir, "plugins", "plugin-demo");
1512
1718
  await renderTemplate("plugin", pluginDir, {
1513
1719
  name: "@chatbi-v/plugin-demo",
1514
1720
  pluginId: "demo",
@@ -1550,15 +1756,15 @@ async function init(options) {
1550
1756
  const cliConfig = {
1551
1757
  coreVersion: kernelVersion
1552
1758
  };
1553
- await fs7.writeJson(path8.join(targetDir, "chatbi.config.json"), cliConfig, { spaces: 2 });
1759
+ await fs11.writeJson(path13.join(targetDir, "chatbi.config.json"), cliConfig, { spaces: 2 });
1554
1760
  configSpinner.succeed("\u9879\u76EE\u914D\u7F6E\u751F\u6210\u5B8C\u6210");
1555
1761
  printBox(
1556
- `${pc6.green(pc6.bold("\u2728 \u9879\u76EE\u5DF2\u6210\u529F\u521B\u5EFA!"))}
1762
+ `${pc7.green(pc7.bold("\u2728 \u9879\u76EE\u5DF2\u6210\u529F\u521B\u5EFA!"))}
1557
1763
 
1558
- ${pc6.white("\u63A5\u4E0B\u6765\u4F60\u53EF\u4EE5:")}
1559
- ${pc6.cyan(` cd ${name}`)}
1560
- ${pc6.cyan(" pnpm install")}
1561
- ${pc6.cyan(" pnpm dev")}`,
1764
+ ${pc7.white("\u63A5\u4E0B\u6765\u4F60\u53EF\u4EE5:")}
1765
+ ${pc7.cyan(` cd ${name}`)}
1766
+ ${pc7.cyan(" pnpm install")}
1767
+ ${pc7.cyan(" pnpm dev")}`,
1562
1768
  "Success"
1563
1769
  );
1564
1770
  } catch (error) {
@@ -1572,6 +1778,9 @@ var init_init = __esm({
1572
1778
  init_esm_shims();
1573
1779
  init_utils();
1574
1780
  init_sync();
1781
+ init_constants();
1782
+ init_config();
1783
+ init_SandboxRenderer();
1575
1784
  }
1576
1785
  });
1577
1786
 
@@ -1580,34 +1789,34 @@ var fetch_exports = {};
1580
1789
  __export(fetch_exports, {
1581
1790
  fetch: () => fetch
1582
1791
  });
1583
- import path13 from "path";
1584
- import pc12 from "picocolors";
1792
+ import path18 from "path";
1793
+ import pc13 from "picocolors";
1585
1794
  import { execa as execa4 } from "execa";
1586
1795
  async function fetch(version, options = {}) {
1587
- const fetchSpinner = createSpinner(`\u6B63\u5728\u83B7\u53D6\u5185\u6838\u7248\u672C ${pc12.cyan(version)}...`).start();
1796
+ const fetchSpinner = createSpinner(`\u6B63\u5728\u83B7\u53D6\u5185\u6838\u7248\u672C ${pc13.cyan(version)}...`).start();
1588
1797
  const versionPath = await Sandbox.prepare(version, true);
1589
- fetchSpinner.succeed(`\u5185\u6838\u7248\u672C ${pc12.cyan(version)} \u83B7\u53D6\u6210\u529F`);
1798
+ fetchSpinner.succeed(`\u5185\u6838\u7248\u672C ${pc13.cyan(version)} \u83B7\u53D6\u6210\u529F`);
1590
1799
  if (options.pack) {
1591
1800
  const packSpinner = createSpinner("\u6B63\u5728\u6253\u5305\u79BB\u7EBF\u8D44\u6E90...").start();
1592
1801
  const tgzName = `chatbi-core-${version}.tgz`;
1593
- const tgzPath = path13.resolve(process.cwd(), tgzName);
1802
+ const tgzPath = path18.resolve(process.cwd(), tgzName);
1594
1803
  await execa4("tar", [
1595
1804
  "-czf",
1596
1805
  tgzPath,
1597
1806
  "-C",
1598
- path13.dirname(versionPath),
1599
- path13.basename(versionPath)
1807
+ path18.dirname(versionPath),
1808
+ path18.basename(versionPath)
1600
1809
  ]);
1601
1810
  packSpinner.succeed("\u79BB\u7EBF\u8D44\u6E90\u6253\u5305\u5B8C\u6210");
1602
1811
  printBox(
1603
- `${pc12.green(pc12.bold("\u2728 \u79BB\u7EBF\u5305\u5DF2\u751F\u6210!"))}
1812
+ `${pc13.green(pc13.bold("\u2728 \u79BB\u7EBF\u5305\u5DF2\u751F\u6210!"))}
1604
1813
 
1605
- ${pc12.white("\u6587\u4EF6\u8DEF\u5F84: ")} ${pc12.cyan(tgzPath)}
1606
- ${pc12.white("\u5B89\u88C5\u547D\u4EE4: ")} ${pc12.gray(`chatbi install ${tgzName}`)}`,
1814
+ ${pc13.white("\u6587\u4EF6\u8DEF\u5F84: ")} ${pc13.cyan(tgzPath)}
1815
+ ${pc13.white("\u5B89\u88C5\u547D\u4EE4: ")} ${pc13.gray(`chatbi install ${tgzName}`)}`,
1607
1816
  "Pack Success"
1608
1817
  );
1609
1818
  } else {
1610
- logger.success(`\u5185\u6838\u7248\u672C ${pc12.cyan(version)} \u5DF2\u51C6\u5907\u5C31\u7EEA\u3002`);
1819
+ logger.success(`\u5185\u6838\u7248\u672C ${pc13.cyan(version)} \u5DF2\u51C6\u5907\u5C31\u7EEA\u3002`);
1611
1820
  }
1612
1821
  }
1613
1822
  var init_fetch = __esm({
@@ -1624,24 +1833,24 @@ var bench_exports = {};
1624
1833
  __export(bench_exports, {
1625
1834
  bench: () => bench
1626
1835
  });
1627
- import pc14 from "picocolors";
1628
- import fs13 from "fs-extra";
1629
- import path15 from "path";
1836
+ import pc15 from "picocolors";
1837
+ import fs17 from "fs-extra";
1838
+ import path20 from "path";
1630
1839
  import os2 from "os";
1631
1840
  async function bench() {
1632
1841
  logger.info("\u6B63\u5728\u542F\u52A8 CLI \u6027\u80FD\u57FA\u51C6\u6D4B\u8BD5...");
1633
1842
  const results = [];
1634
- const tmpDir = path15.join(os2.tmpdir(), `chatbi-bench-${Date.now()}`);
1635
- await fs13.ensureDir(tmpDir);
1843
+ const tmpDir = path20.join(os2.tmpdir(), `chatbi-bench-${Date.now()}`);
1844
+ await fs17.ensureDir(tmpDir);
1636
1845
  try {
1637
1846
  const initSpinner = createSpinner("\u6B63\u5728\u6D4B\u8BD5: \u521D\u59CB\u5316\u63D2\u4EF6\u9879\u76EE (init)...").start();
1638
1847
  const startInit = Date.now();
1639
1848
  const { init: init2 } = await Promise.resolve().then(() => (init_init(), init_exports));
1640
- const testProjDir = path15.join(tmpDir, "bench-proj");
1849
+ const testProjDir = path20.join(tmpDir, "bench-proj");
1641
1850
  await init2({ name: "bench-proj", projectType: "plugin", cwd: tmpDir });
1642
1851
  const endInit = Date.now();
1643
1852
  const initTime = (endInit - startInit) / 1e3;
1644
- initSpinner.succeed(`\u521D\u59CB\u5316\u5B8C\u6210: ${pc14.cyan(initTime.toFixed(2) + "s")}`);
1853
+ initSpinner.succeed(`\u521D\u59CB\u5316\u5B8C\u6210: ${pc15.cyan(initTime.toFixed(2) + "s")}`);
1645
1854
  results.push({
1646
1855
  scene: "\u521D\u59CB\u5316\u63D2\u4EF6\u9879\u76EE (init)",
1647
1856
  target: "\u2264 5.0 s",
@@ -1653,7 +1862,7 @@ async function bench() {
1653
1862
  await sync2({ cwd: testProjDir });
1654
1863
  const endSync = Date.now();
1655
1864
  const syncTime = (endSync - startSync) / 1e3;
1656
- syncSpinner.succeed(`\u540C\u6B65\u5B8C\u6210: ${pc14.cyan(syncTime.toFixed(2) + "s")}`);
1865
+ syncSpinner.succeed(`\u540C\u6B65\u5B8C\u6210: ${pc15.cyan(syncTime.toFixed(2) + "s")}`);
1657
1866
  results.push({
1658
1867
  scene: "\u6C99\u7BB1\u73AF\u5883\u540C\u6B65 (sync)",
1659
1868
  target: "\u2264 2.0 s",
@@ -1668,20 +1877,20 @@ async function bench() {
1668
1877
  process.chdir(originalCwd);
1669
1878
  const endBuild = Date.now();
1670
1879
  const buildTime = (endBuild - startBuild) / 1e3;
1671
- buildSpinner.succeed(`\u6784\u5EFA\u5B8C\u6210: ${pc14.cyan(buildTime.toFixed(2) + "s")}`);
1880
+ buildSpinner.succeed(`\u6784\u5EFA\u5B8C\u6210: ${pc15.cyan(buildTime.toFixed(2) + "s")}`);
1672
1881
  results.push({
1673
1882
  scene: "\u63D2\u4EF6\u9879\u76EE\u6784\u5EFA (build)",
1674
1883
  target: "\u2264 10.0 s",
1675
1884
  actual: `${buildTime.toFixed(2)} s`
1676
1885
  });
1677
1886
  printBox(
1678
- `${pc14.green(pc14.bold("\u2728 \u57FA\u51C6\u6D4B\u8BD5\u5B8C\u6210!"))}
1887
+ `${pc15.green(pc15.bold("\u2728 \u57FA\u51C6\u6D4B\u8BD5\u5B8C\u6210!"))}
1679
1888
 
1680
- ` + results.map((r) => `${pc14.white(r.scene.padEnd(20))}: ${pc14.cyan(r.actual)} (\u76EE\u6807 ${r.target})`).join("\n"),
1889
+ ` + results.map((r) => `${pc15.white(r.scene.padEnd(20))}: ${pc15.cyan(r.actual)} (\u76EE\u6807 ${r.target})`).join("\n"),
1681
1890
  "Benchmark Results"
1682
1891
  );
1683
1892
  } finally {
1684
- await fs13.remove(tmpDir);
1893
+ await fs17.remove(tmpDir);
1685
1894
  }
1686
1895
  }
1687
1896
  var init_bench = __esm({
@@ -1696,7 +1905,7 @@ var init_bench = __esm({
1696
1905
  init_esm_shims();
1697
1906
  init_build();
1698
1907
  import cac from "cac";
1699
- import pc15 from "picocolors";
1908
+ import pc16 from "picocolors";
1700
1909
  import figlet from "figlet";
1701
1910
  import gradient from "gradient-string";
1702
1911
  import boxen2 from "boxen";
@@ -1716,20 +1925,26 @@ init_init();
1716
1925
  init_esm_shims();
1717
1926
  init_utils();
1718
1927
  init_config();
1719
- import fs8 from "fs-extra";
1720
- import path9 from "path";
1721
- import pc7 from "picocolors";
1928
+ init_constants();
1929
+ init_SandboxRenderer();
1930
+ import fs12 from "fs-extra";
1931
+ import path14 from "path";
1932
+ import pc8 from "picocolors";
1722
1933
  import prompts3 from "prompts";
1723
- import Handlebars3 from "handlebars";
1934
+ import { createRequire as createRequire3 } from "module";
1935
+ import { fileURLToPath as fileURLToPath3 } from "url";
1936
+ var _require2 = createRequire3(import.meta.url);
1937
+ var _filename2 = fileURLToPath3(import.meta.url);
1938
+ var _dirname2 = path14.dirname(_filename2);
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.6",
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) {