@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 CHANGED
@@ -3,9 +3,8 @@
3
3
  #### 依赖
4
4
 
5
5
  1. `ssh`
6
- 2. `ssh-copy-id`
7
- 3. `rsync`
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 模块对接盒子帐号体系。
@@ -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
- } else if (resp.status == 201) {
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)
@@ -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 { spawnSync } from "child_process"
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 (process.platform == "darwin") {
83
- const result = spawnSync("sh", ["-c", "ifconfig"], {
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 (process.platform == "win32") {
94
+ } else if (isWindows) {
92
95
  logger.debug(`当前 Windows 不支持使用 LocalIP 反代`)
93
96
  return ""
94
97
  } else {
95
- const result = spawnSync("ip", ["addr", "show", "heiyu-0"], {
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
- let p = spawnSync("sh", ["-c", this.options["buildscript"]], {
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 { spawnSync } from "node:child_process"
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
- spawnSync("npm", ["init", v, path.basename(to)], { stdio: "inherit" })
78
+ spawn.sync("npm", ["init", v, path.basename(to)], { stdio: "inherit" })
79
79
  },
80
80
  after: async function (name, cwd) {
81
- spawnSync("npm", ["install"], {
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
- spawnSync("npm", ["install"], {
185
+ spawn.sync("npm", ["install"], {
186
186
  cwd: path.join(cwd, name),
187
187
  stdio: "inherit"
188
188
  })
@@ -1,26 +1,38 @@
1
- import { spawn, spawnSync } from "child_process"
1
+ import spawn from "cross-spawn"
2
2
  import fs from "node:fs"
3
3
  import shellApi from "../shellapi.js"
4
- import { isDebugMode, resolveDomain, sleep, sshCmd } from "../utils.js"
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 = spawnSync(resolvedCmd, args, {
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
- sshCmd("-p 22222", `box@${resolvedIp}`),
47
- [`install --uid ${this.uid} --pkgId ${pkgId}`],
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
- // 检查rsync工具是否存在:提示用户
68
- const existed = commandExists.sync("ssh-copy-id")
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
- return this.common(`ssh-copy-id`, [
76
- `-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -f -p 22222 box@${resolvedIp}`
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(this.sshCmd, [`status --uid ${this.uid}`, appId])
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(this.sshCmd, [
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(this.sshCmd, [`resume --uid ${this.uid}`, appId])
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(this.sshCmd, [`uninstall --uid ${this.uid}`, appId])
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
- sshCmd("-p 22222", `box@${resolvedIp}`),
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
- sshCmd("-p 22222", `box@${resolvedIp}`),
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"]
@@ -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
- resolveDomain
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
- `${isDebugMode() ? '"LogLevel=DEBUG"' : '"LogLevel=ERROR"'}`
438
+ `${isTraceMode() ? '"LogLevel=DEBUG"' : '"LogLevel=ERROR"'}`
434
439
  ].join(" ")
435
- // 检查rsync工具是否存在:提示用户
436
- const rsyncExisted = commandExists.sync("rsync")
437
- if (!rsyncExisted) {
438
- logger.error("请检查 rsync 是否安装,路径是否正确!")
439
- process.exit(1)
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
- execSync(
449
- `rsync ${rsyncDebug} --rsh='${rsh}' --recursive --relative --perms --update -F --filter=':- .gitignore' --ignore-errors . ${dest}`,
450
- { stdio: ["ignore", isDebugMode() ? "inherit" : "ignore", "inherit"] }
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 { contextDirname } from "./utils.js"
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 (process.platform === "darwin") {
24
+ if (isMacOs) {
19
25
  suffix = "/Library/Application Support/hportal-client"
20
- } else if (process.platform === "win32") {
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
- logger.warn(`WARN:: 当前终端环境已配置 HTTP_PROXY 代理,可能会导致访问懒猫微服失败,如果未影响功能可以忽略该警告`)
39
+ logger.warn(
40
+ `WARN:: 当前终端环境已配置 HTTP_PROXY 代理,可能会导致访问懒猫微服失败,如果未影响功能可以忽略该警告`
41
+ )
38
42
  }
39
43
 
40
- const md = new grpc.Metadata()
41
- md.add("lzc-shellapi-cred", cred)
42
- this.metadata = md
43
- this.info = await this.initBoxInfo()
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(process.env.HTTPS_PROXY ||
125
- process.env.HTTP_PROXY ||
126
- process.env.ALL_PROXY ||
127
- process.env.https_proxy ||
128
- process.env.http_proxy ||
129
- process.env.all_proxy
130
- ){
131
- return true
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 { spawnSync } from "node:child_process"
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 = spawnSync("lsof", ["-w", "-t", filename])
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.37",
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",