@mindbase/deploy 1.0.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 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1,245 @@
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
@@ -0,0 +1 @@
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"]}
@@ -0,0 +1,626 @@
1
+ import * as clack2 from '@clack/prompts';
2
+ import pc from 'picocolors';
3
+ import { noteBox } from '@mindbase/cli-ui';
4
+ import { existsSync, writeFileSync, readFileSync, mkdirSync } from 'fs';
5
+ import { join, resolve } from 'path';
6
+ import { homedir } from 'os';
7
+ import SftpClient from 'ssh2-sftp-client';
8
+ import spawn from 'cross-spawn';
9
+
10
+ // src/app.ts
11
+ var defaultConfig = {
12
+ servers: {},
13
+ projects: {}
14
+ };
15
+ var CONFIG_DIR = join(homedir(), ".mindbase");
16
+ var CONFIG_PATH = join(CONFIG_DIR, "deploy.json");
17
+ function ensureConfigDir() {
18
+ if (!existsSync(CONFIG_DIR)) {
19
+ mkdirSync(CONFIG_DIR, { recursive: true });
20
+ }
21
+ }
22
+ function getConfig() {
23
+ if (!existsSync(CONFIG_PATH)) {
24
+ ensureConfigDir();
25
+ writeFileSync(CONFIG_PATH, JSON.stringify(defaultConfig, null, 2), "utf-8");
26
+ return structuredClone(defaultConfig);
27
+ }
28
+ try {
29
+ const content = readFileSync(CONFIG_PATH, "utf-8");
30
+ return JSON.parse(content);
31
+ } catch {
32
+ return structuredClone(defaultConfig);
33
+ }
34
+ }
35
+ function setConfig(config) {
36
+ ensureConfigDir();
37
+ writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
38
+ }
39
+ function getConfigPath() {
40
+ return CONFIG_PATH;
41
+ }
42
+ function resolvePath(path) {
43
+ if (path.startsWith("~/")) {
44
+ return `${homedir()}${path.slice(1)}`;
45
+ }
46
+ return path;
47
+ }
48
+ function buildConnectConfig(config) {
49
+ const base = {
50
+ host: config.host,
51
+ port: config.port,
52
+ username: config.username
53
+ };
54
+ if (config.privateKeyPath) {
55
+ return {
56
+ ...base,
57
+ privateKey: readFileSync(resolvePath(config.privateKeyPath)),
58
+ passphrase: config.passphrase || void 0
59
+ };
60
+ }
61
+ return {
62
+ ...base,
63
+ password: config.password
64
+ };
65
+ }
66
+ async function connectSftp(name, config) {
67
+ const sftp = new SftpClient(name);
68
+ await sftp.connect(buildConnectConfig(config));
69
+ return sftp;
70
+ }
71
+ async function testConnection(config) {
72
+ const sftp = new SftpClient("test");
73
+ try {
74
+ await sftp.connect(buildConnectConfig(config));
75
+ return true;
76
+ } catch {
77
+ return false;
78
+ } finally {
79
+ await sftp.end();
80
+ }
81
+ }
82
+
83
+ // src/script/parser.ts
84
+ function resolveVariables(template, project) {
85
+ const vars = {
86
+ localDir: project.localDir,
87
+ remoteDir: project.remoteDir,
88
+ projectName: project.name
89
+ };
90
+ return template.replace(/\$\{(\w+)\}/g, (match, key) => {
91
+ return vars[key] !== void 0 ? vars[key] : match;
92
+ });
93
+ }
94
+
95
+ // src/script/runner.ts
96
+ function runLocal(cmd, cwd) {
97
+ return new Promise((resolve2, reject) => {
98
+ const cp = spawn(cmd, [], {
99
+ cwd,
100
+ detached: false,
101
+ shell: true
102
+ });
103
+ if (cp.stdout) cp.stdout.pipe(process.stdout);
104
+ if (cp.stderr) cp.stderr.pipe(process.stderr);
105
+ cp.on("close", (code) => {
106
+ if (code === 0) {
107
+ resolve2();
108
+ } else {
109
+ reject(new Error(`\u672C\u5730\u547D\u4EE4\u6267\u884C\u5931\u8D25 (code=${code}): ${cmd}`));
110
+ }
111
+ });
112
+ cp.on("error", (err) => {
113
+ reject(err);
114
+ });
115
+ });
116
+ }
117
+ async function runStep(step, project, sftp) {
118
+ switch (step.type) {
119
+ case "log": {
120
+ const msg = resolveVariables(step.message, project);
121
+ console.log(msg);
122
+ break;
123
+ }
124
+ case "local": {
125
+ const cmd = resolveVariables(step.cmd, project);
126
+ const cwd = step.cwd ? resolveVariables(step.cwd, project) : void 0;
127
+ await runLocal(cmd, cwd);
128
+ break;
129
+ }
130
+ case "remote": {
131
+ const { execRemote } = await import('./executor-OQZFWKVP.js');
132
+ const cmd = resolveVariables(step.cmd, project);
133
+ await execRemote(sftp, cmd);
134
+ break;
135
+ }
136
+ case "uploadDir": {
137
+ const local = resolveVariables(step.local, project);
138
+ const remote = resolveVariables(step.remote, project);
139
+ await sftp.mkdir(remote, true);
140
+ await sftp.uploadDir(local, remote, { useFastput: true });
141
+ break;
142
+ }
143
+ case "uploadFile": {
144
+ const local = resolveVariables(step.local, project);
145
+ const remote = resolveVariables(step.remote, project);
146
+ await sftp.fastPut(local, remote);
147
+ break;
148
+ }
149
+ case "downloadDir": {
150
+ const remote = resolveVariables(step.remote, project);
151
+ const local = resolveVariables(step.local, project);
152
+ await sftp.downloadDir(remote, local);
153
+ break;
154
+ }
155
+ case "downloadFile": {
156
+ const remote = resolveVariables(step.remote, project);
157
+ const local = resolveVariables(step.local, project);
158
+ await sftp.fastGet(remote, local);
159
+ break;
160
+ }
161
+ }
162
+ }
163
+ async function runSteps(steps, project, sftp) {
164
+ for (const step of steps) {
165
+ await runStep(step, project, sftp);
166
+ }
167
+ }
168
+
169
+ // src/app.ts
170
+ function handleCancel(result) {
171
+ if (typeof result === "symbol" && clack2.isCancel(result)) {
172
+ clack2.cancel("\u64CD\u4F5C\u5DF2\u53D6\u6D88");
173
+ process.exit(0);
174
+ }
175
+ return result;
176
+ }
177
+ function showConfig() {
178
+ clack2.intro("\u914D\u7F6E\u4FE1\u606F");
179
+ const config = getConfig();
180
+ const path = getConfigPath();
181
+ noteBox(`\u914D\u7F6E\u6587\u4EF6: ${path}
182
+
183
+ ${JSON.stringify(config, null, 2)}`, "\u5F53\u524D\u914D\u7F6E");
184
+ clack2.outro("\u5B8C\u6210");
185
+ }
186
+ async function runProject(projectId, serverOverride) {
187
+ const config = getConfig();
188
+ const project = config.projects[projectId];
189
+ if (!project) {
190
+ clack2.log.error(`\u9879\u76EE "${projectId}" \u4E0D\u5B58\u5728`);
191
+ const available = Object.keys(config.projects);
192
+ if (available.length > 0) {
193
+ clack2.log.info(`\u53EF\u7528\u9879\u76EE: ${available.join(", ")}`);
194
+ }
195
+ process.exit(1);
196
+ }
197
+ const serverId = serverOverride || project.server;
198
+ const serverConfig = config.servers[serverId];
199
+ if (!serverConfig) {
200
+ clack2.log.error(`\u670D\u52A1\u5668 "${serverId}" \u4E0D\u5B58\u5728`);
201
+ process.exit(1);
202
+ }
203
+ clack2.intro(`\u90E8\u7F72 ${pc.cyan(project.name)} \u2192 ${pc.cyan(serverId)}`);
204
+ const spinner3 = clack2.spinner();
205
+ spinner3.start("\u8FDE\u63A5\u670D\u52A1\u5668...");
206
+ const sftp = await connectSftp(projectId, serverConfig);
207
+ spinner3.stop("\u5DF2\u8FDE\u63A5");
208
+ try {
209
+ await runSteps(project.steps, project, sftp);
210
+ clack2.log.success(`${project.name} \u90E8\u7F72\u5B8C\u6210`);
211
+ } catch (err) {
212
+ clack2.log.error(`\u90E8\u7F72\u5931\u8D25: ${err}`);
213
+ } finally {
214
+ await sftp.end();
215
+ }
216
+ clack2.outro("\u5B8C\u6210");
217
+ }
218
+ async function runApp() {
219
+ const config = getConfig();
220
+ const serverIds = Object.keys(config.servers);
221
+ const projectIds = Object.keys(config.projects);
222
+ if (serverIds.length === 0) {
223
+ clack2.log.error("\u6CA1\u6709\u5DF2\u914D\u7F6E\u7684\u670D\u52A1\u5668\uFF0C\u8BF7\u5148\u8FD0\u884C deploy server add");
224
+ return;
225
+ }
226
+ if (projectIds.length === 0) {
227
+ clack2.log.error("\u6CA1\u6709\u5DF2\u914D\u7F6E\u7684\u9879\u76EE\uFF0C\u8BF7\u5148\u8FD0\u884C deploy init");
228
+ return;
229
+ }
230
+ clack2.intro("deploy");
231
+ const serverId = handleCancel(
232
+ await clack2.select({
233
+ message: "\u9009\u62E9\u76EE\u6807\u670D\u52A1\u5668",
234
+ options: serverIds.map((id) => ({
235
+ value: id,
236
+ label: `${id} (${config.servers[id].host}:${config.servers[id].port})`
237
+ }))
238
+ })
239
+ );
240
+ const filteredProjects = projectIds.filter(
241
+ (id) => config.projects[id].server === serverId
242
+ );
243
+ const selectableProjects = filteredProjects.length > 0 ? filteredProjects : projectIds;
244
+ const selectedProjects = handleCancel(
245
+ await clack2.multiselect({
246
+ message: "\u9009\u62E9\u8981\u90E8\u7F72\u7684\u9879\u76EE\uFF08\u7A7A\u683C\u5207\u6362\uFF0Ca \u5168\u9009\uFF09",
247
+ options: selectableProjects.map((id) => ({
248
+ value: id,
249
+ label: `${config.projects[id].name} (${id})`
250
+ })),
251
+ required: true
252
+ })
253
+ );
254
+ const spinner3 = clack2.spinner();
255
+ spinner3.start(`\u8FDE\u63A5 ${serverId}...`);
256
+ const serverConfig = config.servers[serverId];
257
+ const sftp = await connectSftp("deploy", serverConfig);
258
+ spinner3.stop("\u5DF2\u8FDE\u63A5");
259
+ try {
260
+ for (const projectId of selectedProjects) {
261
+ const project = config.projects[projectId];
262
+ const effectiveProject = {
263
+ ...project,
264
+ server: serverId
265
+ };
266
+ clack2.log.step(`\u5F00\u59CB\u90E8\u7F72 ${pc.cyan(project.name)}`);
267
+ try {
268
+ await runSteps(project.steps, effectiveProject, sftp);
269
+ clack2.log.success(`${project.name} \u90E8\u7F72\u5B8C\u6210`);
270
+ } catch (err) {
271
+ clack2.log.error(`${project.name} \u90E8\u7F72\u5931\u8D25: ${err}`);
272
+ const continueDeploy = handleCancel(
273
+ await clack2.confirm({ message: "\u662F\u5426\u7EE7\u7EED\u90E8\u7F72\u4E0B\u4E00\u4E2A\u9879\u76EE\uFF1F" })
274
+ );
275
+ if (!continueDeploy) break;
276
+ }
277
+ }
278
+ } finally {
279
+ await sftp.end();
280
+ }
281
+ clack2.outro("\u5168\u90E8\u5B8C\u6210");
282
+ }
283
+ function handleCancel2(result) {
284
+ if (typeof result === "symbol" && clack2.isCancel(result)) {
285
+ clack2.cancel("\u64CD\u4F5C\u5DF2\u53D6\u6D88");
286
+ process.exit(0);
287
+ }
288
+ return result;
289
+ }
290
+ function formatStepSummary(step) {
291
+ switch (step.type) {
292
+ case "log":
293
+ return `log: ${step.message}`;
294
+ case "local":
295
+ return step.cwd ? `local: ${step.cmd} (cwd: ${step.cwd})` : `local: ${step.cmd}`;
296
+ case "remote":
297
+ return `remote: ${step.cmd}`;
298
+ case "uploadDir":
299
+ return `uploadDir: ${step.local} \u2192 ${step.remote}`;
300
+ case "uploadFile":
301
+ return `uploadFile: ${step.local} \u2192 ${step.remote}`;
302
+ case "downloadDir":
303
+ return `downloadDir: ${step.remote} \u2192 ${step.local}`;
304
+ case "downloadFile":
305
+ return `downloadFile: ${step.remote} \u2192 ${step.local}`;
306
+ }
307
+ }
308
+ function showStepsSummary(steps) {
309
+ const lines = steps.map((s, i) => `${i + 1}. ${formatStepSummary(s)}`).join("\n");
310
+ noteBox(lines, `\u5DF2\u914D\u7F6E\u6B65\u9AA4 (${steps.length})`);
311
+ }
312
+ var VARS_REFERENCE = [
313
+ "\u53EF\u7528\u53D8\u91CF:",
314
+ " ${localDir} \u9879\u76EE\u672C\u5730\u76EE\u5F55",
315
+ " ${remoteDir} \u9879\u76EE\u8FDC\u7A0B\u76EE\u5F55",
316
+ " ${projectName} \u9879\u76EE\u540D\u79F0"
317
+ ].join("\n");
318
+ var STEP_EXAMPLES = {
319
+ log: [
320
+ "\u793A\u4F8B:",
321
+ " \u5F00\u59CB\u53D1\u5E03...",
322
+ " \u7F16\u8BD1\u5B8C\u6210"
323
+ ].join("\n"),
324
+ local: [
325
+ VARS_REFERENCE,
326
+ "",
327
+ "\u793A\u4F8B:",
328
+ " npm run build",
329
+ " pnpm install",
330
+ "",
331
+ "cwd: \u5DE5\u4F5C\u76EE\u5F55\uFF0C\u7559\u7A7A\u4F7F\u7528\u9ED8\u8BA4\uFF0C\u53EF\u7528 ${localDir}"
332
+ ].join("\n"),
333
+ remote: [
334
+ VARS_REFERENCE,
335
+ "",
336
+ "\u793A\u4F8B:",
337
+ " cd ${remoteDir} && rm -rf ./*",
338
+ " mkdir -p ${remoteDir}src",
339
+ " pm2 restart myapp"
340
+ ].join("\n"),
341
+ uploadDir: [
342
+ VARS_REFERENCE,
343
+ "",
344
+ "\u793A\u4F8B:",
345
+ " \u672C\u5730: ${localDir}/dist",
346
+ " \u8FDC\u7AEF: ${remoteDir}"
347
+ ].join("\n"),
348
+ uploadFile: [
349
+ VARS_REFERENCE,
350
+ "",
351
+ "\u793A\u4F8B:",
352
+ " \u672C\u5730: ${localDir}/package.json",
353
+ " \u8FDC\u7AEF: ${remoteDir}package.json"
354
+ ].join("\n"),
355
+ downloadDir: [
356
+ VARS_REFERENCE,
357
+ "",
358
+ "\u793A\u4F8B:",
359
+ " \u8FDC\u7AEF: ${remoteDir}backup/",
360
+ " \u672C\u5730: ${localDir}/backup"
361
+ ].join("\n"),
362
+ downloadFile: [
363
+ VARS_REFERENCE,
364
+ "",
365
+ "\u793A\u4F8B:",
366
+ " \u8FDC\u7AEF: ${remoteDir}config.json",
367
+ " \u672C\u5730: ${localDir}/config.json.bak"
368
+ ].join("\n")
369
+ };
370
+ var STEP_TYPE_OPTIONS = [
371
+ { value: "log", label: "\u65E5\u5FD7\u8F93\u51FA", hint: "\u8F93\u51FA\u4FE1\u606F" },
372
+ { value: "local", label: "\u672C\u5730\u547D\u4EE4", hint: "\u5728\u672C\u5730\u6267\u884C shell \u547D\u4EE4" },
373
+ { value: "remote", label: "\u8FDC\u7A0B\u547D\u4EE4", hint: "\u5728\u670D\u52A1\u5668\u6267\u884C shell \u547D\u4EE4" },
374
+ { value: "uploadDir", label: "\u4E0A\u4F20\u76EE\u5F55", hint: "\u4E0A\u4F20\u6574\u4E2A\u76EE\u5F55\u5230\u670D\u52A1\u5668" },
375
+ { value: "uploadFile", label: "\u4E0A\u4F20\u6587\u4EF6", hint: "\u4E0A\u4F20\u5355\u4E2A\u6587\u4EF6\u5230\u670D\u52A1\u5668" },
376
+ { value: "downloadDir", label: "\u4E0B\u8F7D\u76EE\u5F55\uFF08\u5907\u4EFD\uFF09", hint: "\u4ECE\u670D\u52A1\u5668\u4E0B\u8F7D\u6574\u4E2A\u76EE\u5F55" },
377
+ { value: "downloadFile", label: "\u4E0B\u8F7D\u6587\u4EF6\uFF08\u5907\u4EFD\uFF09", hint: "\u4ECE\u670D\u52A1\u5668\u4E0B\u8F7D\u5355\u4E2A\u6587\u4EF6" }
378
+ ];
379
+ function getDefaultProjectName(cwd) {
380
+ const pkgPath = resolve(cwd, "package.json");
381
+ if (existsSync(pkgPath)) {
382
+ try {
383
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
384
+ if (pkg.name) return pkg.name;
385
+ } catch {
386
+ }
387
+ }
388
+ return "";
389
+ }
390
+ async function browseRemoteDir(sftp, currentPath) {
391
+ while (true) {
392
+ let entries;
393
+ try {
394
+ entries = await sftp.list(currentPath);
395
+ } catch {
396
+ return currentPath;
397
+ }
398
+ const dirOptions = entries.filter((e) => e.type === "d").map((e) => ({ value: e.name, label: `\u{1F4C1} ${e.name}` }));
399
+ const choices = [
400
+ { value: "__confirm__", label: pc.green(`\u2713 \u4F7F\u7528\u5F53\u524D\u76EE\u5F55: ${currentPath}`) },
401
+ { value: "__manual__", label: pc.yellow("\u270E \u624B\u52A8\u8F93\u5165\u8DEF\u5F84") },
402
+ ...dirOptions
403
+ ];
404
+ const selected = handleCancel2(
405
+ await clack2.select({
406
+ message: `\u6D4F\u89C8\u8FDC\u7AEF\u76EE\u5F55: ${currentPath}`,
407
+ options: choices
408
+ })
409
+ );
410
+ if (selected === "__confirm__") {
411
+ return currentPath;
412
+ }
413
+ if (selected === "__manual__") {
414
+ const input = handleCancel2(
415
+ await clack2.text({
416
+ message: "\u8F93\u5165\u8FDC\u7AEF\u76EE\u5F55\u8DEF\u5F84",
417
+ initialValue: currentPath
418
+ })
419
+ );
420
+ currentPath = input;
421
+ continue;
422
+ }
423
+ currentPath = currentPath.endsWith("/") ? `${currentPath}${selected}` : `${currentPath}/${selected}`;
424
+ }
425
+ }
426
+ async function collectStep() {
427
+ const stepType = handleCancel2(
428
+ await clack2.select({
429
+ message: "\u9009\u62E9\u6B65\u9AA4\u7C7B\u578B",
430
+ options: STEP_TYPE_OPTIONS
431
+ })
432
+ );
433
+ noteBox(STEP_EXAMPLES[stepType], "\u53C2\u8003\uFF08\u53EF\u76F4\u63A5\u590D\u5236\u53D8\u91CF\uFF09");
434
+ switch (stepType) {
435
+ case "log": {
436
+ const message = handleCancel2(
437
+ await clack2.text({ message: "\u65E5\u5FD7\u4FE1\u606F" })
438
+ );
439
+ return { type: "log", message };
440
+ }
441
+ case "local": {
442
+ const cmd = handleCancel2(
443
+ await clack2.text({ message: "\u547D\u4EE4" })
444
+ );
445
+ const cwd = handleCancel2(
446
+ await clack2.text({
447
+ message: "\u5DE5\u4F5C\u76EE\u5F55\uFF08\u53EF\u9009\uFF0C\u7559\u7A7A\u4F7F\u7528\u9ED8\u8BA4\uFF09",
448
+ placeholder: "${localDir}"
449
+ })
450
+ );
451
+ const step = { type: "local", cmd };
452
+ if (cwd) step.cwd = cwd;
453
+ return step;
454
+ }
455
+ case "remote": {
456
+ const cmd = handleCancel2(
457
+ await clack2.text({ message: "\u8FDC\u7A0B\u547D\u4EE4" })
458
+ );
459
+ return { type: "remote", cmd };
460
+ }
461
+ case "uploadDir": {
462
+ const local = handleCancel2(
463
+ await clack2.text({ message: "\u672C\u5730\u76EE\u5F55", placeholder: "${localDir}/dist" })
464
+ );
465
+ const remote = handleCancel2(
466
+ await clack2.text({ message: "\u8FDC\u7AEF\u76EE\u5F55", placeholder: "${remoteDir}" })
467
+ );
468
+ return { type: "uploadDir", local, remote };
469
+ }
470
+ case "uploadFile": {
471
+ const local = handleCancel2(
472
+ await clack2.text({ message: "\u672C\u5730\u6587\u4EF6\u8DEF\u5F84" })
473
+ );
474
+ const remote = handleCancel2(
475
+ await clack2.text({ message: "\u8FDC\u7AEF\u6587\u4EF6\u8DEF\u5F84" })
476
+ );
477
+ return { type: "uploadFile", local, remote };
478
+ }
479
+ case "downloadDir": {
480
+ const remote = handleCancel2(
481
+ await clack2.text({ message: "\u8FDC\u7AEF\u76EE\u5F55" })
482
+ );
483
+ const local = handleCancel2(
484
+ await clack2.text({ message: "\u672C\u5730\u4FDD\u5B58\u76EE\u5F55" })
485
+ );
486
+ return { type: "downloadDir", remote, local };
487
+ }
488
+ case "downloadFile": {
489
+ const remote = handleCancel2(
490
+ await clack2.text({ message: "\u8FDC\u7AEF\u6587\u4EF6\u8DEF\u5F84" })
491
+ );
492
+ const local = handleCancel2(
493
+ await clack2.text({ message: "\u672C\u5730\u4FDD\u5B58\u8DEF\u5F84" })
494
+ );
495
+ return { type: "downloadFile", remote, local };
496
+ }
497
+ }
498
+ }
499
+ async function runInit(cwd) {
500
+ clack2.intro("deploy init");
501
+ const deployConfig = getConfig();
502
+ const defaultName = getDefaultProjectName(cwd);
503
+ const projectId = handleCancel2(
504
+ await clack2.text({
505
+ message: "\u9879\u76EE ID\uFF08\u7528\u4E8E\u6807\u8BC6\uFF09",
506
+ initialValue: defaultName
507
+ })
508
+ );
509
+ if (deployConfig.projects[projectId]) {
510
+ clack2.log.warn(`\u9879\u76EE "${projectId}" \u5DF2\u5B58\u5728\uFF0C\u5C06\u8986\u76D6\u914D\u7F6E`);
511
+ }
512
+ const name = handleCancel2(
513
+ await clack2.text({
514
+ message: "\u9879\u76EE\u663E\u793A\u540D\u79F0",
515
+ initialValue: projectId
516
+ })
517
+ );
518
+ const serverIds = Object.keys(deployConfig.servers);
519
+ if (serverIds.length === 0) {
520
+ clack2.log.error("\u6CA1\u6709\u5DF2\u914D\u7F6E\u7684\u670D\u52A1\u5668\uFF0C\u8BF7\u5148\u8FD0\u884C deploy server add \u6DFB\u52A0\u670D\u52A1\u5668");
521
+ clack2.outro("\u8BF7\u5148\u914D\u7F6E\u670D\u52A1\u5668");
522
+ return;
523
+ }
524
+ const serverId = handleCancel2(
525
+ await clack2.select({
526
+ message: "\u9009\u62E9\u76EE\u6807\u670D\u52A1\u5668",
527
+ options: serverIds.map((id) => ({
528
+ value: id,
529
+ label: `${id} (${deployConfig.servers[id].host})`
530
+ }))
531
+ })
532
+ );
533
+ const localDir = handleCancel2(
534
+ await clack2.text({
535
+ message: "\u672C\u5730\u76EE\u5F55\uFF08\u7EDD\u5BF9\u8DEF\u5F84\uFF09",
536
+ initialValue: cwd
537
+ })
538
+ );
539
+ const spinner3 = clack2.spinner();
540
+ spinner3.start("\u8FDE\u63A5\u670D\u52A1\u5668...");
541
+ let sftp;
542
+ try {
543
+ sftp = await connectSftp("init", deployConfig.servers[serverId]);
544
+ spinner3.stop("\u5DF2\u8FDE\u63A5");
545
+ } catch (err) {
546
+ spinner3.stop("\u8FDE\u63A5\u5931\u8D25");
547
+ clack2.log.error(`\u65E0\u6CD5\u8FDE\u63A5\u670D\u52A1\u5668: ${err}`);
548
+ clack2.outro("\u8FDE\u63A5\u5931\u8D25");
549
+ return;
550
+ }
551
+ let remoteDir;
552
+ try {
553
+ const browseChoice = handleCancel2(
554
+ await clack2.select({
555
+ message: "\u8FDC\u7AEF\u76EE\u5F55\u8BBE\u7F6E\u65B9\u5F0F",
556
+ options: [
557
+ { value: "browse", label: "\u6D4F\u89C8\u670D\u52A1\u5668\u76EE\u5F55" },
558
+ { value: "manual", label: "\u624B\u52A8\u8F93\u5165\u8DEF\u5F84" }
559
+ ]
560
+ })
561
+ );
562
+ if (browseChoice === "browse") {
563
+ remoteDir = await browseRemoteDir(sftp, "/");
564
+ } else {
565
+ remoteDir = handleCancel2(
566
+ await clack2.text({
567
+ message: "\u8FDC\u7AEF\u76EE\u5F55\u8DEF\u5F84",
568
+ placeholder: "/appliction/my-app/"
569
+ })
570
+ );
571
+ const confirmMkdir = handleCancel2(
572
+ await clack2.confirm({ message: `\u662F\u5426\u81EA\u52A8\u521B\u5EFA\u76EE\u5F55 ${remoteDir}\uFF08\u5982\u679C\u4E0D\u5B58\u5728\uFF09\uFF1F` })
573
+ );
574
+ if (confirmMkdir) {
575
+ await sftp.mkdir(remoteDir, true);
576
+ clack2.log.success(`\u76EE\u5F55\u5DF2\u521B\u5EFA: ${remoteDir}`);
577
+ }
578
+ }
579
+ } finally {
580
+ await sftp.end();
581
+ }
582
+ const steps = [];
583
+ clack2.log.info("\u5F00\u59CB\u914D\u7F6E\u90E8\u7F72\u6B65\u9AA4\uFF08\u6BCF\u4E2A\u6B65\u9AA4\u4F1A\u5C55\u793A\u53C2\u8003\u548C\u53EF\u7528\u53D8\u91CF\uFF09");
584
+ while (true) {
585
+ if (steps.length > 0) {
586
+ const action = handleCancel2(
587
+ await clack2.select({
588
+ message: `\u5DF2\u914D\u7F6E ${steps.length} \u4E2A\u6B65\u9AA4`,
589
+ options: [
590
+ { value: "add", label: "\u6DFB\u52A0\u6B65\u9AA4" },
591
+ { value: "done", label: "\u5B8C\u6210\u914D\u7F6E" }
592
+ ]
593
+ })
594
+ );
595
+ if (action === "done") break;
596
+ }
597
+ const step = await collectStep();
598
+ if (step) {
599
+ steps.push(step);
600
+ showStepsSummary(steps);
601
+ }
602
+ }
603
+ const projectConfig = {
604
+ name,
605
+ localDir: resolve(localDir),
606
+ server: serverId,
607
+ remoteDir,
608
+ steps
609
+ };
610
+ noteBox(JSON.stringify(projectConfig, null, 2), "\u9879\u76EE\u914D\u7F6E\u9884\u89C8");
611
+ const confirmSave = handleCancel2(
612
+ await clack2.confirm({ message: "\u786E\u8BA4\u4FDD\u5B58\u6B64\u914D\u7F6E\uFF1F" })
613
+ );
614
+ if (confirmSave) {
615
+ deployConfig.projects[projectId] = projectConfig;
616
+ setConfig(deployConfig);
617
+ clack2.log.success(`\u9879\u76EE "${projectId}" \u914D\u7F6E\u5DF2\u4FDD\u5B58`);
618
+ } else {
619
+ clack2.log.info("\u5DF2\u53D6\u6D88\u4FDD\u5B58");
620
+ }
621
+ clack2.outro("\u5B8C\u6210");
622
+ }
623
+
624
+ export { getConfig, runApp, runInit, runProject, setConfig, showConfig, testConnection };
625
+ //# sourceMappingURL=chunk-YB2FK4HH.js.map
626
+ //# sourceMappingURL=chunk-YB2FK4HH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config/manager.ts","../src/ssh/client.ts","../src/script/parser.ts","../src/script/runner.ts","../src/app.ts","../src/init/app.ts"],"names":["homedir","readFileSync","resolve","clack","spinner","handleCancel","noteBox","existsSync","pc"],"mappings":"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","file":"chunk-YB2FK4HH.js","sourcesContent":["import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';\nimport { dirname, join } from 'path';\nimport { homedir } from 'os';\nimport type { DeployConfig } from './schema.js';\n\n/** 默认配置 */\nconst defaultConfig: DeployConfig = {\n servers: {},\n projects: {},\n};\n\n/** 配置文件路径 */\nconst CONFIG_DIR = join(homedir(), '.mindbase');\nconst CONFIG_PATH = join(CONFIG_DIR, 'deploy.json');\n\n/**\n * 确保配置目录存在\n */\nfunction ensureConfigDir(): void {\n if (!existsSync(CONFIG_DIR)) {\n mkdirSync(CONFIG_DIR, { recursive: true });\n }\n}\n\n/**\n * 读取部署配置\n */\nexport function getConfig(): DeployConfig {\n if (!existsSync(CONFIG_PATH)) {\n ensureConfigDir();\n writeFileSync(CONFIG_PATH, JSON.stringify(defaultConfig, null, 2), 'utf-8');\n return structuredClone(defaultConfig);\n }\n\n try {\n const content = readFileSync(CONFIG_PATH, 'utf-8');\n return JSON.parse(content);\n } catch {\n return structuredClone(defaultConfig);\n }\n}\n\n/**\n * 写入部署配置\n */\nexport function setConfig(config: DeployConfig): void {\n ensureConfigDir();\n writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), 'utf-8');\n}\n\n/**\n * 获取配置文件路径\n */\nexport function getConfigPath(): string {\n return CONFIG_PATH;\n}\n","import SftpClient from 'ssh2-sftp-client';\nimport { readFileSync } from 'fs';\nimport { homedir } from 'os';\nimport type { ServerConfig } from '../config/schema.js';\n\n/**\n * 解析路径中的 ~ 为用户目录\n */\nfunction resolvePath(path: string): string {\n if (path.startsWith('~/')) {\n return `${homedir()}${path.slice(1)}`;\n }\n return path;\n}\n\n/**\n * 构建 SSH 连接配置\n */\nfunction buildConnectConfig(config: ServerConfig): Record<string, unknown> {\n const base = {\n host: config.host,\n port: config.port,\n username: config.username,\n };\n\n if (config.privateKeyPath) {\n return {\n ...base,\n privateKey: readFileSync(resolvePath(config.privateKeyPath)),\n passphrase: config.passphrase || undefined,\n };\n }\n\n return {\n ...base,\n password: config.password,\n };\n}\n\n/**\n * 创建并连接 SFTP 客户端\n */\nexport async function connectSftp(name: string, config: ServerConfig): Promise<SftpClient> {\n const sftp = new SftpClient(name);\n await sftp.connect(buildConnectConfig(config));\n return sftp;\n}\n\n/**\n * 测试服务器连接\n */\nexport async function testConnection(config: ServerConfig): Promise<boolean> {\n const sftp = new SftpClient('test');\n try {\n await sftp.connect(buildConnectConfig(config));\n return true;\n } catch {\n return false;\n } finally {\n await sftp.end();\n }\n}\n","import type { ProjectConfig } from '../config/schema.js';\n\n/**\n * 内置变量名\n */\nconst BUILT_IN_VARS = ['localDir', 'remoteDir', 'projectName'] as const;\n\n/**\n * 替换字符串中的 ${varName} 占位符\n */\nexport function resolveVariables(template: string, project: ProjectConfig): string {\n const vars: Record<string, string> = {\n localDir: project.localDir,\n remoteDir: project.remoteDir,\n projectName: project.name,\n };\n\n return template.replace(/\\$\\{(\\w+)\\}/g, (match, key: string) => {\n return vars[key] !== undefined ? vars[key] : match;\n });\n}\n\n/**\n * 获取可用变量列表(用于提示)\n */\nexport function getAvailableVariables(): string[] {\n return [...BUILT_IN_VARS];\n}\n","import type SftpClient from 'ssh2-sftp-client';\nimport spawn from 'cross-spawn';\nimport type { Step, ProjectConfig } from '../config/schema.js';\nimport { resolveVariables } from './parser.js';\n\n/**\n * 在本地执行命令\n */\nfunction runLocal(cmd: string, cwd?: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const cp = spawn(cmd, [], {\n cwd,\n detached: false,\n shell: true,\n });\n if (cp.stdout) cp.stdout.pipe(process.stdout);\n if (cp.stderr) cp.stderr.pipe(process.stderr);\n cp.on('close', (code) => {\n if (code === 0) {\n resolve();\n } else {\n reject(new Error(`本地命令执行失败 (code=${code}): ${cmd}`));\n }\n });\n cp.on('error', (err) => {\n reject(err);\n });\n });\n}\n\n/**\n * 执行单个步骤\n */\nasync function runStep(step: Step, project: ProjectConfig, sftp: SftpClient): Promise<void> {\n switch (step.type) {\n case 'log': {\n const msg = resolveVariables(step.message, project);\n console.log(msg);\n break;\n }\n case 'local': {\n const cmd = resolveVariables(step.cmd, project);\n const cwd = step.cwd ? resolveVariables(step.cwd, project) : undefined;\n await runLocal(cmd, cwd);\n break;\n }\n case 'remote': {\n const { execRemote } = await import('../ssh/executor.js');\n const cmd = resolveVariables(step.cmd, project);\n await execRemote(sftp, cmd);\n break;\n }\n case 'uploadDir': {\n const local = resolveVariables(step.local, project);\n const remote = resolveVariables(step.remote, project);\n await sftp.mkdir(remote, true);\n await sftp.uploadDir(local, remote, { useFastput: true });\n break;\n }\n case 'uploadFile': {\n const local = resolveVariables(step.local, project);\n const remote = resolveVariables(step.remote, project);\n await sftp.fastPut(local, remote);\n break;\n }\n case 'downloadDir': {\n const remote = resolveVariables(step.remote, project);\n const local = resolveVariables(step.local, project);\n await sftp.downloadDir(remote, local);\n break;\n }\n case 'downloadFile': {\n const remote = resolveVariables(step.remote, project);\n const local = resolveVariables(step.local, project);\n await sftp.fastGet(remote, local);\n break;\n }\n }\n}\n\n/**\n * 执行项目的所有步骤\n */\nexport async function runSteps(steps: Step[], project: ProjectConfig, sftp: SftpClient): Promise<void> {\n for (const step of steps) {\n await runStep(step, project, sftp);\n }\n}\n","import * as clack from '@clack/prompts';\nimport pc from 'picocolors';\nimport { noteBox } from '@mindbase/cli-ui';\nimport { getConfig, getConfigPath } from './config/manager.js';\nimport type { DeployConfig, ProjectConfig } from './config/schema.js';\nimport { connectSftp } from './ssh/client.js';\nimport { runSteps } from './script/runner.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 */\nexport function showConfig(): void {\n clack.intro('配置信息');\n const config = getConfig();\n const path = getConfigPath();\n noteBox(`配置文件: ${path}\\n\\n${JSON.stringify(config, null, 2)}`, '当前配置');\n clack.outro('完成');\n}\n\n/**\n * 直接运行指定项目\n */\nexport async function runProject(projectId: string, serverOverride?: string): Promise<void> {\n const config = getConfig();\n\n const project = config.projects[projectId];\n if (!project) {\n clack.log.error(`项目 \"${projectId}\" 不存在`);\n const available = Object.keys(config.projects);\n if (available.length > 0) {\n clack.log.info(`可用项目: ${available.join(', ')}`);\n }\n process.exit(1);\n }\n\n const serverId = serverOverride || project.server;\n const serverConfig = config.servers[serverId];\n if (!serverConfig) {\n clack.log.error(`服务器 \"${serverId}\" 不存在`);\n process.exit(1);\n }\n\n clack.intro(`部署 ${pc.cyan(project.name)} → ${pc.cyan(serverId)}`);\n\n const spinner = clack.spinner();\n spinner.start('连接服务器...');\n const sftp = await connectSftp(projectId, serverConfig);\n spinner.stop('已连接');\n\n try {\n await runSteps(project.steps, project, sftp);\n clack.log.success(`${project.name} 部署完成`);\n } catch (err) {\n clack.log.error(`部署失败: ${err}`);\n } finally {\n await sftp.end();\n }\n\n clack.outro('完成');\n}\n\n/**\n * 交互式主流程:选服务器 → 多选项目 → 依次部署\n */\nexport async function runApp(): Promise<void> {\n const config = getConfig();\n\n // 检查配置\n const serverIds = Object.keys(config.servers);\n const projectIds = Object.keys(config.projects);\n\n if (serverIds.length === 0) {\n clack.log.error('没有已配置的服务器,请先运行 deploy server add');\n return;\n }\n if (projectIds.length === 0) {\n clack.log.error('没有已配置的项目,请先运行 deploy init');\n return;\n }\n\n clack.intro('deploy');\n\n // 选择服务器\n const serverId = handleCancel<string>(\n await clack.select({\n message: '选择目标服务器',\n options: serverIds.map((id) => ({\n value: id,\n label: `${id} (${config.servers[id].host}:${config.servers[id].port})`,\n })),\n })\n );\n\n // 筛选该项目服务器下的项目(或显示全部)\n const filteredProjects = projectIds.filter(\n (id) => config.projects[id].server === serverId\n );\n const selectableProjects = filteredProjects.length > 0 ? filteredProjects : projectIds;\n\n // 多选项目\n const selectedProjects = handleCancel<string[]>(\n await clack.multiselect({\n message: '选择要部署的项目(空格切换,a 全选)',\n options: selectableProjects.map((id) => ({\n value: id,\n label: `${config.projects[id].name} (${id})`,\n })),\n required: true,\n })\n );\n\n // 连接服务器\n const spinner = clack.spinner();\n spinner.start(`连接 ${serverId}...`);\n const serverConfig = config.servers[serverId];\n const sftp = await connectSftp('deploy', serverConfig);\n spinner.stop('已连接');\n\n try {\n // 依次部署选中的项目\n for (const projectId of selectedProjects) {\n const project = config.projects[projectId];\n // 运行时使用选中的服务器覆盖项目默认服务器\n const effectiveProject: ProjectConfig = {\n ...project,\n server: serverId,\n };\n\n clack.log.step(`开始部署 ${pc.cyan(project.name)}`);\n\n try {\n await runSteps(project.steps, effectiveProject, sftp);\n clack.log.success(`${project.name} 部署完成`);\n } catch (err) {\n clack.log.error(`${project.name} 部署失败: ${err}`);\n const continueDeploy = handleCancel<boolean>(\n await clack.confirm({ message: '是否继续部署下一个项目?' })\n );\n if (!continueDeploy) break;\n }\n }\n } finally {\n await sftp.end();\n }\n\n clack.outro('全部完成');\n}\n","import * as clack from '@clack/prompts';\nimport pc from 'picocolors';\nimport { resolve } from 'path';\nimport { existsSync, readFileSync } from 'fs';\nimport { noteBox } from '@mindbase/cli-ui';\nimport { getConfig, setConfig } from '../config/manager.js';\nimport type { Step, StepType, ProjectConfig, ServerConfig } from '../config/schema.js';\nimport { connectSftp } 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/** 格式化单个步骤为摘要行 */\nfunction formatStepSummary(step: Step): string {\n switch (step.type) {\n case 'log':\n return `log: ${step.message}`;\n case 'local':\n return step.cwd\n ? `local: ${step.cmd} (cwd: ${step.cwd})`\n : `local: ${step.cmd}`;\n case 'remote':\n return `remote: ${step.cmd}`;\n case 'uploadDir':\n return `uploadDir: ${step.local} → ${step.remote}`;\n case 'uploadFile':\n return `uploadFile: ${step.local} → ${step.remote}`;\n case 'downloadDir':\n return `downloadDir: ${step.remote} → ${step.local}`;\n case 'downloadFile':\n return `downloadFile: ${step.remote} → ${step.local}`;\n }\n}\n\n/** 展示当前所有已配置步骤 */\nfunction showStepsSummary(steps: Step[]): void {\n const lines = steps.map((s, i) => `${i + 1}. ${formatStepSummary(s)}`).join('\\n');\n noteBox(lines, `已配置步骤 (${steps.length})`);\n}\n\n/** 可用变量参考文本 */\nconst VARS_REFERENCE = [\n '可用变量:',\n ' ${localDir} 项目本地目录',\n ' ${remoteDir} 项目远程目录',\n ' ${projectName} 项目名称',\n].join('\\n');\n\n/** 步骤类型示例 */\nconst STEP_EXAMPLES: Record<StepType, string> = {\n log: [\n '示例:',\n ' 开始发布...',\n ' 编译完成',\n ].join('\\n'),\n local: [\n VARS_REFERENCE,\n '',\n '示例:',\n ' npm run build',\n ' pnpm install',\n '',\n 'cwd: 工作目录,留空使用默认,可用 ${localDir}',\n ].join('\\n'),\n remote: [\n VARS_REFERENCE,\n '',\n '示例:',\n ' cd ${remoteDir} && rm -rf ./*',\n ' mkdir -p ${remoteDir}src',\n ' pm2 restart myapp',\n ].join('\\n'),\n uploadDir: [\n VARS_REFERENCE,\n '',\n '示例:',\n ' 本地: ${localDir}/dist',\n ' 远端: ${remoteDir}',\n ].join('\\n'),\n uploadFile: [\n VARS_REFERENCE,\n '',\n '示例:',\n ' 本地: ${localDir}/package.json',\n ' 远端: ${remoteDir}package.json',\n ].join('\\n'),\n downloadDir: [\n VARS_REFERENCE,\n '',\n '示例:',\n ' 远端: ${remoteDir}backup/',\n ' 本地: ${localDir}/backup',\n ].join('\\n'),\n downloadFile: [\n VARS_REFERENCE,\n '',\n '示例:',\n ' 远端: ${remoteDir}config.json',\n ' 本地: ${localDir}/config.json.bak',\n ].join('\\n'),\n};\n\n/** 步骤类型选项 */\nconst STEP_TYPE_OPTIONS = [\n { value: 'log' as StepType, label: '日志输出', hint: '输出信息' },\n { value: 'local' as StepType, label: '本地命令', hint: '在本地执行 shell 命令' },\n { value: 'remote' as StepType, label: '远程命令', hint: '在服务器执行 shell 命令' },\n { value: 'uploadDir' as StepType, label: '上传目录', hint: '上传整个目录到服务器' },\n { value: 'uploadFile' as StepType, label: '上传文件', hint: '上传单个文件到服务器' },\n { value: 'downloadDir' as StepType, label: '下载目录(备份)', hint: '从服务器下载整个目录' },\n { value: 'downloadFile' as StepType, label: '下载文件(备份)', hint: '从服务器下载单个文件' },\n];\n\n/**\n * 读取 package.json 的 name 字段作为默认项目名\n */\nfunction getDefaultProjectName(cwd: string): string {\n const pkgPath = resolve(cwd, 'package.json');\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n if (pkg.name) return pkg.name;\n } catch { /* 忽略 */ }\n }\n return '';\n}\n\n/**\n * 通过 SFTP 浏览远端目录\n */\nasync function browseRemoteDir(sftp: import('ssh2-sftp-client'), currentPath: string): Promise<string> {\n while (true) {\n let entries: import('ssh2-sftp-client').FileInfo[];\n try {\n entries = await sftp.list(currentPath);\n } catch {\n // 目录不存在,直接返回手动输入的路径\n return currentPath;\n }\n\n const dirOptions = entries\n .filter((e) => e.type === 'd')\n .map((e) => ({ value: e.name, label: `📁 ${e.name}` }));\n\n const choices = [\n { value: '__confirm__', label: pc.green(`✓ 使用当前目录: ${currentPath}`) },\n { value: '__manual__', label: pc.yellow('✎ 手动输入路径') },\n ...dirOptions,\n ];\n\n const selected = handleCancel<string>(\n await clack.select({\n message: `浏览远端目录: ${currentPath}`,\n options: choices,\n })\n );\n\n if (selected === '__confirm__') {\n return currentPath;\n }\n\n if (selected === '__manual__') {\n const input = handleCancel<string>(\n await clack.text({\n message: '输入远端目录路径',\n initialValue: currentPath,\n })\n );\n currentPath = input;\n continue;\n }\n\n // 进入子目录\n currentPath = currentPath.endsWith('/') ? `${currentPath}${selected}` : `${currentPath}/${selected}`;\n }\n}\n\n/**\n * 收集单个步骤\n */\nasync function collectStep(): Promise<Step | null> {\n const stepType = handleCancel<StepType>(\n await clack.select({\n message: '选择步骤类型',\n options: STEP_TYPE_OPTIONS,\n })\n );\n\n noteBox(STEP_EXAMPLES[stepType], '参考(可直接复制变量)');\n\n switch (stepType) {\n case 'log': {\n const message = handleCancel<string>(\n await clack.text({ message: '日志信息' })\n );\n return { type: 'log', message };\n }\n case 'local': {\n const cmd = handleCancel<string>(\n await clack.text({ message: '命令' })\n );\n const cwd = handleCancel<string>(\n await clack.text({\n message: '工作目录(可选,留空使用默认)',\n placeholder: '${localDir}',\n })\n );\n const step: Step = { type: 'local', cmd };\n if (cwd) step.cwd = cwd;\n return step;\n }\n case 'remote': {\n const cmd = handleCancel<string>(\n await clack.text({ message: '远程命令' })\n );\n return { type: 'remote', cmd };\n }\n case 'uploadDir': {\n const local = handleCancel<string>(\n await clack.text({ message: '本地目录', placeholder: '${localDir}/dist' })\n );\n const remote = handleCancel<string>(\n await clack.text({ message: '远端目录', placeholder: '${remoteDir}' })\n );\n return { type: 'uploadDir', local, remote };\n }\n case 'uploadFile': {\n const local = handleCancel<string>(\n await clack.text({ message: '本地文件路径' })\n );\n const remote = handleCancel<string>(\n await clack.text({ message: '远端文件路径' })\n );\n return { type: 'uploadFile', local, remote };\n }\n case 'downloadDir': {\n const remote = handleCancel<string>(\n await clack.text({ message: '远端目录' })\n );\n const local = handleCancel<string>(\n await clack.text({ message: '本地保存目录' })\n );\n return { type: 'downloadDir', remote, local };\n }\n case 'downloadFile': {\n const remote = handleCancel<string>(\n await clack.text({ message: '远端文件路径' })\n );\n const local = handleCancel<string>(\n await clack.text({ message: '本地保存路径' })\n );\n return { type: 'downloadFile', remote, local };\n }\n }\n}\n\n/**\n * 运行 deploy init 主流程\n */\nexport async function runInit(cwd: string): Promise<void> {\n clack.intro('deploy init');\n\n const deployConfig = getConfig();\n\n // 1. 项目名称\n const defaultName = getDefaultProjectName(cwd);\n const projectId = handleCancel<string>(\n await clack.text({\n message: '项目 ID(用于标识)',\n initialValue: defaultName,\n })\n );\n\n if (deployConfig.projects[projectId]) {\n clack.log.warn(`项目 \"${projectId}\" 已存在,将覆盖配置`);\n }\n\n const name = handleCancel<string>(\n await clack.text({\n message: '项目显示名称',\n initialValue: projectId,\n })\n );\n\n // 2. 检查是否有可用服务器\n const serverIds = Object.keys(deployConfig.servers);\n if (serverIds.length === 0) {\n clack.log.error('没有已配置的服务器,请先运行 deploy server add 添加服务器');\n clack.outro('请先配置服务器');\n return;\n }\n\n // 3. 选择目标服务器\n const serverId = handleCancel<string>(\n await clack.select({\n message: '选择目标服务器',\n options: serverIds.map((id) => ({\n value: id,\n label: `${id} (${deployConfig.servers[id].host})`,\n })),\n })\n );\n\n // 4. 本地目录(绝对路径)\n const localDir = handleCancel<string>(\n await clack.text({\n message: '本地目录(绝对路径)',\n initialValue: cwd,\n })\n );\n\n // 5. 远端目录\n const spinner = clack.spinner();\n spinner.start('连接服务器...');\n let sftp: import('ssh2-sftp-client');\n try {\n sftp = await connectSftp('init', deployConfig.servers[serverId]);\n spinner.stop('已连接');\n } catch (err) {\n spinner.stop('连接失败');\n clack.log.error(`无法连接服务器: ${err}`);\n clack.outro('连接失败');\n return;\n }\n\n let remoteDir: string;\n try {\n const browseChoice = handleCancel<'browse' | 'manual'>(\n await clack.select({\n message: '远端目录设置方式',\n options: [\n { value: 'browse', label: '浏览服务器目录' },\n { value: 'manual', label: '手动输入路径' },\n ],\n })\n );\n\n if (browseChoice === 'browse') {\n remoteDir = await browseRemoteDir(sftp, '/');\n } else {\n remoteDir = handleCancel<string>(\n await clack.text({\n message: '远端目录路径',\n placeholder: '/appliction/my-app/',\n })\n );\n // 确保目录存在(循环创建)\n const confirmMkdir = handleCancel<boolean>(\n await clack.confirm({ message: `是否自动创建目录 ${remoteDir}(如果不存在)?` })\n );\n if (confirmMkdir) {\n await sftp.mkdir(remoteDir, true);\n clack.log.success(`目录已创建: ${remoteDir}`);\n }\n }\n } finally {\n await sftp.end();\n }\n\n // 6. 收集步骤\n const steps: Step[] = [];\n clack.log.info('开始配置部署步骤(每个步骤会展示参考和可用变量)');\n\n while (true) {\n if (steps.length > 0) {\n const action = handleCancel<'add' | 'done'>(\n await clack.select({\n message: `已配置 ${steps.length} 个步骤`,\n options: [\n { value: 'add', label: '添加步骤' },\n { value: 'done', label: '完成配置' },\n ],\n })\n );\n if (action === 'done') break;\n }\n\n const step = await collectStep();\n if (step) {\n steps.push(step);\n showStepsSummary(steps);\n }\n }\n\n // 7. 预览配置\n const projectConfig: ProjectConfig = {\n name,\n localDir: resolve(localDir),\n server: serverId,\n remoteDir,\n steps,\n };\n\n noteBox(JSON.stringify(projectConfig, null, 2), '项目配置预览');\n\n const confirmSave = handleCancel<boolean>(\n await clack.confirm({ message: '确认保存此配置?' })\n );\n\n if (confirmSave) {\n deployConfig.projects[projectId] = projectConfig;\n setConfig(deployConfig);\n clack.log.success(`项目 \"${projectId}\" 配置已保存`);\n } else {\n clack.log.info('已取消保存');\n }\n\n clack.outro('完成');\n}\n"]}
@@ -0,0 +1,38 @@
1
+ // src/ssh/executor.ts
2
+ async function execRemote(sftp, command) {
3
+ const fullCommand = `[ -f /etc/profile ] && source /etc/profile; ${command}`;
4
+ return new Promise((resolve, reject) => {
5
+ const client = sftp.client;
6
+ if (!client) {
7
+ reject(new Error("SSH \u5BA2\u6237\u7AEF\u672A\u8FDE\u63A5"));
8
+ return;
9
+ }
10
+ client.exec(fullCommand, (err, stream) => {
11
+ if (err) {
12
+ reject(err);
13
+ return;
14
+ }
15
+ let outStr = "";
16
+ let errStr = "";
17
+ stream.on("close", (code, signal) => {
18
+ if (code !== 0) {
19
+ reject(new Error(`\u8FDC\u7A0B\u547D\u4EE4\u6267\u884C\u5931\u8D25 (code=${code}): ${errStr || signal}`));
20
+ return;
21
+ }
22
+ resolve(outStr);
23
+ }).on("data", (data) => {
24
+ const str = data.toString();
25
+ outStr += str;
26
+ process.stdout.write(str);
27
+ }).stderr.on("data", (data) => {
28
+ const str = data.toString();
29
+ errStr += str;
30
+ process.stderr.write(str);
31
+ });
32
+ });
33
+ });
34
+ }
35
+
36
+ export { execRemote };
37
+ //# sourceMappingURL=executor-OQZFWKVP.js.map
38
+ //# sourceMappingURL=executor-OQZFWKVP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ssh/executor.ts"],"names":[],"mappings":";AAKA,eAAsB,UAAA,CAAW,MAAkB,OAAA,EAAkC;AAEnF,EAAA,MAAM,WAAA,GAAc,+CAA+C,OAAO,CAAA,CAAA;AAC1E,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AAEtC,IAAA,MAAM,SAAU,IAAA,CAAa,MAAA;AAC7B,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,MAAA,CAAO,IAAI,KAAA,CAAM,0CAAY,CAAC,CAAA;AAC9B,MAAA;AAAA,IACF;AACA,IAAA,MAAA,CAAO,IAAA,CAAK,WAAA,EAAa,CAAC,GAAA,EAAwB,MAAA,KAAgB;AAChE,MAAA,IAAI,GAAA,EAAK;AACP,QAAA,MAAA,CAAO,GAAG,CAAA;AACV,QAAA;AAAA,MACF;AACA,MAAA,IAAI,MAAA,GAAS,EAAA;AACb,MAAA,IAAI,MAAA,GAAS,EAAA;AACb,MAAA,MAAA,CACG,EAAA,CAAG,OAAA,EAAS,CAAC,IAAA,EAAc,MAAA,KAAmB;AAC7C,QAAA,IAAI,SAAS,CAAA,EAAG;AACd,UAAA,MAAA,CAAO,IAAI,MAAM,CAAA,uDAAA,EAAkB,IAAI,MAAM,MAAA,IAAU,MAAM,EAAE,CAAC,CAAA;AAChE,UAAA;AAAA,QACF;AACA,QAAA,OAAA,CAAQ,MAAM,CAAA;AAAA,MAChB,CAAC,CAAA,CACA,EAAA,CAAG,MAAA,EAAQ,CAAC,IAAA,KAAiB;AAC5B,QAAA,MAAM,GAAA,GAAM,KAAK,QAAA,EAAS;AAC1B,QAAA,MAAA,IAAU,GAAA;AACV,QAAA,OAAA,CAAQ,MAAA,CAAO,MAAM,GAAG,CAAA;AAAA,MAC1B,CAAC,CAAA,CACA,MAAA,CAAO,EAAA,CAAG,MAAA,EAAQ,CAAC,IAAA,KAAiB;AACnC,QAAA,MAAM,GAAA,GAAM,KAAK,QAAA,EAAS;AAC1B,QAAA,MAAA,IAAU,GAAA;AACV,QAAA,OAAA,CAAQ,MAAA,CAAO,MAAM,GAAG,CAAA;AAAA,MAC1B,CAAC,CAAA;AAAA,IACL,CAAC,CAAA;AAAA,EACH,CAAC,CAAA;AACH","file":"executor-OQZFWKVP.js","sourcesContent":["import type SftpClient from 'ssh2-sftp-client';\n\n/**\n * 在远程服务器执行命令\n */\nexport async function execRemote(sftp: SftpClient, command: string): Promise<string> {\n // 尝试加载环境变量,如果文件不存在则忽略错误\n const fullCommand = `[ -f /etc/profile ] && source /etc/profile; ${command}`;\n return new Promise((resolve, reject) => {\n // @ts-ignore: ssh2-sftp-client types don't expose client but it exists at runtime\n const client = (sftp as any).client;\n if (!client) {\n reject(new Error('SSH 客户端未连接'));\n return;\n }\n client.exec(fullCommand, (err: Error | undefined, stream: any) => {\n if (err) {\n reject(err);\n return;\n }\n let outStr = '';\n let errStr = '';\n stream\n .on('close', (code: number, signal: string) => {\n if (code !== 0) {\n reject(new Error(`远程命令执行失败 (code=${code}): ${errStr || signal}`));\n return;\n }\n resolve(outStr);\n })\n .on('data', (data: Buffer) => {\n const str = data.toString();\n outStr += str;\n process.stdout.write(str);\n })\n .stderr.on('data', (data: Buffer) => {\n const str = data.toString();\n errStr += str;\n process.stderr.write(str);\n });\n });\n });\n}\n"]}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * 查看配置
3
+ */
4
+ declare function showConfig(): void;
5
+ /**
6
+ * 直接运行指定项目
7
+ */
8
+ declare function runProject(projectId: string, serverOverride?: string): Promise<void>;
9
+ /**
10
+ * 交互式主流程:选服务器 → 多选项目 → 依次部署
11
+ */
12
+ declare function runApp(): Promise<void>;
13
+
14
+ /**
15
+ * 运行 deploy init 主流程
16
+ */
17
+ declare function runInit(cwd: string): Promise<void>;
18
+
19
+ export { runApp, runInit, runProject, showConfig };
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { runApp, runInit, runProject, showConfig } from './chunk-YB2FK4HH.js';
2
+ //# sourceMappingURL=index.js.map
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@mindbase/deploy",
3
+ "version": "1.0.0",
4
+ "description": "MindBase 部署工具",
5
+ "type": "module",
6
+ "bin": {
7
+ "deploy": "./dist/bin/index.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "engines": {
13
+ "node": ">=20.0.0"
14
+ },
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "scripts": {
19
+ "build": "tsup",
20
+ "dev": "tsup --watch",
21
+ "clean": "rm -rf dist",
22
+ "start": "node dist/bin/index.js",
23
+ "prepublishOnly": "npm run build"
24
+ },
25
+ "dependencies": {
26
+ "@clack/prompts": "^0.9.1",
27
+ "@mindbase/cli-ui": "workspace:*",
28
+ "commander": "^12.0.0",
29
+ "cross-spawn": "^7.0.6",
30
+ "picocolors": "^1.1.1",
31
+ "ssh2-sftp-client": "^9.1.0"
32
+ },
33
+ "devDependencies": {
34
+ "@types/cross-spawn": "^6.0.6",
35
+ "@types/node": "^20.0.0",
36
+ "@types/ssh2-sftp-client": "^9.0.4",
37
+ "tsup": "^8.0.0",
38
+ "typescript": "^5.1.3"
39
+ }
40
+ }