@lazycatcloud/lzc-cli 1.1.13 → 1.2.1

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.
Files changed (40) hide show
  1. package/README.md +30 -62
  2. package/lib/app/index.js +33 -35
  3. package/lib/app/lpk_debug_bridge.js +101 -0
  4. package/lib/app/lpk_devshell.js +55 -114
  5. package/lib/app/lpk_installer.js +43 -40
  6. package/lib/appstore/login.js +1 -1
  7. package/lib/appstore/publish.js +18 -8
  8. package/lib/box/index.js +2 -86
  9. package/lib/env.js +18 -178
  10. package/lib/lzc_sdk.js +16 -2
  11. package/lib/shellapi.js +95 -0
  12. package/lib/shellapi.proto +301 -0
  13. package/lib/utils.js +2 -103
  14. package/package.json +4 -26
  15. package/template/_lpk/busybox-1.35.0 +0 -0
  16. package/template/_lpk/exec.sh +1 -10
  17. package/template/_lpk/init_debug_bridge.sh +24 -0
  18. package/template/_lpk/vue.lzc-build.yml.in +0 -16
  19. package/template/golang/lzc-build.yml +0 -15
  20. package/template/ionic_vue3/lzc-build.yml +0 -16
  21. package/template/ionic_vue3/package-lock.json +8100 -0
  22. package/template/lite/lzc-build.yml +0 -28
  23. package/lib/api.js +0 -155
  24. package/lib/app/lpk_devshell_docker.js +0 -211
  25. package/lib/app/lpk_log.js +0 -68
  26. package/lib/app/lpk_status.js +0 -18
  27. package/lib/app/lpk_uninstall.js +0 -19
  28. package/lib/box/api/clientapi.js +0 -1322
  29. package/lib/box/api/empty.js +0 -35
  30. package/lib/box/check_qemu.js +0 -51
  31. package/lib/box/qemu_vm_mgr.js +0 -608
  32. package/lib/box/schemes/vm_box_system_debian.json +0 -47
  33. package/lib/core.proto +0 -118
  34. package/lib/docker/promise.js +0 -91
  35. package/lib/docker-compose.js +0 -50
  36. package/lib/fetch.js +0 -50
  37. package/lib/git/git-commit.sh +0 -7
  38. package/lib/git/git-reset.sh +0 -15
  39. package/lib/key.js +0 -102
  40. package/lib/sdk.js +0 -135
@@ -1,54 +1,64 @@
1
- import BoxAPI from "../api.js";
2
- import { sdkEnv } from "../env.js";
3
1
  import logger from "loglevel";
4
2
  import fs from "node:fs";
5
3
  import path from "node:path";
6
- import Key from "../key.js";
7
4
  import { loadFromYaml, Downloader } from "../utils.js";
8
5
  import { spawnSync } from "node:child_process";
9
- import { getUidByManifest } from "../lzc_sdk.js";
6
+ import { DebugBridge } from "./lpk_debug_bridge.js";
7
+ import shellapi from "../shellapi.js";
8
+
9
+ // 从一个目录中找出修改时间最新的包
10
+ function findOnceLpkByDir(dir = process.cwd()) {
11
+ const pkg = fs
12
+ .readdirSync(dir)
13
+ .map((filename) => {
14
+ const stat = fs.statSync(path.resolve(dir, filename));
15
+ return { filename, mtime: stat.mtime };
16
+ })
17
+ .sort((a, b) => {
18
+ return b.mtime - a.mtime;
19
+ })
20
+ .find((entry) => {
21
+ return entry.filename.endsWith(".lpk");
22
+ });
23
+ return pkg?.filename ? path.resolve(dir, pkg.filename) : "";
24
+ }
10
25
 
