@mindbase/deploy 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,266 @@
1
+ import * as clack from '@clack/prompts';
2
+ import pc from 'picocolors';
3
+ import { Command } from 'commander';
4
+ import { noteBox } from '@mindbase/cli-ui';
5
+ import { getConfig, setConfig } from '../config/manager.js';
6
+ import type { ServerConfig } from '../config/schema.js';
7
+ import { testConnection } from '../ssh/client.js';
8
+
9
+ /** 取消处理 */
10
+ function handleCancel<T>(result: T | symbol): T {
11
+ if (typeof result === 'symbol' && clack.isCancel(result)) {
12
+ clack.cancel('操作已取消');
13
+ process.exit(0);
14
+ }
15
+ return result as T;
16
+ }
17
+
18
+ /**
19
+ * 交互式添加服务器
20
+ */
21
+ async function addServer(): Promise<void> {
22
+ const config = getConfig();
23
+ const id = handleCancel<string>(
24
+ await clack.text({ message: '服务器 ID(用于标识,如 own、29)' })
25
+ );
26
+
27
+ if (config.servers[id]) {
28
+ clack.log.warn(`服务器 "${id}" 已存在`);
29
+ return;
30
+ }
31
+
32
+ const serverConfig = await collectServerConfig();
33
+ config.servers[id] = serverConfig;
34
+ setConfig(config);
35
+ clack.log.success(`服务器 "${id}" 已添加`);
36
+ }
37
+
38
+ /**
39
+ * 交互式编辑服务器
40
+ */
41
+ async function editServer(): Promise<void> {
42
+ const config = getConfig();
43
+ const ids = Object.keys(config.servers);
44
+ if (ids.length === 0) {
45
+ clack.log.warn('没有已配置的服务器');
46
+ return;
47
+ }
48
+
49
+ const id = handleCancel<string>(
50
+ await clack.select({
51
+ message: '选择要编辑的服务器',
52
+ options: ids.map((id) => ({ value: id, label: `${id} (${config.servers[id].host})` })),
53
+ })
54
+ );
55
+
56
+ const existing = config.servers[id];
57
+ const serverConfig = await collectServerConfig(existing);
58
+ config.servers[id] = serverConfig;
59
+ setConfig(config);
60
+ clack.log.success(`服务器 "${id}" 已更新`);
61
+ }
62
+
63
+ /**
64
+ * 删除服务器
65
+ */
66
+ async function deleteServer(): Promise<void> {
67
+ const config = getConfig();
68
+ const ids = Object.keys(config.servers);
69
+ if (ids.length === 0) {
70
+ clack.log.warn('没有已配置的服务器');
71
+ return;
72
+ }
73
+
74
+ const id = handleCancel<string>(
75
+ await clack.select({
76
+ message: '选择要删除的服务器',
77
+ options: ids.map((id) => ({ value: id, label: `${id} (${config.servers[id].host})` })),
78
+ })
79
+ );
80
+
81
+ const confirm = handleCancel<boolean>(
82
+ await clack.confirm({ message: `确定删除服务器 "${id}"?` })
83
+ );
84
+ if (!confirm) {
85
+ clack.log.info('已取消');
86
+ return;
87
+ }
88
+
89
+ delete config.servers[id];
90
+ setConfig(config);
91
+ clack.log.success(`服务器 "${id}" 已删除`);
92
+ }
93
+
94
+ /**
95
+ * 测试服务器连接
96
+ */
97
+ async function doTestConnection(): Promise<void> {
98
+ const config = getConfig();
99
+ const ids = Object.keys(config.servers);
100
+ if (ids.length === 0) {
101
+ clack.log.warn('没有已配置的服务器');
102
+ return;
103
+ }
104
+
105
+ const id = handleCancel<string>(
106
+ await clack.select({
107
+ message: '选择要测试的服务器',
108
+ options: ids.map((id) => ({ value: id, label: `${id} (${config.servers[id].host})` })),
109
+ })
110
+ );
111
+
112
+ const spinner = clack.spinner();
113
+ spinner.start(`正在连接 ${id}...`);
114
+ const ok = await testConnection(config.servers[id]);
115
+ if (ok) {
116
+ spinner.stop(`${id} 连接成功`);
117
+ } else {
118
+ spinner.stop(`${id} 连接失败`);
119
+ }
120
+ }
121
+
122
+ /**
123
+ * 列出所有服务器
124
+ */
125
+ function listServers(): void {
126
+ const config = getConfig();
127
+ const ids = Object.keys(config.servers);
128
+ if (ids.length === 0) {
129
+ clack.log.info('没有已配置的服务器');
130
+ return;
131
+ }
132
+
133
+ const lines = ids.map((id) => {
134
+ const s = config.servers[id];
135
+ const auth = s.privateKeyPath ? '私钥' : '密码';
136
+ return ` ${pc.cyan(id)}: ${s.host}:${s.port} (${s.username}, ${auth})`;
137
+ });
138
+ noteBox(lines.join('\n'), '服务器列表');
139
+ }
140
+
141
+ /**
142
+ * 收集服务器配置(交互式)
143
+ */
144
+ async function collectServerConfig(existing?: ServerConfig): Promise<ServerConfig> {
145
+ const host = handleCancel<string>(
146
+ await clack.text({
147
+ message: '主机地址',
148
+ initialValue: existing?.host,
149
+ })
150
+ );
151
+
152
+ const portStr = handleCancel<string>(
153
+ await clack.text({
154
+ message: '端口',
155
+ initialValue: existing?.port?.toString() ?? '22',
156
+ validate: (value) => {
157
+ const parsed = parseInt(value, 10);
158
+ if (isNaN(parsed) || parsed < 1 || parsed > 65535) {
159
+ return '请输入有效的端口号 (1-65535)';
160
+ }
161
+ },
162
+ })
163
+ );
164
+ const port = parseInt(portStr, 10);
165
+
166
+ const username = handleCancel<string>(
167
+ await clack.text({
168
+ message: '用户名',
169
+ initialValue: existing?.username ?? 'root',
170
+ })
171
+ );
172
+
173
+ const authType = handleCancel<'password' | 'privateKey'>(
174
+ await clack.select({
175
+ message: '认证方式',
176
+ options: [
177
+ { value: 'password', label: '密码' },
178
+ { value: 'privateKey', label: '私钥' },
179
+ ],
180
+ initialValue: existing?.privateKeyPath ? 'privateKey' : 'password',
181
+ })
182
+ );
183
+
184
+ if (authType === 'password') {
185
+ const password = handleCancel<string>(
186
+ await clack.password({
187
+ message: '密码',
188
+ })
189
+ );
190
+ return { host, port, username, password };
191
+ } else {
192
+ const privateKeyPath = handleCancel<string>(
193
+ await clack.text({
194
+ message: '私钥文件路径',
195
+ initialValue: existing?.privateKeyPath ?? '~/.ssh/id_rsa',
196
+ })
197
+ );
198
+ const passphrase = handleCancel<string>(
199
+ await clack.password({
200
+ message: '私钥密码(如有)',
201
+ })
202
+ );
203
+ return {
204
+ host,
205
+ port,
206
+ username,
207
+ privateKeyPath,
208
+ passphrase: passphrase || undefined,
209
+ };
210
+ }
211
+ }
212
+
213
+ /**
214
+ * 创建 server 子命令
215
+ */
216
+ export function createServerCommand(): Command {
217
+ const cmd = new Command('server');
218
+ cmd.description('服务器管理');
219
+
220
+ cmd
221
+ .command('add')
222
+ .description('添加服务器')
223
+ .action(async () => {
224
+ clack.intro('添加服务器');
225
+ await addServer();
226
+ clack.outro('完成');
227
+ });
228
+
229
+ cmd
230
+ .command('edit')
231
+ .description('编辑服务器')
232
+ .action(async () => {
233
+ clack.intro('编辑服务器');
234
+ await editServer();
235
+ clack.outro('完成');
236
+ });
237
+
238
+ cmd
239
+ .command('delete')
240
+ .description('删除服务器')
241
+ .action(async () => {
242
+ clack.intro('删除服务器');
243
+ await deleteServer();
244
+ clack.outro('完成');
245
+ });
246
+
247
+ cmd
248
+ .command('test')
249
+ .description('测试服务器连接')
250
+ .action(async () => {
251
+ clack.intro('测试连接');
252
+ await doTestConnection();
253
+ clack.outro('完成');
254
+ });
255
+
256
+ cmd
257
+ .command('list')
258
+ .description('列出所有服务器')
259
+ .action(() => {
260
+ clack.intro('服务器列表');
261
+ listServers();
262
+ clack.outro('完成');
263
+ });
264
+
265
+ return cmd;
266
+ }
@@ -0,0 +1,62 @@
1
+ import SftpClient from 'ssh2-sftp-client';
2
+ import { readFileSync } from 'fs';
3
+ import { homedir } from 'os';
4
+ import type { ServerConfig } from '../config/schema.js';
5
+
6
+ /**
7
+ * 解析路径中的 ~ 为用户目录
8
+ */
9
+ function resolvePath(path: string): string {
10
+ if (path.startsWith('~/')) {
11
+ return `${homedir()}${path.slice(1)}`;
12
+ }
13
+ return path;
14
+ }
15
+
16
+ /**
17
+ * 构建 SSH 连接配置
18
+ */
19
+ function buildConnectConfig(config: ServerConfig): Record<string, unknown> {
20
+ const base = {
21
+ host: config.host,
22
+ port: config.port,
23
+ username: config.username,
24
+ };
25
+
26
+ if (config.privateKeyPath) {
27
+ return {
28
+ ...base,
29
+ privateKey: readFileSync(resolvePath(config.privateKeyPath)),
30
+ passphrase: config.passphrase || undefined,
31
+ };
32
+ }
33
+
34
+ return {
35
+ ...base,
36
+ password: config.password,
37
+ };
38
+ }
39
+
40
+ /**
41
+ * 创建并连接 SFTP 客户端
42
+ */
43
+ export async function connectSftp(name: string, config: ServerConfig): Promise<SftpClient> {
44
+ const sftp = new SftpClient(name);
45
+ await sftp.connect(buildConnectConfig(config));
46
+ return sftp;
47
+ }
48
+
49
+ /**
50
+ * 测试服务器连接
51
+ */
52
+ export async function testConnection(config: ServerConfig): Promise<boolean> {
53
+ const sftp = new SftpClient('test');
54
+ try {
55
+ await sftp.connect(buildConnectConfig(config));
56
+ return true;
57
+ } catch {
58
+ return false;
59
+ } finally {
60
+ await sftp.end();
61
+ }
62
+ }
@@ -0,0 +1,43 @@
1
+ import type SftpClient from 'ssh2-sftp-client';
2
+
3
+ /**
4
+ * 在远程服务器执行命令
5
+ */
6
+ export async function execRemote(sftp: SftpClient, command: string): Promise<string> {
7
+ // 尝试加载环境变量,如果文件不存在则忽略错误
8
+ const fullCommand = `[ -f /etc/profile ] && source /etc/profile; ${command}`;
9
+ return new Promise((resolve, reject) => {
10
+ // @ts-ignore: ssh2-sftp-client types don't expose client but it exists at runtime
11
+ const client = (sftp as any).client;
12
+ if (!client) {
13
+ reject(new Error('SSH 客户端未连接'));
14
+ return;
15
+ }
16
+ client.exec(fullCommand, (err: Error | undefined, stream: any) => {
17
+ if (err) {
18
+ reject(err);
19
+ return;
20
+ }
21
+ let outStr = '';
22
+ let errStr = '';
23
+ stream
24
+ .on('close', (code: number, signal: string) => {
25
+ if (code !== 0) {
26
+ reject(new Error(`远程命令执行失败 (code=${code}): ${errStr || signal}`));
27
+ return;
28
+ }
29
+ resolve(outStr);
30
+ })
31
+ .on('data', (data: Buffer) => {
32
+ const str = data.toString();
33
+ outStr += str;
34
+ process.stdout.write(str);
35
+ })
36
+ .stderr.on('data', (data: Buffer) => {
37
+ const str = data.toString();
38
+ errStr += str;
39
+ process.stderr.write(str);
40
+ });
41
+ });
42
+ });
43
+ }
@@ -1 +0,0 @@
1
- #!/usr/bin/env node
package/dist/bin/index.js DELETED
@@ -1,245 +0,0 @@
1
- #!/usr/bin/env node
2
- import { runApp, runInit, runProject, showConfig, getConfig, setConfig, testConnection } from '../chunk-YB2FK4HH.js';
3
- import { Command } from 'commander';
4
- import * as clack from '@clack/prompts';
5
- import { resolve } from 'path';
6
- import pc from 'picocolors';
7
- import { noteBox } from '@mindbase/cli-ui';
8
-
9
- function handleCancel(result) {
10
- if (typeof result === "symbol" && clack.isCancel(result)) {
11
- clack.cancel("\u64CD\u4F5C\u5DF2\u53D6\u6D88");
12
- process.exit(0);
13
- }
14
- return result;
15
- }
16
- async function addServer() {
17
- const config = getConfig();
18
- const id = handleCancel(
19
- await clack.text({ message: "\u670D\u52A1\u5668 ID\uFF08\u7528\u4E8E\u6807\u8BC6\uFF0C\u5982 own\u300129\uFF09" })
20
- );
21
- if (config.servers[id]) {
22
- clack.log.warn(`\u670D\u52A1\u5668 "${id}" \u5DF2\u5B58\u5728`);
23
- return;
24
- }
25
- const serverConfig = await collectServerConfig();
26
- config.servers[id] = serverConfig;
27
- setConfig(config);
28
- clack.log.success(`\u670D\u52A1\u5668 "${id}" \u5DF2\u6DFB\u52A0`);
29
- }
30
- async function editServer() {
31
- const config = getConfig();
32
- const ids = Object.keys(config.servers);
33
- if (ids.length === 0) {
34
- clack.log.warn("\u6CA1\u6709\u5DF2\u914D\u7F6E\u7684\u670D\u52A1\u5668");
35
- return;
36
- }
37
- const id = handleCancel(
38
- await clack.select({
39
- message: "\u9009\u62E9\u8981\u7F16\u8F91\u7684\u670D\u52A1\u5668",
40
- options: ids.map((id2) => ({ value: id2, label: `${id2} (${config.servers[id2].host})` }))
41
- })
42
- );
43
- const existing = config.servers[id];
44
- const serverConfig = await collectServerConfig(existing);
45
- config.servers[id] = serverConfig;
46
- setConfig(config);
47
- clack.log.success(`\u670D\u52A1\u5668 "${id}" \u5DF2\u66F4\u65B0`);
48
- }
49
- async function deleteServer() {
50
- const config = getConfig();
51
- const ids = Object.keys(config.servers);
52
- if (ids.length === 0) {
53
- clack.log.warn("\u6CA1\u6709\u5DF2\u914D\u7F6E\u7684\u670D\u52A1\u5668");
54
- return;
55
- }
56
- const id = handleCancel(
57
- await clack.select({
58
- message: "\u9009\u62E9\u8981\u5220\u9664\u7684\u670D\u52A1\u5668",
59
- options: ids.map((id2) => ({ value: id2, label: `${id2} (${config.servers[id2].host})` }))
60
- })
61
- );
62
- const confirm2 = handleCancel(
63
- await clack.confirm({ message: `\u786E\u5B9A\u5220\u9664\u670D\u52A1\u5668 "${id}"\uFF1F` })
64
- );
65
- if (!confirm2) {
66
- clack.log.info("\u5DF2\u53D6\u6D88");
67
- return;
68
- }
69
- delete config.servers[id];
70
- setConfig(config);
71
- clack.log.success(`\u670D\u52A1\u5668 "${id}" \u5DF2\u5220\u9664`);
72
- }
73
- async function doTestConnection() {
74
- const config = getConfig();
75
- const ids = Object.keys(config.servers);
76
- if (ids.length === 0) {
77
- clack.log.warn("\u6CA1\u6709\u5DF2\u914D\u7F6E\u7684\u670D\u52A1\u5668");
78
- return;
79
- }
80
- const id = handleCancel(
81
- await clack.select({
82
- message: "\u9009\u62E9\u8981\u6D4B\u8BD5\u7684\u670D\u52A1\u5668",
83
- options: ids.map((id2) => ({ value: id2, label: `${id2} (${config.servers[id2].host})` }))
84
- })
85
- );
86
- const spinner2 = clack.spinner();
87
- spinner2.start(`\u6B63\u5728\u8FDE\u63A5 ${id}...`);
88
- const ok = await testConnection(config.servers[id]);
89
- if (ok) {
90
- spinner2.stop(`${id} \u8FDE\u63A5\u6210\u529F`);
91
- } else {
92
- spinner2.stop(`${id} \u8FDE\u63A5\u5931\u8D25`);
93
- }
94
- }
95
- function listServers() {
96
- const config = getConfig();
97
- const ids = Object.keys(config.servers);
98
- if (ids.length === 0) {
99
- clack.log.info("\u6CA1\u6709\u5DF2\u914D\u7F6E\u7684\u670D\u52A1\u5668");
100
- return;
101
- }
102
- const lines = ids.map((id) => {
103
- const s = config.servers[id];
104
- const auth = s.privateKeyPath ? "\u79C1\u94A5" : "\u5BC6\u7801";
105
- return ` ${pc.cyan(id)}: ${s.host}:${s.port} (${s.username}, ${auth})`;
106
- });
107
- noteBox(lines.join("\n"), "\u670D\u52A1\u5668\u5217\u8868");
108
- }
109
- async function collectServerConfig(existing) {
110
- const host = handleCancel(
111
- await clack.text({
112
- message: "\u4E3B\u673A\u5730\u5740",
113
- initialValue: existing?.host
114
- })
115
- );
116
- const portStr = handleCancel(
117
- await clack.text({
118
- message: "\u7AEF\u53E3",
119
- initialValue: existing?.port?.toString() ?? "22",
120
- validate: (value) => {
121
- const parsed = parseInt(value, 10);
122
- if (isNaN(parsed) || parsed < 1 || parsed > 65535) {
123
- return "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u7AEF\u53E3\u53F7 (1-65535)";
124
- }
125
- }
126
- })
127
- );
128
- const port = parseInt(portStr, 10);
129
- const username = handleCancel(
130
- await clack.text({
131
- message: "\u7528\u6237\u540D",
132
- initialValue: existing?.username ?? "root"
133
- })
134
- );
135
- const authType = handleCancel(
136
- await clack.select({
137
- message: "\u8BA4\u8BC1\u65B9\u5F0F",
138
- options: [
139
- { value: "password", label: "\u5BC6\u7801" },
140
- { value: "privateKey", label: "\u79C1\u94A5" }
141
- ],
142
- initialValue: existing?.privateKeyPath ? "privateKey" : "password"
143
- })
144
- );
145
- if (authType === "password") {
146
- const password2 = handleCancel(
147
- await clack.password({
148
- message: "\u5BC6\u7801"
149
- })
150
- );
151
- return { host, port, username, password: password2 };
152
- } else {
153
- const privateKeyPath = handleCancel(
154
- await clack.text({
155
- message: "\u79C1\u94A5\u6587\u4EF6\u8DEF\u5F84",
156
- initialValue: existing?.privateKeyPath ?? "~/.ssh/id_rsa"
157
- })
158
- );
159
- const passphrase = handleCancel(
160
- await clack.password({
161
- message: "\u79C1\u94A5\u5BC6\u7801\uFF08\u5982\u6709\uFF09"
162
- })
163
- );
164
- return {
165
- host,
166
- port,
167
- username,
168
- privateKeyPath,
169
- passphrase: passphrase || void 0
170
- };
171
- }
172
- }
173
- function createServerCommand() {
174
- const cmd = new Command("server");
175
- cmd.description("\u670D\u52A1\u5668\u7BA1\u7406");
176
- cmd.command("add").description("\u6DFB\u52A0\u670D\u52A1\u5668").action(async () => {
177
- clack.intro("\u6DFB\u52A0\u670D\u52A1\u5668");
178
- await addServer();
179
- clack.outro("\u5B8C\u6210");
180
- });
181
- cmd.command("edit").description("\u7F16\u8F91\u670D\u52A1\u5668").action(async () => {
182
- clack.intro("\u7F16\u8F91\u670D\u52A1\u5668");
183
- await editServer();
184
- clack.outro("\u5B8C\u6210");
185
- });
186
- cmd.command("delete").description("\u5220\u9664\u670D\u52A1\u5668").action(async () => {
187
- clack.intro("\u5220\u9664\u670D\u52A1\u5668");
188
- await deleteServer();
189
- clack.outro("\u5B8C\u6210");
190
- });
191
- cmd.command("test").description("\u6D4B\u8BD5\u670D\u52A1\u5668\u8FDE\u63A5").action(async () => {
192
- clack.intro("\u6D4B\u8BD5\u8FDE\u63A5");
193
- await doTestConnection();
194
- clack.outro("\u5B8C\u6210");
195
- });
196
- cmd.command("list").description("\u5217\u51FA\u6240\u6709\u670D\u52A1\u5668").action(() => {
197
- clack.intro("\u670D\u52A1\u5668\u5217\u8868");
198
- listServers();
199
- clack.outro("\u5B8C\u6210");
200
- });
201
- return cmd;
202
- }
203
-
204
- // bin/shared.ts
205
- function createDeployProgram() {
206
- const program = new Command();
207
- program.name("deploy").description("\u90E8\u7F72\u5DE5\u5177").version("1.0.0").action(async () => {
208
- clack.intro("deploy");
209
- try {
210
- await runApp();
211
- } catch (err) {
212
- clack.log.error(`\u6267\u884C\u5931\u8D25: ${err}`);
213
- process.exit(1);
214
- }
215
- clack.outro("\u518D\u89C1");
216
- });
217
- program.command("init").description("\u4EA4\u4E92\u5F0F\u521D\u59CB\u5316\u9879\u76EE\u90E8\u7F72\u914D\u7F6E").action(async () => {
218
- clack.intro("deploy init");
219
- try {
220
- await runInit(resolve("."));
221
- } catch (err) {
222
- clack.log.error(`\u6267\u884C\u5931\u8D25: ${err}`);
223
- process.exit(1);
224
- }
225
- });
226
- program.command("run <project>").description("\u76F4\u63A5\u8FD0\u884C\u6307\u5B9A\u9879\u76EE").option("-s, --server <server>", "\u8986\u76D6\u9879\u76EE\u9ED8\u8BA4\u670D\u52A1\u5668").action(async (project, options) => {
227
- clack.intro("deploy run");
228
- try {
229
- await runProject(project, options.server);
230
- } catch (err) {
231
- clack.log.error(`\u6267\u884C\u5931\u8D25: ${err}`);
232
- process.exit(1);
233
- }
234
- });
235
- program.command("config").description("\u67E5\u770B\u914D\u7F6E").action(() => {
236
- showConfig();
237
- });
238
- program.addCommand(createServerCommand());
239
- return program;
240
- }
241
-
242
- // bin/index.ts
243
- createDeployProgram().parse();
244
- //# sourceMappingURL=index.js.map
245
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/server/app.ts","../../bin/shared.ts","../../bin/index.ts"],"names":["id","confirm","spinner","password","Command","clack2"],"mappings":"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","file":"index.js","sourcesContent":["import * as clack from '@clack/prompts';\nimport pc from 'picocolors';\nimport { Command } from 'commander';\nimport { noteBox } from '@mindbase/cli-ui';\nimport { getConfig, setConfig } from '../config/manager.js';\nimport type { ServerConfig } from '../config/schema.js';\nimport { testConnection } from '../ssh/client.js';\n\n/** 取消处理 */\nfunction handleCancel<T>(result: T | symbol): T {\n if (typeof result === 'symbol' && clack.isCancel(result)) {\n clack.cancel('操作已取消');\n process.exit(0);\n }\n return result as T;\n}\n\n/**\n * 交互式添加服务器\n */\nasync function addServer(): Promise<void> {\n const config = getConfig();\n const id = handleCancel<string>(\n await clack.text({ message: '服务器 ID(用于标识,如 own、29)' })\n );\n\n if (config.servers[id]) {\n clack.log.warn(`服务器 \"${id}\" 已存在`);\n return;\n }\n\n const serverConfig = await collectServerConfig();\n config.servers[id] = serverConfig;\n setConfig(config);\n clack.log.success(`服务器 \"${id}\" 已添加`);\n}\n\n/**\n * 交互式编辑服务器\n */\nasync function editServer(): Promise<void> {\n const config = getConfig();\n const ids = Object.keys(config.servers);\n if (ids.length === 0) {\n clack.log.warn('没有已配置的服务器');\n return;\n }\n\n const id = handleCancel<string>(\n await clack.select({\n message: '选择要编辑的服务器',\n options: ids.map((id) => ({ value: id, label: `${id} (${config.servers[id].host})` })),\n })\n );\n\n const existing = config.servers[id];\n const serverConfig = await collectServerConfig(existing);\n config.servers[id] = serverConfig;\n setConfig(config);\n clack.log.success(`服务器 \"${id}\" 已更新`);\n}\n\n/**\n * 删除服务器\n */\nasync function deleteServer(): Promise<void> {\n const config = getConfig();\n const ids = Object.keys(config.servers);\n if (ids.length === 0) {\n clack.log.warn('没有已配置的服务器');\n return;\n }\n\n const id = handleCancel<string>(\n await clack.select({\n message: '选择要删除的服务器',\n options: ids.map((id) => ({ value: id, label: `${id} (${config.servers[id].host})` })),\n })\n );\n\n const confirm = handleCancel<boolean>(\n await clack.confirm({ message: `确定删除服务器 \"${id}\"?` })\n );\n if (!confirm) {\n clack.log.info('已取消');\n return;\n }\n\n delete config.servers[id];\n setConfig(config);\n clack.log.success(`服务器 \"${id}\" 已删除`);\n}\n\n/**\n * 测试服务器连接\n */\nasync function doTestConnection(): Promise<void> {\n const config = getConfig();\n const ids = Object.keys(config.servers);\n if (ids.length === 0) {\n clack.log.warn('没有已配置的服务器');\n return;\n }\n\n const id = handleCancel<string>(\n await clack.select({\n message: '选择要测试的服务器',\n options: ids.map((id) => ({ value: id, label: `${id} (${config.servers[id].host})` })),\n })\n );\n\n const spinner = clack.spinner();\n spinner.start(`正在连接 ${id}...`);\n const ok = await testConnection(config.servers[id]);\n if (ok) {\n spinner.stop(`${id} 连接成功`);\n } else {\n spinner.stop(`${id} 连接失败`);\n }\n}\n\n/**\n * 列出所有服务器\n */\nfunction listServers(): void {\n const config = getConfig();\n const ids = Object.keys(config.servers);\n if (ids.length === 0) {\n clack.log.info('没有已配置的服务器');\n return;\n }\n\n const lines = ids.map((id) => {\n const s = config.servers[id];\n const auth = s.privateKeyPath ? '私钥' : '密码';\n return ` ${pc.cyan(id)}: ${s.host}:${s.port} (${s.username}, ${auth})`;\n });\n noteBox(lines.join('\\n'), '服务器列表');\n}\n\n/**\n * 收集服务器配置(交互式)\n */\nasync function collectServerConfig(existing?: ServerConfig): Promise<ServerConfig> {\n const host = handleCancel<string>(\n await clack.text({\n message: '主机地址',\n initialValue: existing?.host,\n })\n );\n\n const portStr = handleCancel<string>(\n await clack.text({\n message: '端口',\n initialValue: existing?.port?.toString() ?? '22',\n validate: (value) => {\n const parsed = parseInt(value, 10);\n if (isNaN(parsed) || parsed < 1 || parsed > 65535) {\n return '请输入有效的端口号 (1-65535)';\n }\n },\n })\n );\n const port = parseInt(portStr, 10);\n\n const username = handleCancel<string>(\n await clack.text({\n message: '用户名',\n initialValue: existing?.username ?? 'root',\n })\n );\n\n const authType = handleCancel<'password' | 'privateKey'>(\n await clack.select({\n message: '认证方式',\n options: [\n { value: 'password', label: '密码' },\n { value: 'privateKey', label: '私钥' },\n ],\n initialValue: existing?.privateKeyPath ? 'privateKey' : 'password',\n })\n );\n\n if (authType === 'password') {\n const password = handleCancel<string>(\n await clack.password({\n message: '密码',\n })\n );\n return { host, port, username, password };\n } else {\n const privateKeyPath = handleCancel<string>(\n await clack.text({\n message: '私钥文件路径',\n initialValue: existing?.privateKeyPath ?? '~/.ssh/id_rsa',\n })\n );\n const passphrase = handleCancel<string>(\n await clack.password({\n message: '私钥密码(如有)',\n })\n );\n return {\n host,\n port,\n username,\n privateKeyPath,\n passphrase: passphrase || undefined,\n };\n }\n}\n\n/**\n * 创建 server 子命令\n */\nexport function createServerCommand(): Command {\n const cmd = new Command('server');\n cmd.description('服务器管理');\n\n cmd\n .command('add')\n .description('添加服务器')\n .action(async () => {\n clack.intro('添加服务器');\n await addServer();\n clack.outro('完成');\n });\n\n cmd\n .command('edit')\n .description('编辑服务器')\n .action(async () => {\n clack.intro('编辑服务器');\n await editServer();\n clack.outro('完成');\n });\n\n cmd\n .command('delete')\n .description('删除服务器')\n .action(async () => {\n clack.intro('删除服务器');\n await deleteServer();\n clack.outro('完成');\n });\n\n cmd\n .command('test')\n .description('测试服务器连接')\n .action(async () => {\n clack.intro('测试连接');\n await doTestConnection();\n clack.outro('完成');\n });\n\n cmd\n .command('list')\n .description('列出所有服务器')\n .action(() => {\n clack.intro('服务器列表');\n listServers();\n clack.outro('完成');\n });\n\n return cmd;\n}\n","import { Command } from 'commander';\nimport * as clack from '@clack/prompts';\nimport { resolve } from 'path';\nimport { runApp, runProject, showConfig } from '../src/app.js';\nimport { runInit } from '../src/init/app.js';\nimport { createServerCommand } from '../src/server/app.js';\n\n/**\n * 创建 deploy 命令程序\n */\nexport function createDeployProgram(): Command {\n const program = new Command();\n program\n .name('deploy')\n .description('部署工具')\n .version('1.0.0')\n .action(async () => {\n clack.intro('deploy');\n try {\n await runApp();\n } catch (err) {\n clack.log.error(`执行失败: ${err}`);\n process.exit(1);\n }\n clack.outro('再见');\n });\n\n program\n .command('init')\n .description('交互式初始化项目部署配置')\n .action(async () => {\n clack.intro('deploy init');\n try {\n await runInit(resolve('.'));\n } catch (err) {\n clack.log.error(`执行失败: ${err}`);\n process.exit(1);\n }\n });\n\n program\n .command('run <project>')\n .description('直接运行指定项目')\n .option('-s, --server <server>', '覆盖项目默认服务器')\n .action(async (project: string, options: { server?: string }) => {\n clack.intro('deploy run');\n try {\n await runProject(project, options.server);\n } catch (err) {\n clack.log.error(`执行失败: ${err}`);\n process.exit(1);\n }\n });\n\n program\n .command('config')\n .description('查看配置')\n .action(() => {\n showConfig();\n });\n\n // 服务器管理子命令\n program.addCommand(createServerCommand());\n\n return program;\n}\n","#!/usr/bin/env node\nimport { createDeployProgram } from './shared.js';\ncreateDeployProgram().parse();\n"]}