@zhin.js/cli 1.0.6 → 1.0.8

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,242 @@
1
+ import { Command } from 'commander';
2
+ import { logger } from '../utils/logger.js';
3
+ import fs from 'fs-extra';
4
+ import path from 'path';
5
+ import inquirer from 'inquirer';
6
+ import { execSync } from 'node:child_process';
7
+
8
+ interface InstallOptions {
9
+ save?: boolean;
10
+ saveDev?: boolean;
11
+ global?: boolean;
12
+ }
13
+
14
+ export const installCommand = new Command('install')
15
+ .description('安装插件(npm 包或 git 仓库)')
16
+ .argument('[plugin]', '插件名称或 git 地址')
17
+ .option('-S, --save', '安装到 dependencies(默认)', true)
18
+ .option('-D, --save-dev', '安装到 devDependencies', false)
19
+ .option('-g, --global', '全局安装', false)
20
+ .action(async (plugin: string, options: InstallOptions) => {
21
+ try {
22
+ let pluginToInstall = plugin;
23
+
24
+ // 如果没有指定插件,交互式输入
25
+ if (!pluginToInstall) {
26
+ const { input } = await inquirer.prompt([
27
+ {
28
+ type: 'input',
29
+ name: 'input',
30
+ message: '请输入插件名称或 git 地址:',
31
+ validate: (input: string) => {
32
+ if (!input.trim()) {
33
+ return '插件名称或地址不能为空';
34
+ }
35
+ return true;
36
+ }
37
+ }
38
+ ]);
39
+ pluginToInstall = input;
40
+ }
41
+
42
+ // 判断插件类型
43
+ const pluginType = detectPluginType(pluginToInstall);
44
+
45
+ logger.info(`检测到插件类型: ${pluginType}`);
46
+ logger.info(`正在安装: ${pluginToInstall}`);
47
+ logger.log('');
48
+
49
+ // 构建安装命令
50
+ const installCmd = buildInstallCommand(pluginToInstall, pluginType, options);
51
+
52
+ logger.log(`执行命令: ${installCmd}`);
53
+ logger.log('');
54
+
55
+ // 执行安装
56
+ try {
57
+ execSync(installCmd, {
58
+ cwd: process.cwd(),
59
+ stdio: 'inherit'
60
+ });
61
+
62
+ logger.success('✓ 插件安装成功!');
63
+ logger.log('');
64
+
65
+ // 如果是 git 插件,提供额外说明
66
+ if (pluginType === 'git') {
67
+ logger.log('📝 Git 插件已安装到 node_modules/');
68
+ logger.log('');
69
+ }
70
+
71
+ // 提示如何启用插件
72
+ const pluginName = extractPluginName(pluginToInstall, pluginType);
73
+ if (pluginName) {
74
+ logger.log('🔌 启用插件:');
75
+ logger.log(`在 zhin.config.ts 中添加:`);
76
+ logger.log('');
77
+ logger.log(' export default defineConfig({');
78
+ logger.log(' plugins: [');
79
+ logger.log(` '${pluginName}'`);
80
+ logger.log(' ]');
81
+ logger.log(' });');
82
+ }
83
+
84
+ } catch (error) {
85
+ logger.error('安装失败');
86
+ throw error;
87
+ }
88
+
89
+ } catch (error: any) {
90
+ logger.error(`安装插件失败: ${error.message}`);
91
+ process.exit(1);
92
+ }
93
+ });
94
+
95
+ // 别名命令
96
+ export const addCommand = new Command('add')
97
+ .description('安装插件(install 的别名)')
98
+ .argument('[plugin]', '插件名称或 git 地址')
99
+ .option('-S, --save', '安装到 dependencies(默认)', true)
100
+ .option('-D, --save-dev', '安装到 devDependencies', false)
101
+ .option('-g, --global', '全局安装', false)
102
+ .action(async (plugin: string, options: InstallOptions) => {
103
+ await installCommand.parseAsync(['node', 'zhin', 'install', plugin || '', ...buildOptionsArray(options)], { from: 'user' });
104
+ });
105
+
106
+ /**
107
+ * 检测插件类型
108
+ */
109
+ function detectPluginType(plugin: string): 'npm' | 'git' | 'github' | 'gitlab' | 'bitbucket' {
110
+ // Git 协议
111
+ if (plugin.startsWith('git://') || plugin.startsWith('git+')) {
112
+ return 'git';
113
+ }
114
+
115
+ // HTTPS/SSH git 地址
116
+ if (plugin.includes('github.com') || plugin.includes('gitlab.com') || plugin.includes('bitbucket.org')) {
117
+ if (plugin.includes('github.com')) return 'github';
118
+ if (plugin.includes('gitlab.com')) return 'gitlab';
119
+ if (plugin.includes('bitbucket.org')) return 'bitbucket';
120
+ return 'git';
121
+ }
122
+
123
+ // GitHub 简写 (user/repo)
124
+ if (/^[\w-]+\/[\w-]+$/.test(plugin)) {
125
+ return 'github';
126
+ }
127
+
128
+ // 默认为 npm 包
129
+ return 'npm';
130
+ }
131
+
132
+ /**
133
+ * 构建安装命令
134
+ */
135
+ function buildInstallCommand(plugin: string, type: string, options: InstallOptions): string {
136
+ const parts = ['pnpm', 'add'];
137
+
138
+ // 添加保存选项
139
+ if (options.saveDev) {
140
+ parts.push('-D');
141
+ }
142
+
143
+ if (options.global) {
144
+ parts.push('-g');
145
+ }
146
+
147
+ // 处理不同类型的插件
148
+ let packageSpec = plugin;
149
+
150
+ switch (type) {
151
+ case 'github':
152
+ // 如果是简写形式,转换为完整 GitHub URL
153
+ if (/^[\w-]+\/[\w-]+$/.test(plugin)) {
154
+ packageSpec = `github:${plugin}`;
155
+ } else if (!plugin.startsWith('git+') && !plugin.startsWith('https://')) {
156
+ packageSpec = `git+${plugin}`;
157
+ }
158
+ break;
159
+
160
+ case 'gitlab':
161
+ if (!plugin.startsWith('git+') && !plugin.startsWith('https://')) {
162
+ packageSpec = `git+${plugin}`;
163
+ }
164
+ break;
165
+
166
+ case 'bitbucket':
167
+ if (!plugin.startsWith('git+') && !plugin.startsWith('https://')) {
168
+ packageSpec = `git+${plugin}`;
169
+ }
170
+ break;
171
+
172
+ case 'git':
173
+ // Git URL 直接使用
174
+ break;
175
+
176
+ case 'npm':
177
+ default:
178
+ // npm 包名直接使用
179
+ break;
180
+ }
181
+
182
+ parts.push(packageSpec);
183
+
184
+ return parts.join(' ');
185
+ }
186
+
187
+ /**
188
+ * 提取插件名称
189
+ */
190
+ function extractPluginName(plugin: string, type: string): string | null {
191
+ switch (type) {
192
+ case 'npm':
193
+ // npm 包名可能包含 scope 和版本号
194
+ // @scope/package@version -> @scope/package 或 package
195
+ const match = plugin.match(/^(@?[\w-]+\/)?([^@]+)/);
196
+ if (match) {
197
+ const fullName = match[0].replace(/@[\d.]+.*$/, ''); // 移除版本号
198
+ // 如果是 @zhin.js/ 开头的包,提取最后的名称
199
+ if (fullName.startsWith('@zhin.js/')) {
200
+ return fullName.replace('@zhin.js/', '');
201
+ }
202
+ return fullName;
203
+ }
204
+ return plugin;
205
+
206
+ case 'github':
207
+ case 'gitlab':
208
+ case 'bitbucket':
209
+ // 从 git URL 中提取仓库名
210
+ const repoMatch = plugin.match(/\/([^/]+?)(\.git)?$/);
211
+ if (repoMatch) {
212
+ return repoMatch[1];
213
+ }
214
+ // 简写形式 user/repo
215
+ if (/^[\w-]+\/([\w-]+)$/.test(plugin)) {
216
+ return plugin.split('/')[1];
217
+ }
218
+ return null;
219
+
220
+ case 'git':
221
+ // 从 git URL 中提取仓库名
222
+ const gitMatch = plugin.match(/\/([^/]+?)(\.git)?$/);
223
+ if (gitMatch) {
224
+ return gitMatch[1];
225
+ }
226
+ return null;
227
+
228
+ default:
229
+ return null;
230
+ }
231
+ }
232
+
233
+ /**
234
+ * 构建选项数组
235
+ */
236
+ function buildOptionsArray(options: InstallOptions): string[] {
237
+ const arr: string[] = [];
238
+ if (options.saveDev) arr.push('-D');
239
+ if (options.global) arr.push('-g');
240
+ return arr;
241
+ }
242
+
@@ -7,16 +7,17 @@ import { execSync } from 'node:child_process';
7
7
 