11
26
  export class LpkInstaller {
12
27
  constructor() {}
13
28
 
14
- async init() {
15
- // 1. 确保 sdk 已经安装
16
- await sdkEnv.ensure();
17
-
18
- // 2. pull 镜像需要ssh key
19
- const k = new Key();
20
- await k.ensure(sdkEnv.sdkUrl);
21
- }
29
+ async init() {}
22
30
 
23
- // TODO: 将 devshell 的判断逻辑放在 builder 中判断
24
- async deploy(builder, isDevshell = false) {
31
+ // deploy 构建和安装
32
+ async deploy(builder) {
25
33
  if (!builder) {
26
34
  throw "deploy 必须传递一个 builder";
27
35
  }
28
36
 
29
37
  let manifest = await builder.getManifest();
30
- let uid = await getUidByManifest(manifest);
31
- let api = new BoxAPI(manifest["package"], sdkEnv.sdkUrl, uid);
32
-
33
- let pkgPath = await builder.exec("");
38
+ let pkgPath = await builder.exec();
34
39
  logger.info("开始部署应用");
35
- await api.install(pkgPath, isDevshell);
36
-
37
- const appUrl = sdkEnv.sdkUrl.replace(
38
- /sdk/,
39
- manifest["application"]["subdomain"]
40
- );
41
- // 新安装的应用默认为休眠的状态,需要先等容器创建成功,等成为 paused 后 resume 下
42
- await api.checkStatus(true);
40
+ const bridge = new DebugBridge();
41
+ await bridge.install(pkgPath);
42
+ const appUrl = `https://${manifest["application"]["subdomain"]}.${shellapi.boxname}.heiyu.space`;
43
43
  logger.info(`👉 请在浏览器中访问 ${appUrl}`);
44
44
  }
45
45
 
46
- async install(pkgUrl) {
47
- if (pkgUrl.startsWith("http")) {
48
- return this.installFromUrl(pkgUrl);
46
+ async install(pkgPath) {
47
+ if (pkgPath.startsWith("http")) {
48
+ return this.installFromUrl(pkgPath);
49
+ } else if (fs.statSync(pkgPath).isDirectory()) {
50
+ return this.installFromDirectory(pkgPath);
49
51
  } else {
50
- return this.installFromFile(pkgUrl);
52
+ return this.installFromFile(pkgPath);
53
+ }
54
+ }
55
+
56
+ async installFromDirectory(dir) {
57
+ const lpk = findOnceLpkByDir(dir);
58
+ if (lpk) {
59
+ logger.info(`正在安装自动扫描出来的lpk包(${lpk})`);
51
60
  }
61
+ return this.installFromFile(lpk);
52
62
  }
53
63
 
54
64
  async installFromUrl(url) {
@@ -77,28 +87,21 @@ export class LpkInstaller {
77
87
  }
78
88
 
79
89
  const tempDir = fs.mkdtempSync(".lzc-cli-install");
80
- let pkgName;
81
90
  let manifest;
82
91
  try {
83
92
  spawnSync("unzip", ["-d", tempDir, pkgPath]);
84
-
85
93
  manifest = loadFromYaml(path.join(tempDir, "manifest.yml"));
86
94
  logger.debug("lpk manifest", manifest);
87
-
88
- pkgName = manifest.package;
89
95
  } finally {
90
96
  fs.rmSync(tempDir, { recursive: true });
91
97
  }
92
98
 
93
- let api = new BoxAPI(pkgName, sdkEnv.sdkUrl);
99
+ const bridge = new DebugBridge();
94
100
  logger.info("开始安装应用");
95
- await api.install(pkgPath);
101
+ await bridge.install(pkgPath);
96
102
  logger.info(`安装成功!`);
97
103
  logger.info(
98
- `👉 请在浏览器中访问 ${sdkEnv.sdkUrl.replace(
99
- /sdk/,
100
- manifest["application"]["subdomain"]
101
- )}`
104
+ `👉 请在浏览器中访问 https://${manifest["application"]["subdomain"]}.${shellapi.boxname}.heiyu.space`
102
105
  );
103
106
  }
104
107
  }
@@ -1,4 +1,4 @@
1
- import fetch from "../fetch.js";
1
+ import fetch from "node-fetch";
2
2
  import path from "path";
3
3
  import logger from "loglevel";
4
4
  import env from "../env.js";
@@ -20,7 +20,7 @@ async function askChangeLog() {
20
20
  }
21
21
 
22
22
  export class Publish {
23
- constructor(baseUrl = "https://developer.lazycat.cloud/api/app") {
23
+ constructor(baseUrl = "https://developer.lazycat.cloud/api/v2/developer") {
24
24
  this.baseUrl = baseUrl;
25
25
  }
26
26
 
@@ -42,22 +42,32 @@ export class Publish {
42
42
  changelog = answer.changelog;
43
43
  }
44
44
 
45
- logger.info("正在发布...");
45
+ logger.info("正在提交审核...");
46
46
  const form = new FormData();
47
- form.append("files", fs.createReadStream(pkgPath));
48
- form.append("changelog", changelog);
47
+ form.append("file", fs.createReadStream(pkgPath));
49
48
 
50
- return request(this.baseUrl + "/update", {
49
+ const res = await request(this.baseUrl + "/upload_lpk", {
51
50
  method: "POST",
52
51
  body: form,
52
+ });
53
+ const lpkInfo = await res.json();
54
+ logger.debug("upload lpk response", lpkInfo);
55
+
56
+ return request(this.baseUrl + `/apps/${lpkInfo.package}/reviews`, {
57
+ method: "POST",
58
+ body: JSON.stringify({
59
+ changelog,
60
+ iconPath: lpkInfo.iconPath,
61
+ pkgPath: lpkInfo.url,
62
+ }),
53
63
  }).then(async (res) => {
54
- if (res.status != 200) {
64
+ if (res.status != 201) {
55
65
  logger.error(await res.text());
56
66
  return;
57
67
  }
58
68
 
59
- logger.info("发布成功!");
60
- logger.debug("publish response", await res.json());
69
+ logger.info("应用提交成功! 请等待审核结果");
70
+ logger.debug("publish response", await res.text());
61
71
  return;
62
72
  });
63
73
  }
package/lib/box/index.js CHANGED
@@ -1,94 +1,10 @@
1
- #!/bin/node
2
-
3
- import fs from "node:fs";
4
- import path from "node:path";
5
- import { QemuVM, QemuResource } from "./qemu_vm_mgr.js";
6
- import { contextDirname, envsubstrDefault, randomString } from "../utils.js";
7
- import env, { sdkEnv } from "../env.js";
8
- import { CheckQemu } from "./check_qemu.js";
9
- import logger from "loglevel";
10
-
11
1
  export function boxCommand(box) {
12
2
  let subCommands = [
13
- {
14
- command: "create",
15
- desc: "创建一个虚拟盒子,并注册运行",
16
- handler: async () => {
17
- const cq = await new CheckQemu().init();
18
- const m = await new QemuVM().init();
19
-
20
- const scheme = m.scheme;
21
- const resource = await new QemuResource(scheme.path).init();
22
-
23
- const name = randomString();
24
-
25
- let boxId;
26
- try {
27
- // 创建盒子阶段,如果出错,将直接删除所有的资源。
28
- boxId = await m.createVM(resource, name);
29
- } catch (error) {
30
- logger.error(error);
31
- await m.cleanVM(name);
32
- return;
33
- }
34
- console.log(
35
- `\n盒子初始化成功
36
- 请在浏览器中打开 http://127.0.0.1:30554/add?boxId=${boxId} 完成盒子注册`
37
- );
38
- },
39
- },
40
- {
41
- command: "start <boxNameOrBoxId>",
42
- desc: "启动一个虚拟盒子",
43
- handler: async ({ boxNameOrBoxId }) => {
44
- const m = await new QemuVM().init();
45
- let boxid = await m.runVM(boxNameOrBoxId);
46
- if (boxid) {
47
- logger.info("盒子ID: ", boxid);
48
- }
49
- },
50
- },
51
- {
52
- command: "stop <boxNameOrBoxId>",
53
- desc: "停止一个虚拟盒子",
54
- handler: async ({ boxNameOrBoxId }) => {
55
- const m = await new QemuVM().init();
56
- await m.stopVM(boxNameOrBoxId);
57
- },
58
- },
59
- {
60
- command: "delete <boxNameOrBoxId>",
61
- desc: "删除一个盒子,将会删除其所具有的数据",
62
- handler: async ({ boxNameOrBoxId }) => {
63
- const m = await new QemuVM().init();
64
- await m.deleteVM(boxNameOrBoxId);
65
- },
66
- },
67
- {
68
- command: "list [boxNameOrBoxId]",
69
- desc: "获取一个虚拟盒子的信息",
70
- handler: async ({ boxNameOrBoxId }) => {
71
- const m = await new QemuVM().init();
72
-
73
- let vmInfos = await m.listVM(boxNameOrBoxId);
74
- if (vmInfos.length == 0) {
75
- logger.info("没有找到任何盒子,创建一个吧!");
76
- return;
77
- }
78
- console.table(vmInfos);
79
- const hasNoRegistry = vmInfos.some((vm) => !vm.name);
80
- if (hasNoRegistry) {
81
- logger.info(
82
- `还没有注册的盒子,你可以复制 id 到 http://127.0.0.1:30554/add 进行注册`
83
- );
84
- }
85
- },
86
- },
87
3
  {
88
4
  command: "switch <boxName>",
89
5
  desc: "设置默认的盒子",
90
- handler: async ({ boxName }) => {
91
- await sdkEnv.setDefaultBoxName(boxName);
6
+ handler: async () => {
7
+ throw "暂时不支持设置默认盒子,请通过在客户端上点击默认的盒子进行设置。";
92
8
  },
93
9
  },
94
10
  ];
package/lib/env.js CHANGED
@@ -1,109 +1,21 @@
1
1
  import path from "path";
2
2
  import fs from "fs";
3
- import {
4
- ensureDir,
5
- APP_CONFIG_FILE,
6
- GLOBAL_CONFIG_DIR,
7
- APP_FOLDER,
8
- } from "./utils.js";
9
- import inquirer from "inquirer";
10
- import chalk from "chalk";
3
+ import { ensureDir } from "./utils.js";
11
4
  import logger from "loglevel";
12
- import fetch from "./fetch.js";
5
+ import os from "node:os";
13
6
 
14
- const GLOBAL_CONFIG_NAME = "box-config.json";
15
- const allPermitEnv = [
16
- {
17
- name: "DEFAULT_BOXNAME",
18
- type: "input",
19
- message: "默认的懒猫云盒子名称",
20
- validate: (input) => {
21
- return input != "";
22
- },
23
- },
24
- ];
7
+ export const GLOBAL_CONFIG_DIR = path.join(
8
+ os.homedir(),
9
+ "/.config/lazycat/box-config.json"
10
+ );
25
11
 
26
12
  class Env {
27
- constructor(cwd) {
28
- // 只加载
29
- this.existance = {
30
- global: false,
31
- local: false,
32
- };
13
+ constructor() {
33
14
  this.allEnv = {};
15
+ this.globalEnvPath = GLOBAL_CONFIG_DIR;
16
+ ensureDir(this.globalEnvPath);
34
17
 
35
18
  this._loadGlobalEnv();
36
- this._updateAllEnv();
37
- }
38
-
39
- /**
40
- * 加载应用环境变量.
41
- * @param {string} cwd 当前路径
42
- */
43
- load(cwd) {
44
- this.localEnvPath = path.join(cwd, APP_FOLDER, APP_CONFIG_FILE);
45
- try {
46
- this.localEnv = JSON.parse(fs.readFileSync(this.localEnvPath));
47
- this.existance.local = true;
48
- } catch (e) {
49
- logger.debug("local env fail to fetch ", e.message);
50
- this.localEnv = {};
51
- }
52
-
53
- this._updateAllEnv();
54
- }
55
-
56
- /**
57
- * 确保字段存在, 如果不存在会要求用户输入
58
- * @param pairs {array} 需要询问的环境变量, 同 inquirer.prompt 参数
59
- * @param global {boolean} 是否设置为全局变量, 默认为 false
60
- */
61
- async ensure(pairs, global = false) {
62
- let willAskPairs = [];
63
-
64
- pairs.forEach((pair) => {
65
- if (this.allEnv.hasOwnProperty(pair.name)) return;
66
- let found = allPermits.find((p) => p.name == pair.name);
67
- if (found) {
68
- willAskPairs.push(Object.assign({}, found, pair));
69
- }
70
- });
71
-
72
- const pair = await inquirer.prompt(willAskPairs);
73
- let pairNames = pairs.map((a) => a.name);
74
- this.set(pair, global);
75
- return Object.fromEntries(
76
- Object.entries(this.allEnv).filter(([name, _]) => {
77
- return pairNames.includes(name);
78
- })
79
- );
80
- }
81
-
82
- stringify() {
83
- let content = "";
84
- Object.keys(this.allEnv).forEach((key) => {
85
- content += `${key}="${this.allEnv[key]}"\n`;
86
- });
87
- return content.trimEnd();
88
- }
89
-
90
- get isEnvExist() {
91
- return fs.existsSync(this.envPath);
92
- }
93
-
94
- get all() {
95
- return Object.keys(this.allEnv).reduce((p, c) => {
96
- if (allPermitEnv.includes(c)) p[c] = this.allEnv[c];
97
- return p;
98
- }, {});
99
- }
100
-
101
- get localEnvExist() {
102
- return this.existance.local;
103
- }
104
-
105
- get globalEnvExist() {
106
- return this.existance.global;
107
19
  }
108
20
 
109
21
  has(key) {
@@ -114,94 +26,22 @@ class Env {
114
26
  return this.allEnv[key];
115
27
  }
116
28
 
117
- set(pairs, global = false) {
118
- const configPath = global ? this.globalEnvPath : this.localEnvPath;
119
- let envs = global ? this.globalEnv : this.localEnv;
120
- ensureDir(configPath);
121
-
122
- Object.assign(envs, pairs);
123
- fs.writeFileSync(configPath, JSON.stringify(envs, null, " "));
124
- return this._updateAllEnv();
125
- }
126
-
127
- _updateAllEnv() {
128
- return (this.allEnv = Object.assign({}, this.globalEnv, this.localEnv));
29
+ set(pairs) {
30
+ Object.assign(this.allEnv, pairs);
31
+ fs.writeFileSync(
32
+ this.globalEnvPath,
33
+ JSON.stringify(this.allEnv, null, " ")
34
+ );
129
35
  }
130
36
 
131
37
  _loadGlobalEnv() {
132
- this.globalEnvPath = path.join(GLOBAL_CONFIG_DIR, GLOBAL_CONFIG_NAME);
133
-
134
38
  try {
135
- this.globalEnv = JSON.parse(fs.readFileSync(this.globalEnvPath));
136
- this.existance.global = true;
39
+ this.allEnv = JSON.parse(fs.readFileSync(this.globalEnvPath));
137
40
  } catch (e) {
138
41
  logger.debug("global env fail to fetch ", e.message);
139
- this.globalEnv = {};
140
- }
141
- }
142
- }
143
-
144
- const env = new Env();
145
-
146
- // 用于处理 SDK_URL 这个特殊的环境变量
147
- // SDK_URL 根据默认的盒子名称生成,如果没有指定默认的盒子名称将会
148
- // 询问用户输入对应的盒子名称。
149
- class SDKEnv {
150
- get sdkUrl() {
151
- if (env.has("DEFAULT_BOXNAME")) {
152
- return this.buildSdkUrl(env.get("DEFAULT_BOXNAME"));
153
- }
154
- return "";
155
- }
156
-
157
- get sdkHostName() {
158
- if (env.has("DEFAULT_BOXNAME")) {
159
- return `sdk.${env.get("DEFAULT_BOXNAME")}.heiyu.space`;
42
+ this.allEnv = {};
160
43
  }
161
- return "";
162
- }
163
-
164
- async ensure() {
165
- try {
166
- const resp = await fetch(`${this.sdkUrl}/api/v1/ping`);
167
- if (resp.status != 200)
168
- throw new Error(chalk.red("debug.bridge服务未正常运行"));
169
- } catch (e) {
170
- logger.error(e);
171
- console.log(
172
- chalk.yellow(
173
- `检测到SDK尚未安装,请将盒子"${env.get(
174
- "DEFAULT_BOXNAME"
175
- )}"开启开发者模式`
176
- )
177
- );
178
- process.exit(-1);
179
- }
180
- }
181
-
182
- async setDefaultBoxName(boxname = "") {
183
- if (!boxname) {
184
- boxname = await this._promptAnswers();
185
- }
186
- let url = this.buildSdkUrl(boxname);
187
- env.set({ DEFAULT_BOXNAME: boxname }, true);
188
- }
189
-
190
- buildSdkUrl(boxname) {
191
- return `https://sdk.${boxname}.heiyu.space`;
192
- }
193
-
194
- /**
195
- * 提示用户回答问题
196
- *
197
- */
198
- async _promptAnswers() {
199
- const answers = await inquirer.prompt(
200
- allPermitEnv.filter((a) => a.name == "DEFAULT_BOXNAME")
201
- );
202
- return answers["DEFAULT_BOXNAME"];
203
44
  }
204
45
  }
205
46
 
206
- export const sdkEnv = new SDKEnv();
207
- export default env;
47
+ export default new Env();
package/lib/lzc_sdk.js CHANGED
@@ -7,9 +7,22 @@ import logger from "loglevel";
7
7
  import os from "node:os";
8
8
  import fs from "node:fs";
9
9
 
10
- const SHELLAPI_CONFIG_DIR = path.join(os.homedir(), "/.config/hportal-client");
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
+
11
24
  const coreDefinition = protoLoader.loadSync(
12
- path.join(contextDirname(), "./core.proto"),
25
+ path.join(contextDirname(), "./shellapi.proto"),
13
26
  {
14
27
  keepCase: true,
15
28
  longs: String,
@@ -24,6 +37,7 @@ const core =
24
37
  let client;
25
38
 
26
39
  function readShellApiInfo() {
40
+ const SHELLAPI_CONFIG_DIR = getShellAPIConfigDir();
27
41
  const addr = fs.readFileSync(
28
42
  path.resolve(SHELLAPI_CONFIG_DIR, "shellapi_addr"),
29
43
  { encoding: "utf-8" }
@@ -0,0 +1,95 @@
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 fs from "node:fs";
6
+ import os from "node:os";
7
+
8
+ /**
9
+ * @link {https://www.npmjs.com/package/appdirs}
10
+ */
11
+ function getShellAPIConfigDir() {
12
+ const home = os.homedir();
13
+ const isMacos = process.platform == "darwin";
14
+ let sufix = "/.config/hportal-client";
15
+ if (isMacos) {
16
+ sufix = "/Library/Application Support/hportal-client";
17
+ } // TODO: need reimpl fetch windows
18
+ const SHELLAPI_CONFIG_DIR = path.join(home, sufix);
19
+ return SHELLAPI_CONFIG_DIR;
20
+ }
21
+
22
+ class ShellApi {
23
+ constructor() {
24
+ const pbShell = this.initShell();
25
+ const { addr, cred } = this.readShellApiInfo();
26
+ this.client = new pbShell.ShellCore(
27
+ addr,
28
+ grpc.credentials.createInsecure()
29
+ );
30
+
31
+ const md = new grpc.Metadata();
32
+ md.add("lzc-shellapi-cred", cred);
33
+ this.metadata = md;
34
+ }
35
+
36
+ async init() {
37
+ this.info = await this.initBoxInfo();
38
+ }
39
+
40
+ get boxname() {
41
+ return this.info.boxname;
42
+ }
43
+ get uid() {
44
+ return this.info.uid;
45
+ }
46
+
47
+ readShellApiInfo() {
48
+ const SHELLAPI_CONFIG_DIR = getShellAPIConfigDir();
49
+ const addr = fs.readFileSync(
50
+ path.resolve(SHELLAPI_CONFIG_DIR, "shellapi_addr"),
51
+ { encoding: "utf-8" }
52
+ );
53
+ const cred = fs.readFileSync(
54
+ path.resolve(SHELLAPI_CONFIG_DIR, "shellapi_cred"),
55
+ { encoding: "utf-8" }
56
+ );
57
+ return { addr, cred };
58
+ }
59
+
60
+ initShell() {
61
+ const coreDefinition = protoLoader.loadSync(
62
+ path.join(contextDirname(), "./shellapi.proto"),
63
+ {
64
+ keepCase: true,
65
+ longs: String,
66
+ enums: String,
67
+ defaults: true,
68
+ oneofs: true,
69
+ }
70
+ );
71
+ return grpc.loadPackageDefinition(coreDefinition).space.heiyu.hportal.shell;
72
+ }
73
+
74
+ async initBoxInfo() {
75
+ return new Promise((resolve, reject) => {
76
+ this.client.queryBoxList({}, this.metadata, function (err, response) {
77
+ if (err) {
78
+ reject(err);
79
+ return;
80
+ }
81
+ for (let box of response.boxes) {
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
+ );
90
+ });
91
+ });
92
+ }
93
+ }
94
+
95
+ export default new ShellApi();