@lazycatcloud/lzc-cli 1.2.1 → 1.2.3
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/lib/app/lpk_debug_bridge.js +13 -3
- package/lib/app/lpk_devshell.js +12 -10
- package/lib/box/index.js +40 -3
- package/lib/shellapi.js +28 -10
- package/lib/utils.js +4 -0
- package/package.json +1 -1
- package/template/_lpk/exec.sh +1 -0
- package/template/_lpk/init_debug_bridge.sh +15 -8
- package/lib/lzc_sdk.js +0 -83
|
@@ -71,7 +71,10 @@ export class DebugBridge {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
async isDevshell(appId) {
|
|
74
|
-
const stdout = await this.common(this.sshCmd, [
|
|
74
|
+
const stdout = await this.common(this.sshCmd, [
|
|
75
|
+
`isDevshell --uid ${this.uid}`,
|
|
76
|
+
appId,
|
|
77
|
+
]);
|
|
75
78
|
return stdout == "true";
|
|
76
79
|
}
|
|
77
80
|
|
|
@@ -83,10 +86,17 @@ export class DebugBridge {
|
|
|
83
86
|
return this.common(this.sshCmd, [`uninstall --uid ${this.uid}`, appId]);
|
|
84
87
|
}
|
|
85
88
|
|
|
86
|
-
async devshell(appId) {
|
|
89
|
+
async devshell(appId, isUserApp) {
|
|
87
90
|
const stream = spawn(
|
|
88
91
|
this.sshCmd,
|
|
89
|
-
[
|
|
92
|
+
[
|
|
93
|
+
"-t",
|
|
94
|
+
"devshell",
|
|
95
|
+
`--uid ${this.uid}`,
|
|
96
|
+
isUserApp ? "--userapp" : "",
|
|
97
|
+
appId,
|
|
98
|
+
"/lzcapp/pkg/content/devshell/exec.sh",
|
|
99
|
+
],
|
|
90
100
|
{
|
|
91
101
|
shell: true,
|
|
92
102
|
stdio: "inherit",
|
package/lib/app/lpk_devshell.js
CHANGED
|
@@ -15,12 +15,12 @@ import {
|
|
|
15
15
|
md5File,
|
|
16
16
|
loadFromYaml,
|
|
17
17
|
FileLocker,
|
|
18
|
+
isUserApp,
|
|
18
19
|
} from "../utils.js";
|
|
19
20
|
import os from "node:os";
|
|
20
21
|
import commandExists from "command-exists";
|
|
21
22
|
import chokidar from "chokidar";
|
|
22
23
|
import _ from "lodash";
|
|
23
|
-
import { getUidByManifest } from "../lzc_sdk.js";
|
|
24
24
|
import { DebugBridge } from "./lpk_debug_bridge.js";
|
|
25
25
|
import shellApi from "../shellapi.js";
|
|
26
26
|
|
|
@@ -107,16 +107,16 @@ export class AppDevShell {
|
|
|
107
107
|
this.lpkBuild = lpkBuild;
|
|
108
108
|
this.monitor = undefined;
|
|
109
109
|
this.forceBuild = forceBuild;
|
|
110
|
+
this.isUserApp = false;
|
|
110
111
|
}
|
|
111
112
|
|
|
112
113
|
async init() {
|
|
113
114
|
const manifest = await this.lpkBuild.getManifest();
|
|
114
|
-
const uid = await getUidByManifest(manifest);
|
|
115
115
|
this.monitor = await new AppDevShellMonitor(
|
|
116
116
|
this.cwd,
|
|
117
|
-
manifest["package"]
|
|
118
|
-
uid
|
|
117
|
+
manifest["package"]
|
|
119
118
|
).init();
|
|
119
|
+
this.isUserApp = isUserApp(manifest);
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
async build() {
|
|
@@ -337,8 +337,7 @@ export class AppDevShell {
|
|
|
337
337
|
logger.debug(err);
|
|
338
338
|
}
|
|
339
339
|
|
|
340
|
-
const
|
|
341
|
-
const devshell = new DevShell(pkgId, uid);
|
|
340
|
+
const devshell = new DevShell(pkgId, this.isUserApp);
|
|
342
341
|
if (isSync) {
|
|
343
342
|
await devshell.shell();
|
|
344
343
|
} else {
|
|
@@ -352,9 +351,9 @@ export class AppDevShell {
|
|
|
352
351
|
}
|
|
353
352
|
|
|
354
353
|
class DevShell {
|
|
355
|
-
constructor(appId,
|
|
354
|
+
constructor(appId, isUserApp) {
|
|
356
355
|
this.appId = appId;
|
|
357
|
-
this.
|
|
356
|
+
this.isUserApp = isUserApp;
|
|
358
357
|
}
|
|
359
358
|
|
|
360
359
|
async syncProject(appId) {
|
|
@@ -377,7 +376,10 @@ class DevShell {
|
|
|
377
376
|
}
|
|
378
377
|
|
|
379
378
|
const rsyncDebug = process.env.RSYNCDEBUG ? "-P" : "";
|
|
380
|
-
const
|
|
379
|
+
const host = this.isUserApp
|
|
380
|
+
? `${shellApi.uid}.app.${appId}.lzcapp`
|
|
381
|
+
: `app.${appId}.lzcapp`;
|
|
382
|
+
const dest = `root@${host}:/lzcapp/cache/devshell`;
|
|
381
383
|
try {
|
|
382
384
|
execSync(
|
|
383
385
|
`rsync ${rsyncDebug} --rsh='${rsh}' --recursive --relative --perms --update --filter=':- .gitignore' --ignore-errors --usermap=:nobody --groupmap=*:nobody . ${dest}`,
|
|
@@ -466,6 +468,6 @@ class DevShell {
|
|
|
466
468
|
|
|
467
469
|
async connectShell() {
|
|
468
470
|
const bridge = new DebugBridge();
|
|
469
|
-
await bridge.devshell(this.appId);
|
|
471
|
+
await bridge.devshell(this.appId, this.isUserApp);
|
|
470
472
|
}
|
|
471
473
|
}
|
package/lib/box/index.js
CHANGED
|
@@ -1,10 +1,47 @@
|
|
|
1
|
+
import shellapi from "../shellapi.js";
|
|
2
|
+
|
|
1
3
|
export function boxCommand(box) {
|
|
2
4
|
let subCommands = [
|
|
3
5
|
{
|
|
4
|
-
command: "switch <
|
|
6
|
+
command: "switch <boxname>",
|
|
5
7
|
desc: "设置默认的盒子",
|
|
6
|
-
handler: async () => {
|
|
7
|
-
|
|
8
|
+
handler: async ({ boxname }) => {
|
|
9
|
+
await shellapi.init();
|
|
10
|
+
await shellapi.setDefaultBox(boxname);
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
command: "list",
|
|
15
|
+
desc: "查看盒子列表",
|
|
16
|
+
builder: (args) => {
|
|
17
|
+
args.option("v", {
|
|
18
|
+
alias: "verbose",
|
|
19
|
+
describe: "查看详细输出",
|
|
20
|
+
type: "boolean",
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
handler: async ({ verbose }) => {
|
|
24
|
+
await shellapi.init();
|
|
25
|
+
const boxes = await shellapi.boxList();
|
|
26
|
+
if (boxes.length === 0) {
|
|
27
|
+
console.log("没有找到任何盒子,赶紧添加一个吧!");
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (verbose) {
|
|
31
|
+
console.log(JSON.stringify(boxes, undefined, "\t"));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const list = boxes.map((b) => {
|
|
36
|
+
return {
|
|
37
|
+
名称: b.box_name,
|
|
38
|
+
状态: b.status,
|
|
39
|
+
登录用户: b.login_user,
|
|
40
|
+
是否管理员: b.is_admin_login ? "✔" : "✖",
|
|
41
|
+
是否默认盒子: b.is_default_box ? "✔" : "✖",
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
console.table(list);
|
|
8
45
|
},
|
|
9
46
|
},
|
|
10
47
|
];
|
package/lib/shellapi.js
CHANGED
|
@@ -71,25 +71,43 @@ class ShellApi {
|
|
|
71
71
|
return grpc.loadPackageDefinition(coreDefinition).space.heiyu.hportal.shell;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
async
|
|
74
|
+
async boxList() {
|
|
75
75
|
return new Promise((resolve, reject) => {
|
|
76
76
|
this.client.queryBoxList({}, this.metadata, function (err, response) {
|
|
77
77
|
if (err) {
|
|
78
78
|
reject(err);
|
|
79
79
|
return;
|
|
80
80
|
}
|
|
81
|
-
|
|
82
|
-
if (box.is_default_box) {
|
|
83
|
-
resolve({ uid: box.login_user, boxname: box.box_name });
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
reject(
|
|
88
|
-
"没有默认盒子信息, 请先使用 lzc-cli box switch 设置默认的盒子信息"
|
|
89
|
-
);
|
|
81
|
+
resolve(response.boxes);
|
|
90
82
|
});
|
|
91
83
|
});
|
|
92
84
|
}
|
|
85
|
+
|
|
86
|
+
async initBoxInfo() {
|
|
87
|
+
const boxes = await this.boxList();
|
|
88
|
+
const box = boxes.find((b) => b.is_default_box);
|
|
89
|
+
if (box) {
|
|
90
|
+
return { uid: box.login_user, boxname: box.box_name };
|
|
91
|
+
}
|
|
92
|
+
throw "没有默认盒子信息, 请先使用 lzc-cli box switch 设置默认的盒子信息";
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async setDefaultBox(boxname) {
|
|
96
|
+
const boxes = await this.boxList();
|
|
97
|
+
const box = boxes.find((b) => b.box_name === boxname);
|
|
98
|
+
if (!box) {
|
|
99
|
+
throw `${boxname} 盒子不存在`;
|
|
100
|
+
}
|
|
101
|
+
return new Promise((resolve, reject) => {
|
|
102
|
+
this.client.modifyBoxConfig(
|
|
103
|
+
{ id: box.id, name: box.box_name, set_as_default_box: true },
|
|
104
|
+
this.metadata,
|
|
105
|
+
function (err) {
|
|
106
|
+
err ? reject(err) : resolve();
|
|
107
|
+
}
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
93
111
|
}
|
|
94
112
|
|
|
95
113
|
export default new ShellApi();
|
package/lib/utils.js
CHANGED
|
@@ -294,3 +294,7 @@ export function isValidPackageName(packageName) {
|
|
|
294
294
|
const regex = new RegExp("^(?:[a-z][a-z0-9_]*\\.)+[a-z][a-z0-9_]*$");
|
|
295
295
|
return regex.test(packageName);
|
|
296
296
|
}
|
|
297
|
+
|
|
298
|
+
export function isUserApp(manifest) {
|
|
299
|
+
return !!manifest["application"]["user_app"];
|
|
300
|
+
}
|
package/package.json
CHANGED
package/template/_lpk/exec.sh
CHANGED
|
@@ -7,18 +7,25 @@ BUSYBOX=/lzcapp/pkg/content/devshell/busybox
|
|
|
7
7
|
mkdir -p /root/.ssh
|
|
8
8
|
mkdir -p /etc/debug.bridge
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
if ! [ -f /usr/bin/dropbearmulti ]; then
|
|
11
|
+
${BUSYBOX} wget ${DEBUG_BRIDGE_LZCAPP}/resources/dropbearmulti -O /usr/bin/dropbearmulti
|
|
12
|
+
chmod +x /usr/bin/dropbearmulti
|
|
13
|
+
fi
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
if ! [ -f /usr/bin/rsync ]; then
|
|
16
|
+
${BUSYBOX} wget ${DEBUG_BRIDGE_LZCAPP}/resources/rsync -O /usr/bin/rsync
|
|
17
|
+
chmod +x /usr/bin/rsync
|
|
18
|
+
fi
|
|
15
19
|
|
|
16
20
|
${BUSYBOX} wget ${DEBUG_BRIDGE_LZCAPP}/resources/authorized_keys -O /root/.ssh/authorized_keys
|
|
17
21
|
chmod 0644 /root/.ssh/authorized_keys
|
|
18
22
|
|
|
19
23
|
${BUSYBOX} wget ${DEBUG_BRIDGE_LZCAPP}/bannerfile -O /etc/debug.bridge/bannerfile
|
|
20
24
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
if ${BUSYBOX} netstat -tln | grep ':22222' >/dev/null; then
|
|
26
|
+
echo "端口22222正在监听"
|
|
27
|
+
${BUSYBOX} sleep infinity
|
|
28
|
+
else
|
|
29
|
+
mkdir -p /etc/dropbear
|
|
30
|
+
exec dropbearmulti dropbear -R -F -E -B -p 22222
|
|
31
|
+
fi
|
package/lib/lzc_sdk.js
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import grpc from "@grpc/grpc-js";
|
|
2
|
-
import protoLoader from "@grpc/proto-loader";
|
|
3
|
-
import { contextDirname } from "./utils.js";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
import env from "./env.js";
|
|
6
|
-
import logger from "loglevel";
|
|
7
|
-
import os from "node:os";
|
|
8
|
-
import fs from "node:fs";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* @link {https://www.npmjs.com/package/appdirs}
|
|
12
|
-
*/
|
|
13
|
-
function getShellAPIConfigDir() {
|
|
14
|
-
const home = os.homedir();
|
|
15
|
-
const isMacos = process.platform == "darwin";
|
|
16
|
-
let sufix = "/.config/hportal-client";
|
|
17
|
-
if (isMacos) {
|
|
18
|
-
sufix = "/Library/Application Support/hportal-client";
|
|
19
|
-
} // TODO: need reimpl fetch windows
|
|
20
|
-
const SHELLAPI_CONFIG_DIR = path.join(home, sufix);
|
|
21
|
-
return SHELLAPI_CONFIG_DIR;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const coreDefinition = protoLoader.loadSync(
|
|
25
|
-
path.join(contextDirname(), "./shellapi.proto"),
|
|
26
|
-
{
|
|
27
|
-
keepCase: true,
|
|
28
|
-
longs: String,
|
|
29
|
-
enums: String,
|
|
30
|
-
defaults: true,
|
|
31
|
-
oneofs: true,
|
|
32
|
-
}
|
|
33
|
-
);
|
|
34
|
-
const core =
|
|
35
|
-
grpc.loadPackageDefinition(coreDefinition).space.heiyu.hportal.shell;
|
|
36
|
-
|
|
37
|
-
let client;
|
|
38
|
-
|
|
39
|
-
function readShellApiInfo() {
|
|
40
|
-
const SHELLAPI_CONFIG_DIR = getShellAPIConfigDir();
|
|
41
|
-
const addr = fs.readFileSync(
|
|
42
|
-
path.resolve(SHELLAPI_CONFIG_DIR, "shellapi_addr"),
|
|
43
|
-
{ encoding: "utf-8" }
|
|
44
|
-
);
|
|
45
|
-
const cred = fs.readFileSync(
|
|
46
|
-
path.resolve(SHELLAPI_CONFIG_DIR, "shellapi_cred"),
|
|
47
|
-
{ encoding: "utf-8" }
|
|
48
|
-
);
|
|
49
|
-
return { addr, cred };
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export async function getUidByManifest(manifest) {
|
|
53
|
-
if (!manifest["application"]["user_app"]) {
|
|
54
|
-
return "";
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const { addr, cred } = readShellApiInfo();
|
|
58
|
-
|
|
59
|
-
if (!client) {
|
|
60
|
-
client = new core.ShellCore(addr, grpc.credentials.createInsecure());
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const metadata = new grpc.Metadata();
|
|
64
|
-
metadata.add("lzc-shellapi-cred", cred);
|
|
65
|
-
|
|
66
|
-
const boxName = env.get("DEFAULT_BOXNAME");
|
|
67
|
-
return new Promise((resolve, reject) => {
|
|
68
|
-
client.queryBoxList({}, metadata, function (err, response) {
|
|
69
|
-
if (err) {
|
|
70
|
-
reject(err);
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
for (let box of response.boxes) {
|
|
74
|
-
if (box.box_name == boxName) {
|
|
75
|
-
logger.debug("当前登录用户: ", box.login_user);
|
|
76
|
-
resolve(box.login_user);
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
reject("没有默认盒子信息");
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
}
|