@lazycatcloud/lzc-cli 1.2.37 → 1.2.39
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 +2 -9
- package/lib/app/apkshell.js +5 -3
- package/lib/app/lpk_build.js +12 -7
- package/lib/app/lpk_create_generator.js +4 -4
- package/lib/app/lpk_debug_bridge.js +69 -26
- package/lib/app/lpk_devshell.js +38 -12
- package/lib/shellapi.js +57 -20
- package/lib/utils.js +101 -13
- package/package.json +2 -1
- package/template/_lpk/win-rsync/msys-2.0.dll +0 -0
- package/template/_lpk/win-rsync/rsync.exe +0 -0
package/README.md
CHANGED
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
#### 依赖
|
|
4
4
|
|
|
5
5
|
1. `ssh`
|
|
6
|
-
2. `
|
|
7
|
-
3.
|
|
8
|
-
4. 盒子中安装开发者工具应用
|
|
6
|
+
2. `Linux` 和 `MacOS` 下需要安装 `rsync`
|
|
7
|
+
3. 盒子中安装开发者工具应用
|
|
9
8
|
|
|
10
9
|
#### 快速上手
|
|
11
10
|
|
|
@@ -32,9 +31,3 @@ lzc-cli project devshell
|
|
|
32
31
|
# 经过测试后,将包发布到懒猫云商店中去
|
|
33
32
|
lzc-cli appstore publish
|
|
34
33
|
```
|
|
35
|
-
|
|
36
|
-
### FAQ
|
|
37
|
-
|
|
38
|
-
#### 1. 开发者工具 ssh 的帐号密码是多少?
|
|
39
|
-
|
|
40
|
-
现在帐号密码设置为 box:box,后面会使用 pam 模块对接盒子帐号体系。
|
package/lib/app/apkshell.js
CHANGED
|
@@ -17,14 +17,16 @@ export async function triggerApk(id, name, iconPath) {
|
|
|
17
17
|
}
|
|
18
18
|
const resp = await api.post(
|
|
19
19
|
"https://appstore.lazycat.cloud/api/trigger_latest_for_app",
|
|
20
|
-
form
|
|
20
|
+
form,
|
|
21
|
+
{ timeout: 5000 }
|
|
21
22
|
)
|
|
22
23
|
if (resp.status == 304) {
|
|
23
|
-
|
|
24
|
+
logger.debug(`APK构建任务已创建成功,如需使用安卓端,请耐心等待1分钟左右`)
|
|
25
|
+
} else if (resp.status <= 201) {
|
|
24
26
|
logger.info(`APK构建任务已创建成功,如需使用安卓端,请耐心等待1分钟左右`)
|
|
25
27
|
} else if (resp.status >= 400) {
|
|
26
28
|
logger.debug("请求按钮应用出错:", resp)
|
|
27
|
-
throw
|
|
29
|
+
throw `请求生成应用出错! 使用 --apk=n 停止生成APK`
|
|
28
30
|
}
|
|
29
31
|
} catch (error) {
|
|
30
32
|
logger.error(error)
|
package/lib/app/lpk_build.js
CHANGED
|
@@ -9,9 +9,12 @@ import {
|
|
|
9
9
|
envTemplateFile,
|
|
10
10
|
isValidPackageName,
|
|
11
11
|
tarContentDir,
|
|
12
|
-
isPngWithFile
|
|
12
|
+
isPngWithFile,
|
|
13
|
+
isMacOs,
|
|
14
|
+
isWindows,
|
|
15
|
+
isLinux
|
|
13
16
|
} from "../utils.js"
|
|
14
|
-
import
|
|
17
|
+
import spawn from "cross-spawn"
|
|
15
18
|
import { LpkManifest } from "./lpk_create.js"
|
|
16
19
|
import archiver from "archiver"
|
|
17
20
|
import yaml from "js-yaml"
|
|
@@ -79,8 +82,8 @@ function localIp() {
|
|
|
79
82
|
const regex = /inet6 (fc03:1136:[0-9a-fA-F:]+)[?:\/ ]/
|
|
80
83
|
|
|
81
84
|
let output = ""
|
|
82
|
-
if (
|
|
83
|
-
const result =
|
|
85
|
+
if (isMacOs) {
|
|
86
|
+
const result = spawn.sync("sh", ["-c", "ifconfig"], {
|
|
84
87
|
encoding: "utf-8"
|
|
85
88
|
})
|
|
86
89
|
if (result.status != 0 || result.error) {
|
|
@@ -88,11 +91,11 @@ function localIp() {
|
|
|
88
91
|
return ""
|
|
89
92
|
}
|
|
90
93
|
output = result.stdout
|
|
91
|
-
} else if (
|
|
94
|
+
} else if (isWindows) {
|
|
92
95
|
logger.debug(`当前 Windows 不支持使用 LocalIP 反代`)
|
|
93
96
|
return ""
|
|
94
97
|
} else {
|
|
95
|
-
const result =
|
|
98
|
+
const result = spawn.sync("ip", ["addr", "show", "heiyu-0"], {
|
|
96
99
|
encoding: "utf-8"
|
|
97
100
|
})
|
|
98
101
|
if (result.status !== 0 || result.error) {
|
|
@@ -198,7 +201,9 @@ export class LpkBuild {
|
|
|
198
201
|
}
|
|
199
202
|
|
|
200
203
|
if (this.options["buildscript"]) {
|
|
201
|
-
|
|
204
|
+
const cmd = isWindows ? "cmd" : "sh"
|
|
205
|
+
const cmdArgs = isWindows ? "/c" : "-c"
|
|
206
|
+
let p = spawn.sync(cmd, [cmdArgs, this.options["buildscript"]], {
|
|
202
207
|
cwd: this.pwd,
|
|
203
208
|
stdio: "inherit"
|
|
204
209
|
})
|
|
@@ -4,7 +4,7 @@ import fs from "fs"
|
|
|
4
4
|
import { isBinaryFileSync } from "isbinaryfile"
|
|
5
5
|
import { contextDirname, isFileExist } from "../utils.js"
|
|
6
6
|
import chalk from "chalk"
|
|
7
|
-
import
|
|
7
|
+
import spawn from "cross-spawn"
|
|
8
8
|
import logger from "loglevel"
|
|
9
9
|
|
|
10
10
|
const __dirname = contextDirname(import.meta.url)
|
|
@@ -75,10 +75,10 @@ function vueTemplate(type) {
|
|
|
75
75
|
const v = type == "vue3" ? "vue@3" : "vue@2"
|
|
76
76
|
return {
|
|
77
77
|
build: async (to, option) => {
|
|
78
|
-
|
|
78
|
+
spawn.sync("npm", ["init", v, path.basename(to)], { stdio: "inherit" })
|
|
79
79
|
},
|
|
80
80
|
after: async function (name, cwd) {
|
|
81
|
-
|
|
81
|
+
spawn.sync("npm", ["install"], {
|
|
82
82
|
cwd: path.resolve(cwd, name),
|
|
83
83
|
stdio: "inherit"
|
|
84
84
|
})
|
|
@@ -182,7 +182,7 @@ export const TemplateConfig = {
|
|
|
182
182
|
appendREADME(path.resolve(cwd, name))
|
|
183
183
|
|
|
184
184
|
console.log(chalk.green("开始安装依赖"))
|
|
185
|
-
|
|
185
|
+
spawn.sync("npm", ["install"], {
|
|
186
186
|
cwd: path.join(cwd, name),
|
|
187
187
|
stdio: "inherit"
|
|
188
188
|
})
|
|
@@ -1,26 +1,38 @@
|
|
|
1
|
-
import
|
|
1
|
+
import spawn from "cross-spawn"
|
|
2
2
|
import fs from "node:fs"
|
|
3
3
|
import shellApi from "../shellapi.js"
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
isDebugMode,
|
|
6
|
+
isTraceMode,
|
|
7
|
+
resolveDomain,
|
|
8
|
+
sleep,
|
|
9
|
+
findSshPublicKey
|
|
10
|
+
} from "../utils.js"
|
|
5
11
|
import logger from "loglevel"
|
|
6
12
|
import commandExists from "command-exists"
|
|
7
13
|
|
|
14
|
+
function sshCmdArgs(...args) {
|
|
15
|
+
const defaultOptions = [
|
|
16
|
+
`-o "StrictHostKeyChecking=no"`,
|
|
17
|
+
`-o "UserKnownHostsFile=/dev/null"`,
|
|
18
|
+
"-q",
|
|
19
|
+
"-p 22222",
|
|
20
|
+
`${isTraceMode() ? "-v" : ""}`
|
|
21
|
+
]
|
|
22
|
+
return [...defaultOptions, ...args].join(" ")
|
|
23
|
+
}
|
|
24
|
+
|
|
8
25
|
export class DebugBridge {
|
|
9
26
|
constructor() {
|
|
10
27
|
this.uid = shellApi.uid
|
|
11
28
|
this.boxname = shellApi.boxname
|
|
12
29
|
this.domain = `dev.${this.boxname}.heiyu.space`
|
|
13
|
-
this.sshCmd = sshCmd(
|
|
14
|
-
`${isDebugMode() ? "-vv" : ""}`,
|
|
15
|
-
"-p 22222",
|
|
16
|
-
`box@${this.domain}`
|
|
17
|
-
)
|
|
18
30
|
}
|
|
19
31
|
|
|
20
32
|
async common(cmd, args) {
|
|
21
33
|
const resolvedIp = await resolveDomain(this.domain)
|
|
22
34
|
const resolvedCmd = cmd.replace(this.domain, resolvedIp)
|
|
23
|
-
const ssh =
|
|
35
|
+
const ssh = spawn.sync(resolvedCmd, args, {
|
|
24
36
|
shell: true,
|
|
25
37
|
encoding: "utf-8",
|
|
26
38
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -43,8 +55,11 @@ export class DebugBridge {
|
|
|
43
55
|
const stream = fs.createReadStream(lpkPath)
|
|
44
56
|
const resolvedIp = await resolveDomain(this.domain)
|
|
45
57
|
const ssh = spawn(
|
|
46
|
-
|
|
47
|
-
[
|
|
58
|
+
"ssh",
|
|
59
|
+
[
|
|
60
|
+
sshCmdArgs(`box@${resolvedIp}`),
|
|
61
|
+
`install --uid ${this.uid} --pkgId ${pkgId}`
|
|
62
|
+
],
|
|
48
63
|
{
|
|
49
64
|
shell: true,
|
|
50
65
|
stdio: ["pipe", "inherit", "inherit"]
|
|
@@ -64,25 +79,44 @@ export class DebugBridge {
|
|
|
64
79
|
}
|
|
65
80
|
|
|
66
81
|
async sshCopyId() {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (!existed) {
|
|
70
|
-
logger.error("请检查 ssh-copy-id 是否安装,路径是否正确!")
|
|
71
|
-
process.exit(1)
|
|
72
|
-
}
|
|
82
|
+
const sshInfo = await findSshPublicKey()
|
|
83
|
+
logger.debug("ssh public key info", sshInfo)
|
|
73
84
|
|
|
74
85
|
const resolvedIp = await resolveDomain(this.domain)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
86
|
+
logger.debug("ip", resolvedIp)
|
|
87
|
+
|
|
88
|
+
// 将当前机器上的ssh public key 写到开发者工具中去,并过滤到重复的,管道在执行前会被清空,需要使用一个中间文件
|
|
89
|
+
const stream = spawn.sync(
|
|
90
|
+
"ssh",
|
|
91
|
+
[sshCmdArgs(`box@${resolvedIp}`), `add-ssh-public-key`],
|
|
92
|
+
{
|
|
93
|
+
input: sshInfo.content.trimLeft(),
|
|
94
|
+
shell: true,
|
|
95
|
+
encoding: "utf-8",
|
|
96
|
+
stdio: "pipe"
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
if (stream.status != 0) {
|
|
100
|
+
const stderr = stream.stderr.toString("utf-8")
|
|
101
|
+
const stdout = stream.stdout.toString("utf-8")
|
|
102
|
+
throw stdout + stderr
|
|
103
|
+
}
|
|
104
|
+
if (stream.error) {
|
|
105
|
+
throw stream.error
|
|
106
|
+
}
|
|
78
107
|
}
|
|
79
108
|
|
|
80
109
|
async status(appId) {
|
|
81
|
-
return this.common(
|
|
110
|
+
return this.common("ssh", [
|
|
111
|
+
sshCmdArgs(`box@${this.domain}`),
|
|
112
|
+
`status --uid ${this.uid}`,
|
|
113
|
+
appId
|
|
114
|
+
])
|
|
82
115
|
}
|
|
83
116
|
|
|
84
117
|
async isDevshell(appId) {
|
|
85
|
-
const stdout = await this.common(
|
|
118
|
+
const stdout = await this.common("ssh", [
|
|
119
|
+
sshCmdArgs(`box@${this.domain}`),
|
|
86
120
|
`isDevshell --uid ${this.uid}`,
|
|
87
121
|
appId
|
|
88
122
|
])
|
|
@@ -90,11 +124,19 @@ export class DebugBridge {
|
|
|
90
124
|
}
|
|
91
125
|
|
|
92
126
|
async resume(appId) {
|
|
93
|
-
return this.common(
|
|
127
|
+
return this.common("ssh", [
|
|
128
|
+
sshCmdArgs(`box@${this.domain}`),
|
|
129
|
+
`resume --uid ${this.uid}`,
|
|
130
|
+
appId
|
|
131
|
+
])
|
|
94
132
|
}
|
|
95
133
|
|
|
96
134
|
async uninstall(appId) {
|
|
97
|
-
return this.common(
|
|
135
|
+
return this.common("ssh", [
|
|
136
|
+
sshCmdArgs(`box@${this.domain}`),
|
|
137
|
+
`uninstall --uid ${this.uid}`,
|
|
138
|
+
appId
|
|
139
|
+
])
|
|
98
140
|
}
|
|
99
141
|
|
|
100
142
|
async devshell(appId, isUserApp, onconnect = null) {
|
|
@@ -111,8 +153,9 @@ export class DebugBridge {
|
|
|
111
153
|
const resolvedIp = await resolveDomain(this.domain)
|
|
112
154
|
|
|
113
155
|
const stream = spawn(
|
|
114
|
-
|
|
156
|
+
"ssh",
|
|
115
157
|
[
|
|
158
|
+
sshCmdArgs(`box@${resolvedIp}`),
|
|
116
159
|
"-t",
|
|
117
160
|
"devshell",
|
|
118
161
|
`--uid ${this.uid}`,
|
|
@@ -143,8 +186,8 @@ export class DebugBridge {
|
|
|
143
186
|
const stream = fs.createReadStream(contextTar)
|
|
144
187
|
|
|
145
188
|
const ssh = spawn(
|
|
146
|
-
|
|
147
|
-
[`build --tag ${tag}`],
|
|
189
|
+
"ssh",
|
|
190
|
+
[sshCmdArgs(`box@${resolvedIp}`), `build --tag ${tag}`],
|
|
148
191
|
{
|
|
149
192
|
shell: true,
|
|
150
193
|
stdio: ["pipe", "inherit", "inherit"]
|
package/lib/app/lpk_devshell.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import path from "node:path"
|
|
3
3
|
import fs from "node:fs"
|
|
4
4
|
import logger from "loglevel"
|
|
5
|
-
import { execSync } from "node:child_process"
|
|
5
|
+
// import { execSync } from "node:child_process"
|
|
6
|
+
import spawn from "cross-spawn"
|
|
6
7
|
import { LpkInstaller } from "./lpk_installer.js"
|
|
7
8
|
import debounce from "lodash.debounce"
|
|
8
9
|
import {
|
|
@@ -18,7 +19,11 @@ import {
|
|
|
18
19
|
isUserApp,
|
|
19
20
|
createTemplateFileCommon,
|
|
20
21
|
isDebugMode,
|
|
21
|
-
|
|
22
|
+
isTraceMode,
|
|
23
|
+
resolveDomain,
|
|
24
|
+
isWindows,
|
|
25
|
+
isMacOs,
|
|
26
|
+
isLinux
|
|
22
27
|
} from "../utils.js"
|
|
23
28
|
import os from "node:os"
|
|
24
29
|
import commandExists from "command-exists"
|
|
@@ -430,13 +435,15 @@ class DevShell {
|
|
|
430
435
|
"-o",
|
|
431
436
|
`"ConnectTimeout=30"`,
|
|
432
437
|
"-o",
|
|
433
|
-
`${
|
|
438
|
+
`${isTraceMode() ? '"LogLevel=DEBUG"' : '"LogLevel=ERROR"'}`
|
|
434
439
|
].join(" ")
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
+
if (isLinux || isMacOs) {
|
|
441
|
+
// 检查rsync工具是否存在:提示用户
|
|
442
|
+
const rsyncExisted = commandExists.sync("rsync")
|
|
443
|
+
if (!rsyncExisted) {
|
|
444
|
+
logger.error("请检查 rsync 是否安装,路径是否正确!")
|
|
445
|
+
process.exit(1)
|
|
446
|
+
}
|
|
440
447
|
}
|
|
441
448
|
|
|
442
449
|
const rsyncDebug = isDebugMode() ? "-P" : ""
|
|
@@ -444,11 +451,30 @@ class DevShell {
|
|
|
444
451
|
? `${shellApi.uid}.app.${appId}.lzcapp`
|
|
445
452
|
: `app.${appId}.lzcapp`
|
|
446
453
|
const dest = `root@${host}:/lzcapp/cache/devshell`
|
|
454
|
+
const rsyncCmd = isWindows
|
|
455
|
+
? path.join(
|
|
456
|
+
contextDirname(import.meta.url),
|
|
457
|
+
"../../template/_lpk/win-rsync/rsync.exe"
|
|
458
|
+
)
|
|
459
|
+
: `rsync`
|
|
460
|
+
|
|
447
461
|
try {
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
{
|
|
451
|
-
|
|
462
|
+
const rsyncArgs = [
|
|
463
|
+
`${rsyncDebug}`,
|
|
464
|
+
`--rsh='${rsh}'`,
|
|
465
|
+
`--recursive`,
|
|
466
|
+
`--relative`,
|
|
467
|
+
`--perms`,
|
|
468
|
+
`--update`,
|
|
469
|
+
`-F --filter=':- .gitignore'`,
|
|
470
|
+
`--ignore-errors`,
|
|
471
|
+
`. ${dest}`
|
|
472
|
+
]
|
|
473
|
+
logger.debug("同步代码: ", rsyncCmd, rsyncArgs.join(" "))
|
|
474
|
+
const rsyncStream = spawn.sync(rsyncCmd, rsyncArgs, {
|
|
475
|
+
shell: true,
|
|
476
|
+
stdio: ["ignore", isDebugMode() ? "inherit" : "ignore", "inherit"]
|
|
477
|
+
})
|
|
452
478
|
} catch (err) {
|
|
453
479
|
logger.error("rsync 同步失败")
|
|
454
480
|
logger.debug(err)
|
package/lib/shellapi.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import grpc from "@grpc/grpc-js"
|
|
2
2
|
import protoLoader from "@grpc/proto-loader"
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
contextDirname,
|
|
5
|
+
isMacOs,
|
|
6
|
+
isWindows,
|
|
7
|
+
isLinux,
|
|
8
|
+
isFileExist
|
|
9
|
+
} from "./utils.js"
|
|
4
10
|
import path from "node:path"
|
|
5
11
|
import fs from "node:fs"
|
|
6
12
|
import os from "node:os"
|
|
@@ -15,9 +21,9 @@ const bannerfileContent = `˄=ᆽ=ᐟ \\`
|
|
|
15
21
|
function getShellAPIConfigDir() {
|
|
16
22
|
const home = os.homedir()
|
|
17
23
|
let suffix = "/.config/hportal-client"
|
|
18
|
-
if (
|
|
24
|
+
if (isMacOs) {
|
|
19
25
|
suffix = "/Library/Application Support/hportal-client"
|
|
20
|
-
} else if (
|
|
26
|
+
} else if (isWindows) {
|
|
21
27
|
suffix = "\\AppData\\Roaming\\hportal-client"
|
|
22
28
|
}
|
|
23
29
|
const SHELLAPI_CONFIG_DIR = path.join(home, suffix)
|
|
@@ -28,19 +34,49 @@ class ShellApi {
|
|
|
28
34
|
constructor() {}
|
|
29
35
|
|
|
30
36
|
async init() {
|
|
31
|
-
const pbShell = this.initShell()
|
|
32
|
-
const { addr, cred } = this.readShellApiInfo()
|
|
33
|
-
this.client = new pbShell.ShellCore(addr, grpc.credentials.createInsecure())
|
|
34
|
-
|
|
35
37
|
// 检查当前 shell 环境上下文是否配置 HTTP_PROXY
|
|
36
38
|
if (this.checkEnvProxy()) {
|
|
37
|
-
|
|
39
|
+
logger.warn(
|
|
40
|
+
`WARN:: 当前终端环境已配置 HTTP_PROXY 代理,可能会导致访问懒猫微服失败,如果未影响功能可以忽略该警告`
|
|
41
|
+
)
|
|
38
42
|
}
|
|
39
43
|
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
const SHELLAPI_CONFIG_DIR = getShellAPIConfigDir()
|
|
45
|
+
const addrFile = path.resolve(SHELLAPI_CONFIG_DIR, "shellapi_addr")
|
|
46
|
+
const credFile = path.resolve(SHELLAPI_CONFIG_DIR, "shellapi_cred")
|
|
47
|
+
if (!isFileExist(addrFile) || !isFileExist(credFile)) {
|
|
48
|
+
logger.warn(`WARN:: 读取不到以下路径中的文件,无法连接客户端自动判读盒子名称和盒子用户,
|
|
49
|
+
|
|
50
|
+
${addrFile} 盒子的连接地址
|
|
51
|
+
${credFile} 盒子的连接凭证
|
|
52
|
+
|
|
53
|
+
但可使用环境变量指定:
|
|
54
|
+
- BOX_NAME=foo 指定盒子的名称
|
|
55
|
+
- BOX_UID=bar 指定盒子的用户
|
|
56
|
+
|
|
57
|
+
NOTE:在指定环境变量的模式下,有些接口依旧不能访问,为了更好的开发体验,建议尽量在同主机的环境下启动客户端`)
|
|
58
|
+
const md = new grpc.Metadata()
|
|
59
|
+
md.add("lzc-shellapi-cred", "fakecred")
|
|
60
|
+
this.metadata = md
|
|
61
|
+
const boxUid = process.env.BOX_UID
|
|
62
|
+
const boxName = process.env.BOX_NAME
|
|
63
|
+
if (!boxUid || !boxName) {
|
|
64
|
+
process.exit(1)
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
this.info = { uid: boxUid, boxname: boxName }
|
|
68
|
+
} else {
|
|
69
|
+
const pbShell = this.initShell()
|
|
70
|
+
const { addr, cred } = this.readShellApiInfo()
|
|
71
|
+
this.client = new pbShell.ShellCore(
|
|
72
|
+
addr,
|
|
73
|
+
grpc.credentials.createInsecure()
|
|
74
|
+
)
|
|
75
|
+
const md = new grpc.Metadata()
|
|
76
|
+
md.add("lzc-shellapi-cred", cred)
|
|
77
|
+
this.metadata = md
|
|
78
|
+
this.info = await this.initBoxInfo()
|
|
79
|
+
}
|
|
44
80
|
this.checkDevTools()
|
|
45
81
|
}
|
|
46
82
|
|
|
@@ -121,14 +157,15 @@ class ShellApi {
|
|
|
121
157
|
}
|
|
122
158
|
|
|
123
159
|
checkEnvProxy() {
|
|
124
|
-
if(
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
160
|
+
if (
|
|
161
|
+
process.env.HTTPS_PROXY ||
|
|
162
|
+
process.env.HTTP_PROXY ||
|
|
163
|
+
process.env.ALL_PROXY ||
|
|
164
|
+
process.env.https_proxy ||
|
|
165
|
+
process.env.http_proxy ||
|
|
166
|
+
process.env.all_proxy
|
|
167
|
+
) {
|
|
168
|
+
return true
|
|
132
169
|
}
|
|
133
170
|
return false
|
|
134
171
|
}
|
package/lib/utils.js
CHANGED
|
@@ -12,7 +12,7 @@ import https from "node:https"
|
|
|
12
12
|
import http from "node:http"
|
|
13
13
|
import zlib from "node:zlib"
|
|
14
14
|
import process from "node:process"
|
|
15
|
-
import
|
|
15
|
+
import spawn from "cross-spawn"
|
|
16
16
|
import logger from "loglevel"
|
|
17
17
|
import * as tar from "tar"
|
|
18
18
|
import dns from "node:dns/promises"
|
|
@@ -33,6 +33,10 @@ api.interceptors.response.use(
|
|
|
33
33
|
}
|
|
34
34
|
)
|
|
35
35
|
|
|
36
|
+
export const isMacOs = process.platform == "darwin"
|
|
37
|
+
export const isLinux = process.platform == "linux"
|
|
38
|
+
export const isWindows = process.platform == "win32"
|
|
39
|
+
|
|
36
40
|
export const envsubstr = async (templateContents, args) => {
|
|
37
41
|
const parse = await importDefault("envsub/js/envsub-parser.js")
|
|
38
42
|
return parse(templateContents, args)
|
|
@@ -350,7 +354,7 @@ export class FileLocker {
|
|
|
350
354
|
}
|
|
351
355
|
}
|
|
352
356
|
withOpen(filename) {
|
|
353
|
-
const p =
|
|
357
|
+
const p = spawn.sync("lsof", ["-w", "-t", filename])
|
|
354
358
|
if (p.status === 0) {
|
|
355
359
|
return true
|
|
356
360
|
} else {
|
|
@@ -438,6 +442,10 @@ export function isDebugMode() {
|
|
|
438
442
|
return logger.getLevel() <= logger.levels.DEBUG
|
|
439
443
|
}
|
|
440
444
|
|
|
445
|
+
export function isTraceMode() {
|
|
446
|
+
return logger.getLevel() <= logger.levels.TRACE
|
|
447
|
+
}
|
|
448
|
+
|
|
441
449
|
export async function resolveDomain(domain) {
|
|
442
450
|
try {
|
|
443
451
|
// Set machine's dns server as defalut dns server
|
|
@@ -466,17 +474,6 @@ export async function resolveDomain(domain) {
|
|
|
466
474
|
}
|
|
467
475
|
}
|
|
468
476
|
|
|
469
|
-
export function sshCmd(...args) {
|
|
470
|
-
const baseCommand = "ssh"
|
|
471
|
-
const defaultOptions = [
|
|
472
|
-
"-o StrictHostKeyChecking=no",
|
|
473
|
-
"-o UserKnownHostsFile=/dev/null",
|
|
474
|
-
"-q"
|
|
475
|
-
]
|
|
476
|
-
const options = [...defaultOptions, ...args].join(" ")
|
|
477
|
-
return `${baseCommand} ${options}`
|
|
478
|
-
}
|
|
479
|
-
|
|
480
477
|
export function unzipSync(zipPath, destPath) {
|
|
481
478
|
// 确保目标目录存在
|
|
482
479
|
fs.mkdirSync(destPath, { recursive: true })
|
|
@@ -486,3 +483,94 @@ export function unzipSync(zipPath, destPath) {
|
|
|
486
483
|
// 同步解压所有文件
|
|
487
484
|
zip.extractAllTo(destPath, true)
|
|
488
485
|
}
|
|
486
|
+
|
|
487
|
+
export function findSshPublicKey() {
|
|
488
|
+
// 获取用户 HOME 目录
|
|
489
|
+
let homeDir
|
|
490
|
+
switch (process.platform) {
|
|
491
|
+
case "win32":
|
|
492
|
+
// Windows: 通常是 C:\Users\username
|
|
493
|
+
homeDir = process.env.USERPROFILE || os.homedir()
|
|
494
|
+
break
|
|
495
|
+
case "darwin":
|
|
496
|
+
case "linux":
|
|
497
|
+
// macOS 和 Linux: 通常是 /Users/username 或 /home/username
|
|
498
|
+
homeDir = process.env.HOME || os.homedir()
|
|
499
|
+
break
|
|
500
|
+
default:
|
|
501
|
+
throw new Error("Unsupported operating system")
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
const sshDir = path.join(homeDir, ".ssh")
|
|
505
|
+
|
|
506
|
+
// 检查 .ssh 目录是否存在
|
|
507
|
+
try {
|
|
508
|
+
const dirStat = fs.statSync(sshDir)
|
|
509
|
+
if (!dirStat.isDirectory()) {
|
|
510
|
+
throw new Error(
|
|
511
|
+
".ssh 目前存在,但不是一个正常的目录,请确保 ssh 已经安装,以及 ssh-keygen 生成公钥"
|
|
512
|
+
)
|
|
513
|
+
}
|
|
514
|
+
} catch (error) {
|
|
515
|
+
if (error.code === "ENOENT") {
|
|
516
|
+
throw new Error(
|
|
517
|
+
".ssh 目录不存在, 请确保 ssh 已经安装,以及 ssh-keygen 生成公钥"
|
|
518
|
+
)
|
|
519
|
+
}
|
|
520
|
+
throw error
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// 定义公钥优先级列表
|
|
524
|
+
const priorityKeys = [
|
|
525
|
+
"id_ed25519.pub",
|
|
526
|
+
"id_rsa.pub",
|
|
527
|
+
"id_ecdsa.pub",
|
|
528
|
+
"id_dsa.pub"
|
|
529
|
+
]
|
|
530
|
+
|
|
531
|
+
try {
|
|
532
|
+
// 获取 .ssh 目录下所有文件
|
|
533
|
+
const files = fs.readdirSync(sshDir)
|
|
534
|
+
|
|
535
|
+
// 首先检查优先级列表中的公钥
|
|
536
|
+
for (const keyName of priorityKeys) {
|
|
537
|
+
if (files.includes(keyName)) {
|
|
538
|
+
const keyPath = path.join(sshDir, keyName)
|
|
539
|
+
// 验证文件是否可读
|
|
540
|
+
try {
|
|
541
|
+
fs.accessSync(keyPath, fs.constants.R_OK)
|
|
542
|
+
return {
|
|
543
|
+
path: keyPath,
|
|
544
|
+
content: fs.readFileSync(keyPath, "utf8")
|
|
545
|
+
}
|
|
546
|
+
} catch (error) {
|
|
547
|
+
console.warn(`Found ${keyName} but cannot read it:`, error.message)
|
|
548
|
+
continue
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// 如果没找到优先级列表中的公钥,查找任何 .pub 结尾的文件
|
|
554
|
+
const pubKey = files.find((file) => file.endsWith(".pub"))
|
|
555
|
+
if (pubKey) {
|
|
556
|
+
const keyPath = path.join(sshDir, pubKey)
|
|
557
|
+
try {
|
|
558
|
+
fs.accessSync(keyPath, fs.constants.R_OK)
|
|
559
|
+
return {
|
|
560
|
+
path: keyPath,
|
|
561
|
+
content: fs.readFileSync(keyPath, "utf8")
|
|
562
|
+
}
|
|
563
|
+
} catch (error) {
|
|
564
|
+
throw new Error(`Found ${pubKey} but cannot read it: ${error.message}`)
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
throw new Error(
|
|
568
|
+
".ssh 目录没有没有找到任何 .pub 公钥. 请使用 ssh-keygen 生成"
|
|
569
|
+
)
|
|
570
|
+
} catch (error) {
|
|
571
|
+
if (error.code === "ENOENT") {
|
|
572
|
+
throw new Error("不能查询 .ssh 目录")
|
|
573
|
+
}
|
|
574
|
+
throw error
|
|
575
|
+
}
|
|
576
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazycatcloud/lzc-cli",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.39",
|
|
4
4
|
"description": "lazycat cloud developer kit",
|
|
5
5
|
"files": [
|
|
6
6
|
"template",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"chalk": "^5.3.0",
|
|
27
27
|
"chokidar": "^3.6.0",
|
|
28
28
|
"command-exists": "^1.2.9",
|
|
29
|
+
"cross-spawn": "^7.0.3",
|
|
29
30
|
"dockerfile-ast": "^0.6.1",
|
|
30
31
|
"envsub": "^4.1.0",
|
|
31
32
|
"fast-glob": "^3.3.2",
|
|
Binary file
|
|
Binary file
|