8
8
  interface NewPluginOptions {
9
9
  skipInstall?: boolean;
10
+ isOfficial?: boolean;
10
11
  }
11
12
 
12
13
  export const newCommand = new Command('new')
13
14
  .description('创建插件包模板')
14
15
  .argument('[plugin-name]', '插件名称(如: my-plugin)')
16
+ .option('--is-official', '是否为官方插件', false)
15
17
  .option('--skip-install', '跳过依赖安装', false)
16
18
  .action(async (pluginName: string, options: NewPluginOptions) => {
17
19
  try {
18
20
  let name = pluginName;
19
-
20
21
  if (!name) {
21
22
  const { pluginName: inputName } = await inquirer.prompt([
22
23
  {
@@ -53,7 +54,7 @@ export const newCommand = new Command('new')
53
54
  await createPluginPackage(pluginDir, name, options);
54
55
 
55
56
  // 自动添加到 app/package.json
56
- await addPluginToApp(name);
57
+ await addPluginToApp(name, options.isOfficial);
57
58
 
58
59
  logger.success(`✓ 插件包 ${name} 创建成功!`);
59
60
  logger.log('');
@@ -76,11 +77,11 @@ export const newCommand = new Command('new')
76
77
 
77
78
  async function createPluginPackage(pluginDir: string, pluginName: string, options: NewPluginOptions) {
78
79
  const capitalizedName = pluginName.charAt(0).toUpperCase() + pluginName.slice(1).replace(/-([a-z])/g, (_, c) => c.toUpperCase());
79
- const packageName = `@zhin.js/${pluginName}`;
80
+ const packageName = options.isOfficial ? `@zhin.js/${pluginName}` : `zhin.js-${pluginName}`;
80
81
 
81
82
  // 创建目录结构
82
83
  await fs.ensureDir(pluginDir);
83
- await fs.ensureDir(path.join(pluginDir, 'app'));
84
+ await fs.ensureDir(path.join(pluginDir, 'src'));
84
85
  await fs.ensureDir(path.join(pluginDir, 'client'));
85
86
  await fs.ensureDir(path.join(pluginDir, 'lib'));
86
87
  await fs.ensureDir(path.join(pluginDir, 'dist'));
@@ -104,23 +105,23 @@ async function createPluginPackage(pluginDir: string, pluginName: string, option
104
105
  },
105
106
  files: [
106
107
  'lib',
107
- 'app',
108
+ 'src',
108
109
  'dist',
109
110
  'client',
110
111
  'README.md',
111
112
  'CHANGELOG.md'
112
113
  ],
113
114
  scripts: {
114
- build: 'pnpm build:app && pnpm build:client',
115
- 'build:app': 'tsc --project tsconfig.app.json',
116
- 'build:client': 'tsc --project tsconfig.client.json',
117
- dev: 'tsc --project tsconfig.app.json --watch',
115
+ build: 'pnpm build:node && pnpm build:client',
116
+ 'build:node': 'tsc --project node.tsconfig.json',
117
+ 'build:client': 'zhin-console build',
118
+ dev: 'tsc --project node.tsconfig.json --watch',
118
119
  clean: 'rm -rf lib dist',
119
120
  prepublishOnly: 'pnpm build'
120
121
  },
121
122
  keywords: [
122
- 'zhin',
123
- 'zhin-plugin',
123
+ 'zhin.js',
124
+ 'plugin',
124
125
  pluginName
125
126
  ],
126
127
  author: '',
@@ -135,62 +136,77 @@ async function createPluginPackage(pluginDir: string, pluginName: string, option
135
136
  '@zhin.js/types': 'workspace:*',
136
137
  '@types/node': 'latest',
137
138
  '@types/react': 'latest',
139
+ '@types/react-dom': 'latest',
138
140
  'typescript': 'latest',
139
141
  'react': 'latest',
140
142
  'react-dom': 'latest',
141
- 'lucide-react': 'latest'
143
+ "@zhin.js/client":"workspace:*",
144
+ 'lucide-react': 'latest',
145
+ 'radix-ui': 'latest',
146
+ '@radix-ui/themes': 'latest'
142
147
  }
143
148
  };
144
149
 
145
150
  await fs.writeJson(path.join(pluginDir, 'package.json'), packageJson, { spaces: 2 });
146
151
 
147
- // 创建 tsconfig.app.json (插件代码)
148
- const tsConfigApp = {
149
- extends: '../../tsconfig.json',
150
- compilerOptions: {
151
- rootDir: './app',
152
- outDir: './lib',
153
- declaration: true,
154
- noEmit: false
152
+ // 创建 tsconfig.json (服务端主配置)
153
+ const tsConfig = {
154
+ "compilerOptions": {
155
+ "target": "ES2022",
156
+ "module": "ESNext",
157
+ "moduleResolution": "bundler",
158
+ "outDir": "./lib",
159
+ "rootDir": "./src",
160
+ "strict": true,
161
+ "esModuleInterop": true,
162
+ "skipLibCheck": true,
163
+ "forceConsistentCasingInFileNames": true,
164
+ "resolveJsonModule": true,
165
+ "isolatedModules": true,
166
+ "allowSyntheticDefaultImports": true,
167
+ "experimentalDecorators": true,
168
+ "emitDecoratorMetadata": true,
169
+ "declaration": true,
170
+ "declarationMap": true,
171
+ "sourceMap": true,
172
+ "verbatimModuleSyntax": false,
173
+ "types": [
174
+ "@zhin.js/http"
175
+ ]
155
176
  },
156
- include: ['app/**/*'],
157
- exclude: ['node_modules', 'lib', 'dist']
158
- };
159
-
160
- await fs.writeJson(path.join(pluginDir, 'tsconfig.app.json'), tsConfigApp, { spaces: 2 });
177
+ "include": ["src/**/*"],
178
+ "exclude": ["lib", "node_modules", "client"]
179
+ }
180
+ ;
161
181
 
162
- // 创建 tsconfig.client.json (客户端代码)
163
- const tsConfigClient = {
164
- compilerOptions: {
165
- target: 'ES2022',
166
- module: 'ESNext',
167
- moduleResolution: 'bundler',
168
- rootDir: './client',
169
- outDir: './dist',
170
- declaration: false,
171
- jsx: 'react-jsx',
172
- baseUrl: '.',
173
- skipLibCheck: true,
174
- esModuleInterop: true,
175
- allowSyntheticDefaultImports: true
176
- },
177
- include: ['client/**/*'],
178
- exclude: ['node_modules', 'lib', 'dist']
179
- };
182
+ await fs.writeJson(path.join(pluginDir, 'tsconfig.json'), tsConfig, { spaces: 2 });
180
183
 
181
- await fs.writeJson(path.join(pluginDir, 'tsconfig.client.json'), tsConfigClient, { spaces: 2 });
182
184
 
183
- // 创建 tsconfig.json (编辑器支持)
184
- const tsConfig = {
185
- extends: '../../tsconfig.json',
186
- include: ['app/**/*', 'client/**/*'],
187
- exclude: ['node_modules', 'lib', 'dist']
188
- };
185
+ // 创建 client/tsconfig.json (客户端主配置)
186
+ const clientTsConfig = {
187
+ "compilerOptions": {
188
+ "outDir": "../dist",
189
+ "baseUrl": ".",
190
+ "declaration": true,
191
+ "module": "ESNext",
192
+ "moduleResolution": "bundler",
193
+ "target": "ES2022",
194
+ "jsx":"react-jsx",
195
+ "declarationMap": true,
196
+ "sourceMap": true,
197
+ "skipLibCheck": true,
198
+ "noEmit": false
199
+ },
200
+ "include": [
201
+ "./**/*"
202
+ ]
203
+ }
204
+ ;
189
205
 
190
- await fs.writeJson(path.join(pluginDir, 'tsconfig.json'), tsConfig, { spaces: 2 });
206
+ await fs.writeJson(path.join(pluginDir, 'client', 'tsconfig.json'), clientTsConfig, { spaces: 2 });
191
207
 
192
- // 创建插件主入口文件 app/index.ts
193
- const appContent = `import {
208
+ // 创建服务端入口文件 src/index.ts
209
+ const indexContent = `import {
194
210
  useLogger,
195
211
  useContext,
196
212
  onDispose,
@@ -201,9 +217,10 @@ const logger = useLogger();
201
217
 
202
218
  // 注册客户端入口(如果有客户端代码)
203
219
  useContext('web', (web) => {
204
- const dispose = web.addEntry(
205
- path.resolve(import.meta.dirname, '../client/index.tsx')
206
- );
220
+ const dispose = web.addEntry({
221
+ production: path.resolve(import.meta.dirname, '../dist/index.js'),
222
+ development: path.resolve(import.meta.dirname, '../client/index.tsx')
223
+ });
207
224
  return dispose;
208
225
  });
209
226
 
@@ -215,7 +232,7 @@ onDispose(() => {
215
232
  logger.info('${capitalizedName} 插件已加载');
216
233
  `;
217
234
 
218
- await fs.writeFile(path.join(pluginDir, 'app', 'index.ts'), appContent);
235
+ await fs.writeFile(path.join(pluginDir, 'src', 'index.ts'), indexContent);
219
236
 
220
237
  // 创建客户端入口文件 client/index.tsx
221
238
  const clientContent = `import { addPage } from '@zhin.js/client';
@@ -234,7 +251,7 @@ export { ${capitalizedName}Page };
234
251
  `;
235
252
 
236
253
  await fs.writeFile(path.join(pluginDir, 'client', 'index.tsx'), clientContent);
237
-
254
+
238
255
  // 创建客户端页面组件
239
256
  await fs.ensureDir(path.join(pluginDir, 'client', 'pages'));
240
257
  const pageContent = `import { useEffect } from 'react';
@@ -335,7 +352,7 @@ dist/
335
352
  }
336
353
  }
337
354
 
338
- async function addPluginToApp(pluginName: string) {
355
+ async function addPluginToApp(pluginName: string, isOfficial?: boolean) {
339
356
  try {
340
357
  const rootPackageJsonPath = path.resolve(process.cwd(), 'package.json');
341
358
 
@@ -346,7 +363,7 @@ async function addPluginToApp(pluginName: string) {
346
363
  }
347
364
 
348
365
  const packageJson = await fs.readJson(rootPackageJsonPath);
349
- const packageName = `@zhin.js/${pluginName}`;
366
+ const packageName = isOfficial ? `@zhin.js/${pluginName}` : `zhin.js-${pluginName}`;
350
367
 
351
368
  // 初始化 dependencies
352
369
  if (!packageJson.dependencies) {
@@ -0,0 +1,176 @@
1
+ import { Command } from 'commander';
2
+ import { logger } from '../utils/logger.js';
3
+ import fs from 'fs-extra';
4
+ import path from 'path';
5
+ import inquirer from 'inquirer';
6
+ import { execSync } from 'node:child_process';
7
+
8
+ interface PublishOptions {
9
+ tag?: string;
10
+ access?: 'public' | 'restricted';
11
+ registry?: string;
12
+ dryRun?: boolean;
13
+ skipBuild?: boolean;
14
+ }
15
+
16
+ export const pubCommand = new Command('pub')
17
+ .description('发布插件到 npm')
18
+ .argument('[plugin-name]', '插件名称(如: my-plugin)')
19
+ .option('--tag <tag>', '发布标签', 'latest')
20
+ .option('--access <access>', '访问级别 (public|restricted)', 'public')
21
+ .option('--registry <url>', '自定义 npm registry')
22
+ .option('--dry-run', '试运行,不实际发布', false)
23
+ .option('--skip-build', '跳过构建步骤', false)
24
+ .action(async (pluginName: string, options: PublishOptions) => {
25
+ try {
26
+ const pluginsDir = path.resolve(process.cwd(), 'plugins');
27
+
28
+ // 检查 plugins 目录是否存在
29
+ if (!fs.existsSync(pluginsDir)) {
30
+ logger.error('未找到 plugins 目录,请在项目根目录运行此命令');
31
+ process.exit(1);
32
+ }
33
+
34
+ // 获取所有插件
35
+ const availablePlugins = fs.readdirSync(pluginsDir)
36
+ .filter(name => {
37
+ const pluginPath = path.join(pluginsDir, name);
38
+ return fs.statSync(pluginPath).isDirectory() &&
39
+ fs.existsSync(path.join(pluginPath, 'package.json'));
40
+ });
41
+
42
+ if (availablePlugins.length === 0) {
43
+ logger.error('未找到可发布的插件');
44
+ process.exit(1);
45
+ }
46
+
47
+ // 如果没有指定插件名,让用户选择
48
+ let selectedPlugin = pluginName;
49
+ if (!selectedPlugin) {
50
+ if (availablePlugins.length === 1) {
51
+ selectedPlugin = availablePlugins[0];
52
+ logger.info(`自动选择插件: ${selectedPlugin}`);
53
+ } else {
54
+ const { plugin } = await inquirer.prompt([
55
+ {
56
+ type: 'list',
57
+ name: 'plugin',
58
+ message: '请选择要发布的插件:',
59
+ choices: availablePlugins
60
+ }
61
+ ]);
62
+ selectedPlugin = plugin;
63
+ }
64
+ }
65
+
66
+ // 验证插件是否存在
67
+ const pluginDir = path.join(pluginsDir, selectedPlugin);
68
+ if (!fs.existsSync(pluginDir)) {
69
+ logger.error(`插件不存在: ${selectedPlugin}`);
70
+ logger.log(`可用插件: ${availablePlugins.join(', ')}`);
71
+ process.exit(1);
72
+ }
73
+
74
+ const packageJsonPath = path.join(pluginDir, 'package.json');
75
+ if (!fs.existsSync(packageJsonPath)) {
76
+ logger.error(`未找到 package.json: ${packageJsonPath}`);
77
+ process.exit(1);
78
+ }
79
+
80
+ // 读取 package.json
81
+ const packageJson = await fs.readJson(packageJsonPath);
82
+ const packageName = packageJson.name;
83
+ const version = packageJson.version;
84
+
85
+ logger.info(`准备发布插件: ${packageName}@${version}`);
86
+ logger.log('');
87
+
88
+ // 确认发布
89
+ if (!options.dryRun) {
90
+ const { confirm } = await inquirer.prompt([
91
+ {
92
+ type: 'confirm',
93
+ name: 'confirm',
94
+ message: `确认发布 ${packageName}@${version} 到 npm?`,
95
+ default: false
96
+ }
97
+ ]);
98
+
99
+ if (!confirm) {
100
+ logger.warn('已取消发布');
101
+ process.exit(0);
102
+ }
103
+ }
104
+
105
+ // 构建插件
106
+ if (!options.skipBuild) {
107
+ logger.info('正在构建插件...');
108
+ try {
109
+ execSync('pnpm build', {
110
+ cwd: pluginDir,
111
+ stdio: 'inherit'
112
+ });
113
+ logger.success('✓ 构建完成');
114
+ } catch (error) {
115
+ logger.error('构建失败');
116
+ throw error;
117
+ }
118
+ }
119
+
120
+ // 构建 npm publish 命令
121
+ const publishArgs = ['publish'];
122
+
123
+ if (options.access) {
124
+ publishArgs.push('--access', options.access);
125
+ }
126
+
127
+ if (options.tag) {
128
+ publishArgs.push('--tag', options.tag);
129
+ }
130
+
131
+ if (options.registry) {
132
+ publishArgs.push('--registry', options.registry);
133
+ }
134
+
135
+ if (options.dryRun) {
136
+ publishArgs.push('--dry-run');
137
+ }
138
+
139
+ // 总是添加 --no-git-checks(因为 plugins 可能不是 git 根目录)
140
+ publishArgs.push('--no-git-checks');
141
+
142
+ // 发布插件
143
+ logger.info(`正在发布${options.dryRun ? '(试运行)' : ''}...`);
144
+ logger.log(`命令: pnpm ${publishArgs.join(' ')}`);
145
+ logger.log('');
146
+
147
+ try {
148
+ execSync(`pnpm ${publishArgs.join(' ')}`, {
149
+ cwd: pluginDir,
150
+ stdio: 'inherit'
151
+ });
152
+
153
+ if (options.dryRun) {
154
+ logger.success('✓ 试运行完成');
155
+ logger.log('');
156
+ logger.log('💡 提示: 移除 --dry-run 参数以实际发布');
157
+ } else {
158
+ logger.success(`✓ ${packageName}@${version} 发布成功!`);
159
+ logger.log('');
160
+ logger.log('📦 安装命令:');
161
+ logger.log(` pnpm add ${packageName}`);
162
+ logger.log('');
163
+ logger.log('🔗 npm 链接:');
164
+ logger.log(` https://www.npmjs.com/package/${packageName}`);
165
+ }
166
+ } catch (error) {
167
+ logger.error('发布失败');
168
+ throw error;
169
+ }
170
+
171
+ } catch (error: any) {
172
+ logger.error(`发布失败: ${error.message}`);
173
+ process.exit(1);
174
+ }
175
+ });
176
+