vite-plugin-aipanel 1.2.19 → 1.2.21

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.
@@ -0,0 +1,50 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+ var framework_exports = {};
19
+ __export(framework_exports, {
20
+ detectViteFramework: () => detectViteFramework
21
+ });
22
+ module.exports = __toCommonJS(framework_exports);
23
+ var import_core = require("@aipanel/core");
24
+ const VUE_PLUGIN_NAME_PREFIX = "vite:vue";
25
+ const REACT_PLUGIN_NAME_PREFIX = "vite:react";
26
+ function collectPluginNames(plugins, names = []) {
27
+ for (const plugin of plugins) {
28
+ if (Array.isArray(plugin)) {
29
+ collectPluginNames(plugin, names);
30
+ } else if (plugin && typeof plugin === "object") {
31
+ const name = plugin.name;
32
+ if (typeof name === "string") names.push(name);
33
+ }
34
+ }
35
+ return names;
36
+ }
37
+ function detectViteFramework(config) {
38
+ const names = collectPluginNames(config.plugins ?? []);
39
+ if (names.some((name) => name.startsWith(VUE_PLUGIN_NAME_PREFIX))) {
40
+ return import_core.INSPECTOR_ADAPTER_IDS.vue;
41
+ }
42
+ if (names.some((name) => name.startsWith(REACT_PLUGIN_NAME_PREFIX))) {
43
+ return import_core.INSPECTOR_ADAPTER_IDS.react;
44
+ }
45
+ return null;
46
+ }
47
+ // Annotate the CommonJS export names for ESM import in node:
48
+ 0 && (module.exports = {
49
+ detectViteFramework
50
+ });
@@ -0,0 +1,14 @@
1
+ /**
2
+ * 构建期框架检测:从已解析的 Vite 插件列表推断项目框架。
3
+ * 依据官方插件命名约定(@vitejs/plugin-vue → "vite:vue*",plugin-react / plugin-react-swc →
4
+ * "vite:react*"),用于门控 Vue DevTools 桥注入与各框架 inspector 集成;
5
+ * 标识复用 INSPECTOR_ADAPTER_IDS,与运行时适配器注册表对齐。
6
+ */
7
+ import type { ResolvedConfig } from "vite";
8
+ import { INSPECTOR_ADAPTER_IDS } from "@aipanel/core";
9
+ export type ViteFramework = (typeof INSPECTOR_ADAPTER_IDS)[keyof typeof INSPECTOR_ADAPTER_IDS];
10
+ /**
11
+ * 检测项目框架:vue / react 同时存在时以 vue 优先(与运行时适配器解析顺序一致);
12
+ * 无法识别(solid、svelte、纯原生等)返回 null,调用方按保守策略处理。
13
+ */
14
+ export declare function detectViteFramework(config: ResolvedConfig): ViteFramework | null;
@@ -60,11 +60,11 @@ class AIPanelService {
60
60
  this.provider = provider;
61
61
  }
