@lazycatcloud/lzc-cli 1.3.13 → 2.0.0-pre.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.
- package/README.md +30 -5
- package/changelog.md +16 -0
- package/lib/app/index.js +174 -58
- package/lib/app/lpk_build.js +197 -18
- package/lib/app/lpk_build_images.js +728 -0
- package/lib/app/lpk_create.js +96 -23
- package/lib/app/lpk_create_generator.js +150 -12
- package/lib/app/lpk_devshell.js +35 -21
- package/lib/app/lpk_embed_images.js +257 -0
- package/lib/app/lpk_installer.js +15 -7
- package/lib/app/project_cp.js +64 -0
- package/lib/app/project_deploy.js +33 -0
- package/lib/app/project_exec.js +45 -0
- package/lib/app/project_info.js +106 -0
- package/lib/app/project_log.js +67 -0
- package/lib/app/project_runtime.js +261 -0
- package/lib/app/project_start.js +100 -0
- package/lib/appstore/index.js +56 -16
- package/lib/appstore/publish.js +16 -13
- package/lib/box/index.js +103 -6
- package/lib/box/ssh_remote.js +259 -0
- package/lib/build_remote.js +22 -0
- package/lib/config/index.js +4 -3
- package/lib/debug_bridge.js +837 -44
- package/lib/docker/index.js +30 -10
- package/lib/i18n/index.js +1 -0
- package/lib/i18n/locales/en/translation.json +263 -250
- package/lib/i18n/locales/zh/translation.json +57 -44
- package/lib/lpk/core.js +487 -0
- package/lib/lpk/index.js +210 -0
- package/lib/shellapi.js +5 -5
- package/lib/sig/core.js +254 -0
- package/lib/sig/index.js +88 -0
- package/lib/utils.js +17 -12
- package/package.json +4 -3
- package/scripts/cli.js +4 -0
- package/template/_lpk/README.md +11 -3
- package/template/_lpk/gui-vnc.manifest.yml.in +27 -0
- package/template/_lpk/manifest.yml.in +4 -2
- package/template/_lpk/todolist-golang.manifest.yml.in +16 -0
- package/template/_lpk/todolist-java.manifest.yml.in +15 -0
- package/template/_lpk/todolist-python.manifest.yml.in +15 -0
- package/template/_lpk/vue.lzc-build.yml.in +0 -44
- package/template/blank/_gitignore +1 -0
- package/template/blank/lzc-build.yml +25 -40
- package/template/blank/lzc-manifest.yml +14 -7
- package/template/golang/Dockerfile +19 -0
- package/template/golang/README.md +33 -0
- package/template/golang/_gitignore +3 -0
- package/template/golang/go.mod +3 -0
- package/template/golang/lzc-build.yml +21 -0
- package/template/golang/lzc-icon.png +0 -0
- package/template/golang/main.go +252 -0
- package/template/golang/run.sh +3 -0
- package/template/golang/web/index.html +238 -0
- package/template/gui-vnc/README.md +19 -0
- package/template/gui-vnc/_gitignore +2 -0
- package/template/gui-vnc/images/Dockerfile +30 -0
- package/template/gui-vnc/images/kasmvnc.yaml +33 -0
- package/template/gui-vnc/images/startup-script.desktop +9 -0
- package/template/gui-vnc/images/startup-script.sh +6 -0
- package/template/gui-vnc/lzc-build.yml +23 -0
- package/template/gui-vnc/lzc-icon.png +0 -0
- package/template/python/Dockerfile +15 -0
- package/template/python/README.md +33 -0
- package/template/python/_gitignore +3 -0
- package/template/python/app.py +110 -0
- package/template/python/lzc-build.yml +21 -0
- package/template/python/lzc-icon.png +0 -0
- package/template/python/requirements.txt +1 -0
- package/template/python/run.sh +3 -0
- package/template/python/web/index.html +238 -0
- package/template/springboot/Dockerfile +20 -0
- package/template/springboot/README.md +33 -0
- package/template/springboot/_gitignore +3 -0
- package/template/springboot/lzc-build.yml +21 -0
- package/template/springboot/lzc-icon.png +0 -0
- package/template/springboot/pom.xml +38 -0
- package/template/springboot/run.sh +3 -0
- package/template/springboot/src/main/java/cloud/lazycat/app/Application.java +132 -0
- package/template/springboot/src/main/resources/application.properties +1 -0
- package/template/springboot/src/main/resources/static/index.html +238 -0
- package/template/vue/README.md +17 -7
- package/template/vue/_gitignore +1 -0
- package/template/vue/lzc-build.yml +31 -42
- package/template/vue/src/App.vue +36 -25
- package/template/vue/src/style.css +106 -49
- package/template/vue-minidb/README.md +34 -0
- package/template/vue-minidb/_gitignore +26 -0
- package/template/vue-minidb/index.html +13 -0
- package/template/vue-minidb/lzc-build.yml +48 -0
- package/template/vue-minidb/lzc-icon.png +0 -0
- package/template/vue-minidb/package.json +21 -0
- package/template/vue-minidb/public/vite.svg +1 -0
- package/template/vue-minidb/src/App.vue +206 -0
- package/template/vue-minidb/src/assets/vue.svg +1 -0
- package/template/vue-minidb/src/main.ts +5 -0
- package/template/vue-minidb/src/style.css +136 -0
- package/template/vue-minidb/src/vite-env.d.ts +1 -0
- package/template/vue-minidb/tsconfig.app.json +24 -0
- package/template/vue-minidb/tsconfig.json +7 -0
- package/template/vue-minidb/tsconfig.node.json +22 -0
- package/template/vue-minidb/vite.config.ts +10 -0
- /package/template/{vue → vue-minidb}/src/components/HelloWorld.vue +0 -0
package/lib/box/index.js
CHANGED
|
@@ -1,7 +1,42 @@
|
|
|
1
1
|
import logger from 'loglevel';
|
|
2
2
|
import shellapi from '../shellapi.js';
|
|
3
|
-
import
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import { findSshPublicKey, selectSshPublicKey, isMacOs, isWindows } from '../utils.js';
|
|
4
7
|
import { t } from '../i18n/index.js';
|
|
8
|
+
import { addSSHBox, listSSHBoxes, getDefaultSSHBox, setDefaultSSHBox, clearDefaultSSHBox, removeSSHBox } from './ssh_remote.js';
|
|
9
|
+
|
|
10
|
+
function getShellAPIConfigDir() {
|
|
11
|
+
const home = os.homedir();
|
|
12
|
+
let suffix = '/.config/hportal-client';
|
|
13
|
+
if (isMacOs) {
|
|
14
|
+
suffix = '/Library/Application Support/hportal-client';
|
|
15
|
+
} else if (isWindows) {
|
|
16
|
+
suffix = '\\AppData\\Roaming\\hportal-client';
|
|
17
|
+
}
|
|
18
|
+
return path.join(home, suffix);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function canUseShellApi() {
|
|
22
|
+
const configDir = getShellAPIConfigDir();
|
|
23
|
+
const addrFile = path.resolve(configDir, 'shellapi_addr');
|
|
24
|
+
const credFile = path.resolve(configDir, 'shellapi_cred');
|
|
25
|
+
return fs.existsSync(addrFile) && fs.existsSync(credFile);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function fetchShellBoxes() {
|
|
29
|
+
if (!canUseShellApi()) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
await shellapi.init();
|
|
34
|
+
return await shellapi.boxList();
|
|
35
|
+
} catch (error) {
|
|
36
|
+
logger.debug('query shell boxes failed', error);
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
5
40
|
|
|
6
41
|
export function boxCommand(box) {
|
|
7
42
|
let subCommands = [
|
|
@@ -9,14 +44,39 @@ export function boxCommand(box) {
|
|
|
9
44
|
command: 'switch <boxname>',
|
|
10
45
|
desc: t('lzc_cli.lib.box.index.switch_cmd_desc', '设置默认的盒子'),
|
|
11
46
|
handler: async ({ boxname }) => {
|
|
47
|
+
const target = String(boxname ?? '').trim();
|
|
48
|
+
if (!target) {
|
|
49
|
+
throw new Error('boxname is required');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const sshBoxes = listSSHBoxes();
|
|
53
|
+
if (sshBoxes.some((item) => item.box_name === target)) {
|
|
54
|
+
setDefaultSSHBox(target);
|
|
55
|
+
logger.info(`Default box switched: ${target}`);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!canUseShellApi()) {
|
|
60
|
+
throw new Error(`Box "${target}" not found in SSH boxes and shellapi is unavailable`);
|
|
61
|
+
}
|
|
12
62
|
await shellapi.init();
|
|
13
|
-
await shellapi.setDefaultBox(
|
|
63
|
+
await shellapi.setDefaultBox(target);
|
|
64
|
+
clearDefaultSSHBox();
|
|
65
|
+
logger.info(`Default box switched: ${target}`);
|
|
14
66
|
},
|
|
15
67
|
},
|
|
16
68
|
{
|
|
17
69
|
command: 'default',
|
|
18
70
|
desc: t('lzc_cli.lib.box.index.default_cmd_desc', '输出当前默认的盒子名'),
|
|
19
71
|
handler: async () => {
|
|
72
|
+
const sshDefault = getDefaultSSHBox();
|
|
73
|
+
if (sshDefault) {
|
|
74
|
+
console.log(sshDefault.box_name);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (!canUseShellApi()) {
|
|
78
|
+
throw new Error('Default box not found. Run "lzc-cli box add-by-ssh <loginUser> <address>" first.');
|
|
79
|
+
}
|
|
20
80
|
await shellapi.init();
|
|
21
81
|
console.log(shellapi.boxname);
|
|
22
82
|
},
|
|
@@ -32,8 +92,17 @@ export function boxCommand(box) {
|
|
|
32
92
|
});
|
|
33
93
|
},
|
|
34
94
|
handler: async ({ verbose }) => {
|
|
35
|
-
await
|
|
36
|
-
const
|
|
95
|
+
const shellBoxes = await fetchShellBoxes();
|
|
96
|
+
const sshBoxes = listSSHBoxes();
|
|
97
|
+
const sshDefault = sshBoxes.find((item) => item.is_default_box);
|
|
98
|
+
const boxes = [
|
|
99
|
+
...shellBoxes.map((item) => ({
|
|
100
|
+
...item,
|
|
101
|
+
is_default_box: sshDefault ? false : item.is_default_box,
|
|
102
|
+
box_type: 'hclient',
|
|
103
|
+
})),
|
|
104
|
+
...sshBoxes,
|
|
105
|
+
];
|
|
37
106
|
if (boxes.length === 0) {
|
|
38
107
|
console.log(t('lzc_cli.lib.box.index.list_cmd_box_not_exist_tips', '没有找到任何盒子,赶紧添加一个吧!'));
|
|
39
108
|
return;
|
|
@@ -45,6 +114,7 @@ export function boxCommand(box) {
|
|
|
45
114
|
|
|
46
115
|
const list = boxes.map((b) => {
|
|
47
116
|
const info = {};
|
|
117
|
+
info[`${t('lzc_cli.lib.box.index.list_cmd_table_box_type', 'Type')}`] = b.box_type ?? 'hclient';
|
|
48
118
|
info[`${t('lzc_cli.lib.box.index.list_cmd_table_box_name', '名称')}`] = b.box_name;
|
|
49
119
|
info[`${t('lzc_cli.lib.box.index.list_cmd_table_status', '状态')}`] = b.status;
|
|
50
120
|
info[`${t('lzc_cli.lib.box.index.list_cmd_table_login_user', '登录用户')}`] = b.login_user;
|
|
@@ -59,6 +129,9 @@ export function boxCommand(box) {
|
|
|
59
129
|
command: 'add-public-key',
|
|
60
130
|
desc: t('lzc_cli.lib.box.index.add_public_key_cmd_desc', '添加public-key到开发者工具中'),
|
|
61
131
|
handler: async () => {
|
|
132
|
+
if (!canUseShellApi()) {
|
|
133
|
+
throw new Error('add-public-key requires shellapi connection');
|
|
134
|
+
}
|
|
62
135
|
await shellapi.init();
|
|
63
136
|
const keys = await findSshPublicKey();
|
|
64
137
|
const sshInfo = await selectSshPublicKey(keys);
|
|
@@ -68,13 +141,37 @@ export function boxCommand(box) {
|
|
|
68
141
|
t(
|
|
69
142
|
'lzc_cli.lib.box.index.add_public_key_cmd_apply_tips',
|
|
70
143
|
`请点击下面的连接,或者复制到浏览器中打开,点击申请完成添加
|
|
71
|
-
-> https://dev.{{boxname}}.heiyu.space/auth?key={{pk}}
|
|
144
|
+
-> https://dev.{{ boxname }}.heiyu.space/auth?key={{ pk }}
|
|
72
145
|
`,
|
|
73
|
-
{ boxname: shellapi.boxname, pk: pk },
|
|
146
|
+
{ boxname: shellapi.boxname, pk: pk, interpolation: { escapeValue: false } },
|
|
74
147
|
),
|
|
75
148
|
);
|
|
76
149
|
},
|
|
77
150
|
},
|
|
151
|
+
{
|
|
152
|
+
command: 'add-by-ssh <loginUser> <address>',
|
|
153
|
+
desc: t('lzc_cli.lib.box.index.add_by_ssh_cmd_desc', '通过 ssh 配置远端直连目标'),
|
|
154
|
+
handler: async ({ loginUser, address }) => {
|
|
155
|
+
const remote = addSSHBox(loginUser, address);
|
|
156
|
+
logger.info(`SSH box added: ${remote.box_name} (${remote.ssh_target})`);
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
command: 'del <boxname>',
|
|
161
|
+
aliases: ['delete', 'rm', 'remove'],
|
|
162
|
+
desc: 'Delete ssh box config by name',
|
|
163
|
+
handler: async ({ boxname }) => {
|
|
164
|
+
const target = String(boxname ?? '').trim();
|
|
165
|
+
if (!target) {
|
|
166
|
+
throw new Error('boxname is required');
|
|
167
|
+
}
|
|
168
|
+
const removed = removeSSHBox(target);
|
|
169
|
+
if (!removed) {
|
|
170
|
+
throw new Error(`SSH box not found: ${target}`);
|
|
171
|
+
}
|
|
172
|
+
logger.info(`SSH box deleted: ${target}`);
|
|
173
|
+
},
|
|
174
|
+
},
|
|
78
175
|
];
|
|
79
176
|
box.command({
|
|
80
177
|
command: 'box',
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import env from '../config/env.js';
|
|
2
|
+
|
|
3
|
+
const SSH_BOXES_KEY = 'ssh_boxes';
|
|
4
|
+
const SSH_DEFAULT_BOX_KEY = 'default_ssh_box';
|
|
5
|
+
const DEFAULT_SSH_PORT = 22;
|
|
6
|
+
|
|
7
|
+
function parsePort(rawPort, source) {
|
|
8
|
+
if (rawPort === undefined || rawPort === null || rawPort === '') {
|
|
9
|
+
return DEFAULT_SSH_PORT;
|
|
10
|
+
}
|
|
11
|
+
const port = Number.parseInt(String(rawPort), 10);
|
|
12
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
13
|
+
throw new Error(`Invalid ssh port in ${source}: ${rawPort}`);
|
|
14
|
+
}
|
|
15
|
+
return port;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function normalizeLoginUser(loginUser) {
|
|
19
|
+
const user = String(loginUser ?? '').trim();
|
|
20
|
+
if (!user) {
|
|
21
|
+
throw new Error('loginUser is required');
|
|
22
|
+
}
|
|
23
|
+
return user;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function parseAddress(address) {
|
|
27
|
+
const value = String(address ?? '').trim();
|
|
28
|
+
if (!value) {
|
|
29
|
+
throw new Error('address is required');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (value.startsWith('[')) {
|
|
33
|
+
const end = value.indexOf(']');
|
|
34
|
+
if (end <= 1) {
|
|
35
|
+
throw new Error(`Invalid address: ${address}`);
|
|
36
|
+
}
|
|
37
|
+
const host = value.slice(1, end).trim();
|
|
38
|
+
if (!host) {
|
|
39
|
+
throw new Error(`Invalid address: ${address}`);
|
|
40
|
+
}
|
|
41
|
+
const suffix = value.slice(end + 1).trim();
|
|
42
|
+
if (!suffix) {
|
|
43
|
+
return { sshHost: host, sshPort: DEFAULT_SSH_PORT };
|
|
44
|
+
}
|
|
45
|
+
if (!suffix.startsWith(':')) {
|
|
46
|
+
throw new Error(`Invalid address: ${address}`);
|
|
47
|
+
}
|
|
48
|
+
return { sshHost: host, sshPort: parsePort(suffix.slice(1), `address ${address}`) };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const colonCount = (value.match(/:/g) ?? []).length;
|
|
52
|
+
if (colonCount === 1) {
|
|
53
|
+
const splitIndex = value.lastIndexOf(':');
|
|
54
|
+
const host = value.slice(0, splitIndex).trim();
|
|
55
|
+
const port = value.slice(splitIndex + 1).trim();
|
|
56
|
+
if (!host) {
|
|
57
|
+
throw new Error(`Invalid address: ${address}`);
|
|
58
|
+
}
|
|
59
|
+
return { sshHost: host, sshPort: parsePort(port, `address ${address}`) };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (colonCount > 1) {
|
|
63
|
+
return { sshHost: value, sshPort: DEFAULT_SSH_PORT };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return { sshHost: value, sshPort: DEFAULT_SSH_PORT };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function toBoxName(sshHost, sshPort) {
|
|
70
|
+
if (sshPort === DEFAULT_SSH_PORT) {
|
|
71
|
+
return sshHost;
|
|
72
|
+
}
|
|
73
|
+
return `${sshHost}:${sshPort}`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function normalizeStoredBox(rawBox) {
|
|
77
|
+
if (!rawBox || typeof rawBox !== 'object') {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
const loginUser = String(rawBox.login_user ?? '').trim();
|
|
81
|
+
const sshHost = String(rawBox.ssh_host ?? '').trim();
|
|
82
|
+
let sshPort;
|
|
83
|
+
try {
|
|
84
|
+
sshPort = parsePort(rawBox.ssh_port, 'saved ssh box');
|
|
85
|
+
} catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
const boxName = String(rawBox.box_name ?? '').trim() || toBoxName(sshHost, sshPort);
|
|
89
|
+
if (!loginUser || !sshHost || !boxName) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
box_name: boxName,
|
|
94
|
+
login_user: loginUser,
|
|
95
|
+
ssh_host: sshHost,
|
|
96
|
+
ssh_port: sshPort,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function readStoredBoxes() {
|
|
101
|
+
const rawBoxes = env.get(SSH_BOXES_KEY);
|
|
102
|
+
if (!Array.isArray(rawBoxes)) {
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
const result = [];
|
|
106
|
+
const indexByName = new Map();
|
|
107
|
+
for (const rawBox of rawBoxes) {
|
|
108
|
+
const box = normalizeStoredBox(rawBox);
|
|
109
|
+
if (!box) {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (indexByName.has(box.box_name)) {
|
|
113
|
+
const index = indexByName.get(box.box_name);
|
|
114
|
+
result[index] = box;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
indexByName.set(box.box_name, result.length);
|
|
118
|
+
result.push(box);
|
|
119
|
+
}
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function saveStoredBoxes(boxes) {
|
|
124
|
+
env.set({
|
|
125
|
+
[SSH_BOXES_KEY]: boxes,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function readDefaultBoxName() {
|
|
130
|
+
return String(env.get(SSH_DEFAULT_BOX_KEY) ?? '').trim();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function saveDefaultBoxName(boxname) {
|
|
134
|
+
const name = String(boxname ?? '').trim();
|
|
135
|
+
if (!name) {
|
|
136
|
+
env.del(SSH_DEFAULT_BOX_KEY);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
env.set({
|
|
140
|
+
[SSH_DEFAULT_BOX_KEY]: name,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function resolveDefaultBoxName(storedBoxes) {
|
|
145
|
+
const currentDefault = readDefaultBoxName();
|
|
146
|
+
if (currentDefault && storedBoxes.some((item) => item.box_name === currentDefault)) {
|
|
147
|
+
return currentDefault;
|
|
148
|
+
}
|
|
149
|
+
return '';
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function toPublicBox(stored, defaultName) {
|
|
153
|
+
const sshTarget = `${stored.login_user}@${stored.ssh_host}`;
|
|
154
|
+
return {
|
|
155
|
+
id: `ssh:${stored.box_name}`,
|
|
156
|
+
box_name: stored.box_name,
|
|
157
|
+
status: 'ssh',
|
|
158
|
+
login_user: stored.login_user,
|
|
159
|
+
is_admin_login: true,
|
|
160
|
+
is_default_box: stored.box_name === defaultName,
|
|
161
|
+
box_type: 'ssh',
|
|
162
|
+
ssh_host: stored.ssh_host,
|
|
163
|
+
ssh_port: stored.ssh_port,
|
|
164
|
+
ssh_target: sshTarget,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function toBuildRemote(box) {
|
|
169
|
+
return {
|
|
170
|
+
mode: 'build-remote',
|
|
171
|
+
source: `box:ssh:${box.box_name}`,
|
|
172
|
+
boxname: box.box_name,
|
|
173
|
+
sshUser: box.login_user,
|
|
174
|
+
sshHost: box.ssh_host,
|
|
175
|
+
sshPort: box.ssh_port,
|
|
176
|
+
sshTarget: box.ssh_target,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function listSSHBoxes() {
|
|
181
|
+
const storedBoxes = readStoredBoxes();
|
|
182
|
+
const defaultName = resolveDefaultBoxName(storedBoxes);
|
|
183
|
+
return storedBoxes.map((item) => toPublicBox(item, defaultName));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function addSSHBox(loginUser, address) {
|
|
187
|
+
const sshUser = normalizeLoginUser(loginUser);
|
|
188
|
+
const { sshHost, sshPort } = parseAddress(address);
|
|
189
|
+
const boxName = toBoxName(sshHost, sshPort);
|
|
190
|
+
const nextBox = normalizeStoredBox({
|
|
191
|
+
box_name: boxName,
|
|
192
|
+
login_user: sshUser,
|
|
193
|
+
ssh_host: sshHost,
|
|
194
|
+
ssh_port: sshPort,
|
|
195
|
+
});
|
|
196
|
+
if (!nextBox) {
|
|
197
|
+
throw new Error(`Invalid ssh box config: ${loginUser}@${address}`);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const savedBoxes = readStoredBoxes().filter((item) => item.box_name !== boxName);
|
|
201
|
+
savedBoxes.push(nextBox);
|
|
202
|
+
saveStoredBoxes(savedBoxes);
|
|
203
|
+
saveDefaultBoxName(boxName);
|
|
204
|
+
|
|
205
|
+
return toPublicBox(nextBox, boxName);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export function saveBuildRemoteBySSH(loginUser, address) {
|
|
209
|
+
return addSSHBox(loginUser, address);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function getDefaultSSHBox() {
|
|
213
|
+
const boxes = listSSHBoxes();
|
|
214
|
+
const selected = boxes.find((item) => item.is_default_box);
|
|
215
|
+
return selected ?? null;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function setDefaultSSHBox(boxname) {
|
|
219
|
+
const target = String(boxname ?? '').trim();
|
|
220
|
+
if (!target) {
|
|
221
|
+
throw new Error('boxname is required');
|
|
222
|
+
}
|
|
223
|
+
const exists = listSSHBoxes().some((item) => item.box_name === target);
|
|
224
|
+
if (!exists) {
|
|
225
|
+
throw new Error(`SSH box not found: ${target}`);
|
|
226
|
+
}
|
|
227
|
+
saveDefaultBoxName(target);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export function clearDefaultSSHBox() {
|
|
231
|
+
saveDefaultBoxName('');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export function removeSSHBox(boxname) {
|
|
235
|
+
const target = String(boxname ?? '').trim();
|
|
236
|
+
if (!target) {
|
|
237
|
+
throw new Error('boxname is required');
|
|
238
|
+
}
|
|
239
|
+
const storedBoxes = readStoredBoxes();
|
|
240
|
+
const exists = storedBoxes.some((item) => item.box_name === target);
|
|
241
|
+
if (!exists) {
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const nextBoxes = storedBoxes.filter((item) => item.box_name !== target);
|
|
246
|
+
saveStoredBoxes(nextBoxes);
|
|
247
|
+
if (readDefaultBoxName() === target) {
|
|
248
|
+
saveDefaultBoxName('');
|
|
249
|
+
}
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export function loadBuildRemoteFromConfig() {
|
|
254
|
+
const box = getDefaultSSHBox();
|
|
255
|
+
if (!box) {
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
return toBuildRemote(box);
|
|
259
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { loadBuildRemoteFromConfig } from './box/ssh_remote.js';
|
|
2
|
+
|
|
3
|
+
export const DEFAULT_BUILD_CONFIG_FILE = 'lzc-build.yml';
|
|
4
|
+
export const DEFAULT_BUILD_BASE_FILE = 'lzc-build.base.yml';
|
|
5
|
+
|
|
6
|
+
export function resolveBuildRemoteFromOptions(options, source = DEFAULT_BUILD_CONFIG_FILE) {
|
|
7
|
+
void options;
|
|
8
|
+
void source;
|
|
9
|
+
return loadBuildRemoteFromConfig();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function resolveBuildRemoteFromFile(startDir = process.cwd(), buildConfigFile = DEFAULT_BUILD_CONFIG_FILE) {
|
|
13
|
+
void startDir;
|
|
14
|
+
void buildConfigFile;
|
|
15
|
+
return loadBuildRemoteFromConfig();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function hasBuildRemote(startDir = process.cwd(), buildConfigFile = DEFAULT_BUILD_CONFIG_FILE) {
|
|
19
|
+
void startDir;
|
|
20
|
+
void buildConfigFile;
|
|
21
|
+
return !!loadBuildRemoteFromConfig();
|
|
22
|
+
}
|
package/lib/config/index.js
CHANGED
|
@@ -14,8 +14,9 @@ export function configCommand(program) {
|
|
|
14
14
|
const pair = Object.fromEntries([[key, value]]);
|
|
15
15
|
env.set(pair);
|
|
16
16
|
logger.info(
|
|
17
|
-
t('lzc_cli.lib.config.index.set_cmd_success_tips', `{{key}} 配置成功!`, {
|
|
17
|
+
t('lzc_cli.lib.config.index.set_cmd_success_tips', `{{ key }} 配置成功!`, {
|
|
18
18
|
key,
|
|
19
|
+
interpolation: { escapeValue: false }
|
|
19
20
|
}),
|
|
20
21
|
);
|
|
21
22
|
},
|
|
@@ -26,7 +27,7 @@ export function configCommand(program) {
|
|
|
26
27
|
handler: async ({ key }) => {
|
|
27
28
|
if (key) {
|
|
28
29
|
env.del(key);
|
|
29
|
-
logger.info(t('lzc_cli.lib.config.index.del_cmd_success', `删除 {{key}} 成功!`, { key }));
|
|
30
|
+
logger.info(t('lzc_cli.lib.config.index.del_cmd_success', `删除 {{ key }} 成功!`, { key, interpolation: { escapeValue: false } }));
|
|
30
31
|
return;
|
|
31
32
|
}
|
|
32
33
|
},
|
|
@@ -50,7 +51,7 @@ export function configCommand(program) {
|
|
|
50
51
|
];
|
|
51
52
|
program.command({
|
|
52
53
|
command: 'config',
|
|
53
|
-
desc:
|
|
54
|
+
desc: false,
|
|
54
55
|
builder: (args) => {
|
|
55
56
|
args.command(subCommands);
|
|
56
57
|
args.example('$0 config get', t('lzc_cli.lib.config.index.config_cmd_list_tips', '列出所有配置'))
|