62
62
  sendTaskUpdate(task, data) {
63
- this.currentTask = { task, ...data };
63
+ this.currentTask = { ...data, task };
64
64
  this.sseClients.forEach((client) => {
65
65
  try {
66
66
  client.write(
67
- `data: ${JSON.stringify({ type: import_core.SSE_EVENT_TYPES.TASK_UPDATE, task, ...data })}
67
+ `data: ${JSON.stringify({ type: import_core.SSE_EVENT_TYPES.TASK_UPDATE, ...data, task })}
68
68
 
69
69
  `
70
70
  );
@@ -239,8 +239,8 @@ class AIPanelService {
239
239
  });
240
240
  if (warmupFailed) {
241
241
  this.sendTaskUpdate("chrome_mcp_failed", {
242
- errorType: this.chromeMcpWarmupErrorType,
243
- errorMessage: this.chromeMcpWarmupErrorMessage
242
+ errorType: this.chromeMcpWarmupErrorType ?? void 0,
243
+ errorMessage: this.chromeMcpWarmupErrorMessage ?? void 0
244
244
  });
245
245
  } else {
246
246
  this.sendTaskUpdate("ready");
@@ -1,6 +1,6 @@
1
1
  import type { ResultPromise } from "execa";
2
2
  import type http from "http";
3
- import type { PluginOptions, ProviderEvent, ServiceStartupTask, WebProvider } from "@aipanel/core";
3
+ import type { PluginOptions, ProviderEvent, ServiceTaskState, WebProvider } from "@aipanel/core";
4
4
  import { ChromeMcpWarmupErrorType } from "@aipanel/core";
5
5
  import type { McpProxy } from "./mcp-proxy";
6
6
  export declare class AIPanelService {
@@ -17,10 +17,7 @@ export declare class AIPanelService {
17
17
  chromeMcpWarmupFailed: boolean;
18
18
  chromeMcpWarmupErrorType: ChromeMcpWarmupErrorType | null;
19
19
  chromeMcpWarmupErrorMessage: string | null;
20
- currentTask: {
21
- task: ServiceStartupTask;
22
- data?: Record<string, unknown>;
23
- } | null;
20
+ currentTask: ServiceTaskState | null;
24
21
  workspaceRoot: string | null;
25
22
  private mcp;
26
23
  private provider;
@@ -1,4 +1,4 @@
1
- import type { ChatSession, PageContext, ProviderCapabilities, ProviderEvent, ServiceStartupTask } from "@aipanel/core";
1
+ import type { ChatSession, PageContext, ProviderCapabilities, ProviderEvent, ServiceTaskState } from "@aipanel/core";
2
2
  import type http from "http";
3
3
  export interface EndpointContext {
4
4
  get webUrl(): string | null;
@@ -12,10 +12,7 @@ export interface EndpointContext {
12
12
  /** 清除选中元素(活跃 Tab) */
13
13
  clearSelectedElements(): void;
14
14
  get isServiceStarted(): boolean;
15
- get currentTask(): {
16
- task: ServiceStartupTask;
17
- data?: Record<string, unknown>;
18
- } | null;
15
+ get currentTask(): ServiceTaskState | null;
19
16
  get actualProxyPort(): number;
20
17
  get actualWebPort(): number;
21
18
  get serviceInstanceId(): string;
package/lib/index.cjs CHANGED
@@ -32,16 +32,17 @@ __export(lib_exports, {
32
32
  module.exports = __toCommonJS(lib_exports);
33
33
  var import_crypto = __toESM(require("crypto"));
34
34
  var import_fs = __toESM(require("fs"));
35
- var import_vite = __toESM(require("unplugin-vue-inspector/vite"));
36
35
  var import_core = require("@aipanel/core");
37
36
  var import_node = require("@aipanel/core/node");
38
37
  var import_endpoints = require("./endpoints/index.cjs");
39
38
  var import_injector = require("./core/injector.cjs");
39
+ var import_inspectors = require("./inspectors/index.cjs");
40
40
  var import_provider_loader = require("./core/provider-loader.cjs");
41
41
  var import_service = require("./core/service.cjs");
42
42
  var import_mcp_proxy = require("./core/mcp-proxy.cjs");
43
43
  var import_mcp_tools = require("./core/mcp-tools.cjs");
44
44
  var import_paths = require("./utils/paths.cjs");
45
+ var import_framework = require("./core/framework.cjs");
45
46
  var import_node2 = require("@aipanel/core/node");
46
47
  const DEVTOOLS_BRIDGE_IMPORTEE = "virtual:aipanel-vue-devtools-bridge";
47
48
  const DEVTOOLS_BRIDGE_QUERY = "aipanel_vue_devtools_bridge";
@@ -89,13 +90,7 @@ const SILENT_CONTEXT_SCRIPT = `<script>
89
90
  </script>`;
90
91
  function aipanelPlugin(options = {}) {
91
92
  const plugins = [];
92
- plugins.push(
93
- ...(0, import_vite.default)({
94
- enabled: false,
95
- toggleButtonVisibility: "never",
96
- toggleComboKey: false
97
- })
98
- );
93
+ plugins.push(...(0, import_inspectors.createInspectorPlugins)());
99
94
  plugins.push(createAIPanelPlugin(options));
100
95
  return plugins;
101
96
  }
@@ -111,6 +106,7 @@ function createAIPanelPlugin(options = {}) {
111
106
  let actualProxyPort = config.proxyPort ?? import_core.DEFAULT_PROXY_PORT;
112
107
  let projectRoot = "";
113
108
  let vueDevtoolsApiUrl = "";
109
+ let isVueProject = true;
114
110
  let viteServerHost = "localhost";
115
111
  const pageContext = { url: "", title: "" };
116
112
  const DEFAULT_TAB = "default";
@@ -157,6 +153,9 @@ function createAIPanelPlugin(options = {}) {
157
153
  if (!config.enabled) return false;
158
154
  return env.command === "serve" && process.env.NODE_ENV !== "test";
159
155
  },
156
+ configResolved(resolvedConfig) {
157
+ isVueProject = (0, import_framework.detectViteFramework)(resolvedConfig) !== import_core.INSPECTOR_ADAPTER_IDS.react;
158
+ },
160
159
  async configureServer(server) {
161
160
  const timer = log.timer("configureServer");
162
161
  projectRoot = server.config.root;
@@ -249,7 +248,7 @@ function createAIPanelPlugin(options = {}) {
249
248
  });
250
249
  service.currentTask = {
251
250
  task: "provider_not_installed",
252
- data: { error: e instanceof Error ? e.message : String(e) }
251
+ errorMessage: e instanceof Error ? e.message : String(e)
253
252
  };
254
253
  timer.end("\u274C Provider \u52A0\u8F7D\u5931\u8D25");
255
254
  return;
@@ -309,7 +308,8 @@ function createAIPanelPlugin(options = {}) {
309
308
  logsApiUrl,
310
309
  viteOrigin,
311
310
  mcpProxy,
312
- vueDevtoolsApiUrl
311
+ // React 项目不下发:provider 侧不注册 vue-devtools_* 工具,避免模型调用必失败的工具
312
+ isVueProject ? vueDevtoolsApiUrl : void 0
313
313
  );
314
314
  } catch (e) {
315
315
  log.error("Failed to start services", { error: e });
@@ -345,7 +345,7 @@ function createAIPanelPlugin(options = {}) {
345
345
  },
346
346
  transformIndexHtml(html) {
347
347
  const timer = log.timer("transformIndexHtml");
348
- const tags = [
348
+ const tags = isVueProject ? [
349
349
  {
350
350
  tag: "script",
351
351
  injectTo: "head-prepend",
@@ -354,7 +354,7 @@ function createAIPanelPlugin(options = {}) {
354
354
  src: `/@id/${DEVTOOLS_BRIDGE_IMPORTEE}`
355
355
  }
356
356
  }
357
- ];
357
+ ] : [];
358
358
  const titleInject = `<script>
359
359
  (function () {
360
360
  var KEY = "${import_core.SESSION_ID_KEY}";
@@ -364,7 +364,7 @@ function createAIPanelPlugin(options = {}) {
364
364
  })();
365
365
  </script>`;
366
366
  if (config.mcpOnly) {
367
- timer.end("\u2713 mcp-only (skip widget bubble, keep vue-devtools bridge + context report)");
367
+ timer.end("\u2713 mcp-only (skip widget bubble, keep context report)");
368
368
  return {
369
369
  html: html.replace("</body>", `${titleInject}
370
370
  ${SILENT_CONTEXT_SCRIPT}</body>`),
@@ -0,0 +1,35 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+ var inspectors_exports = {};
19
+ __export(inspectors_exports, {
20
+ createInspectorPlugins: () => createInspectorPlugins
21
+ });
22
+ module.exports = __toCommonJS(inspectors_exports);
23
+ var import_react = require("./react.cjs");
24
+ var import_vue = require("./vue.cjs");
25
+ const integrations = [
26
+ import_vue.vueInspectorIntegration,
27
+ import_react.reactInspectorIntegration
28
+ ];
29
+ function createInspectorPlugins() {
30
+ return integrations.flatMap((integration) => integration.plugins());
31
+ }
32
+ // Annotate the CommonJS export names for ESM import in node:
33
+ 0 && (module.exports = {
34
+ createInspectorPlugins
35
+ });
@@ -0,0 +1,3 @@
1
+ import type { Plugin } from "vite";
2
+ /** 汇总各框架集成需要注入的构建期插件 */
3
+ export declare function createInspectorPlugins(): Plugin[];
@@ -0,0 +1,62 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+ var react_exports = {};
19
+ __export(react_exports, {
20
+ reactInspectorIntegration: () => reactInspectorIntegration
21
+ });
22
+ module.exports = __toCommonJS(react_exports);
23
+ var import_core = require("@code-inspector/core");
24
+ var import_core2 = require("@aipanel/core");
25
+ var import_framework = require("../core/framework.cjs");
26
+ const JSX_FILE_PATTERN = /\.(jsx|tsx)$/;
27
+ const reactInspectorIntegration = {
28
+ id: import_core2.INSPECTOR_ADAPTER_IDS.react,
29
+ plugins: () => {
30
+ let vueProject = false;
31
+ return [
32
+ {
33
+ name: "aipanel:react-inspector",
34
+ enforce: "pre",
35
+ apply: "serve",
36
+ configResolved(config) {
37
+ vueProject = (0, import_framework.detectViteFramework)(config) === import_core2.INSPECTOR_ADAPTER_IDS.vue;
38
+ },
39
+ async transform(code, id) {
40
+ if (vueProject) return null;
41
+ const [filePath] = id.split("?");
42
+ if (!filePath || !JSX_FILE_PATTERN.test(filePath) || filePath.includes("node_modules") || filePath.startsWith("\0")) {
43
+ return null;
44
+ }
45
+ if (!code.includes("<")) return null;
46
+ const result = await (0, import_core.transformCode)({
47
+ content: code,
48
+ filePath,
49
+ fileType: "jsx",
50
+ escapeTags: import_core.CodeInspectorEscapeTags,
51
+ pathType: "relative"
52
+ });
53
+ return result === code ? null : { code: result };
54
+ }
55
+ }
56
+ ];
57
+ }
58
+ };
59
+ // Annotate the CommonJS export names for ESM import in node:
60
+ 0 && (module.exports = {
61
+ reactInspectorIntegration
62
+ });
@@ -0,0 +1,12 @@
1
+ import type { InspectorIntegration } from "./types";
2
+ /**
3
+ * React 集成:复用 code-inspector-plugin 的官方 transformCode API,在 dev 阶段为
4
+ * 项目内 jsx/tsx 注入 data-insp-path 源码坐标标记(pathType: relative 以 git 仓库根
5
+ * 为基准的相对路径,非 git 仓库时由其内部回退绝对路径)。
6
+ *
7
+ * enforce: pre 保证先于 JSX 擦除执行(@vitejs/plugin-react 与 plugin-react-swc 均适用);
8
+ * 仅 serve 生效,生产构建零影响。文件头部 code-inspector-disable/ignore 注释可整文件跳过。
9
+ * Vue 项目(含 vue-jsx)的 jsx/tsx 是 Vue JSX,跳过注入;未识别框架(纯 esbuild JSX 的
10
+ * React 等)保守放行。
11
+ */
12
+ export declare const reactInspectorIntegration: InspectorIntegration;
@@ -0,0 +1,15 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __copyProps = (to, from, except, desc) => {
6
+ if (from && typeof from === "object" || typeof from === "function") {
7
+ for (let key of __getOwnPropNames(from))
8
+ if (!__hasOwnProp.call(to, key) && key !== except)
9
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
10
+ }
11
+ return to;
12
+ };
13
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
14
+ var types_exports = {};
15
+ module.exports = __toCommonJS(types_exports);
@@ -0,0 +1,11 @@
1
+ import type { Plugin } from "vite";
2
+ import type { InspectorAdapterId } from "@aipanel/core";
3
+ /**
4
+ * 框架侧 Inspector 集成:构建期注入「点击元素 → 打开源码」运行时。
5
+ * 与客户端 @aipanel/core 的适配器按同一 id 对齐(INSPECTOR_ADAPTER_IDS)。
6
+ */
7
+ export interface InspectorIntegration {
8
+ readonly id: InspectorAdapterId;
9
+ /** 该框架需要注入的构建期插件 */
10
+ plugins(): Plugin[];
11
+ }
@@ -0,0 +1,46 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+ var vue_exports = {};
29
+ __export(vue_exports, {
30
+ vueInspectorIntegration: () => vueInspectorIntegration
31
+ });
32
+ module.exports = __toCommonJS(vue_exports);
33
+ var import_vite = __toESM(require("unplugin-vue-inspector/vite"));
34
+ var import_core = require("@aipanel/core");
35
+ const vueInspectorIntegration = {
36
+ id: import_core.INSPECTOR_ADAPTER_IDS.vue,
37
+ plugins: () => (0, import_vite.default)({
38
+ enabled: false,
39
+ toggleButtonVisibility: "never",
40
+ toggleComboKey: false
41
+ })
42
+ };
43
+ // Annotate the CommonJS export names for ESM import in node:
44
+ 0 && (module.exports = {
45
+ vueInspectorIntegration
46
+ });
@@ -0,0 +1,7 @@
1
+ import type { InspectorIntegration } from "./types";
2
+ /**
3
+ * Vue 集成:注入 unplugin-vue-inspector 运行时。
4
+ * 常驻但默认不启用(enabled: false),由挂件进入选择模式时开启;
5
+ * 同时隐藏其自带的切换按钮与快捷键,避免与挂件交互冲突。
6
+ */
7
+ export declare const vueInspectorIntegration: InspectorIntegration;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-aipanel",
3
- "version": "1.2.19",
3
+ "version": "1.2.21",
4
4
  "description": "Embed OpenCode Web UI in your Vite dev server for real-time code modification and preview",
5
5
  "type": "module",
6
6
  "main": "lib/index.cjs",
@@ -35,19 +35,20 @@
35
35
  "author": "",
36
36
  "license": "MIT",
37
37
  "dependencies": {
38
- "picomatch": "^4.0.2",
38
+ "@code-inspector/core": "^2.0.9",
39
39
  "@vue/devtools-kit": "^8.0.0",
40
40
  "@vue/language-server": "^3.3.7",
41
41
  "chrome-devtools-mcp": "^1.9.0",
42
42
  "execa": "^9.6.1",
43
+ "picomatch": "^4.0.2",
43
44
  "typescript-language-server": "^5.3.0",
44
45
  "unplugin-vue-inspector": "^3.0.0",
45
- "@aipanel/provider-opencode": "1.2.19",
46
- "@aipanel/core": "1.2.19"
46
+ "@aipanel/core": "1.2.21",
47
+ "@aipanel/provider-opencode": "1.2.21"
47
48
  },
48
49
  "peerDependencies": {
49
50
  "vite": ">=5.0.0",
50
- "@aipanel/provider-deepseek": "1.2.19"
51
+ "@aipanel/provider-deepseek": "1.2.21"
51
52
  },
52
53
  "peerDependenciesMeta": {
53
54
  "@aipanel/provider-deepseek": {
@@ -57,11 +58,12 @@
57
58
  "devDependencies": {
58
59
  "@types/picomatch": "^4.0.0",
59
60
  "sharp": "^0.34.5",
60
- "@aipanel/client": "1.2.19"
61
+ "@aipanel/client": "1.2.21"
61
62
  },
62
63
  "scripts": {
63
64
  "build": "node scripts/sync-official-meta.mjs && pagoda-cli build && vite build -c vite.client.config.ts",
64
65
  "typecheck": "tsc -p tsconfig.declaration.json --noEmit",
65
- "test": "vitest run"
66
+ "test": "vitest run",
67
+ "test:coverage": "vitest run --coverage"
66
68
  }
67
69
  }