@lazycatcloud/lzc-cli 2.0.0-pre.0 → 2.0.0-pre.2

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 (92) hide show
  1. package/README.md +46 -7
  2. package/changelog.md +56 -19
  3. package/lib/app/apkshell.js +7 -44
  4. package/lib/app/index.js +5 -1
  5. package/lib/app/lpk_build.js +266 -56
  6. package/lib/app/lpk_build_images.js +424 -229
  7. package/lib/app/lpk_build_images_local.js +425 -0
  8. package/lib/app/lpk_build_images_pack_local.js +409 -0
  9. package/lib/app/lpk_create.js +158 -83
  10. package/lib/app/lpk_create_generator.js +35 -42
  11. package/lib/app/lpk_devshell.js +6 -2
  12. package/lib/app/lpk_installer.js +4 -3
  13. package/lib/app/manifest_build.js +259 -0
  14. package/lib/app/project_cp.js +5 -10
  15. package/lib/app/project_deploy.js +80 -11
  16. package/lib/app/project_exec.js +48 -11
  17. package/lib/app/project_info.js +59 -59
  18. package/lib/app/project_log.js +5 -10
  19. package/lib/app/project_runtime.js +113 -18
  20. package/lib/app/project_start.js +6 -11
  21. package/lib/app/project_sync.js +499 -0
  22. package/lib/appstore/apkshell.js +50 -0
  23. package/lib/appstore/publish.js +54 -15
  24. package/lib/build_remote.js +0 -1
  25. package/lib/config/index.js +1 -1
  26. package/lib/debug_bridge.js +217 -47
  27. package/lib/i18n/locales/en/translation.json +262 -262
  28. package/lib/i18n/locales/zh/translation.json +262 -262
  29. package/lib/lpk/core.js +2 -1
  30. package/lib/migrate/index.js +52 -0
  31. package/lib/package_info.js +135 -0
  32. package/lib/shellapi.js +35 -1
  33. package/lib/sig/core.js +2 -2
  34. package/lib/utils.js +92 -15
  35. package/package.json +89 -89
  36. package/scripts/cli.js +2 -0
  37. package/scripts/smoke/frontend-dev-entry.mjs +104 -0
  38. package/scripts/smoke/template-project.mjs +311 -0
  39. package/template/_lpk/README.md +6 -3
  40. package/template/_lpk/gui-vnc.manifest.yml.in +0 -9
  41. package/template/_lpk/hello-vue.manifest.yml.in +38 -0
  42. package/template/_lpk/manifest.yml.in +0 -9
  43. package/template/_lpk/package.yml.in +7 -0
  44. package/template/_lpk/todolist-golang.manifest.yml.in +23 -9
  45. package/template/_lpk/todolist-java.manifest.yml.in +23 -9
  46. package/template/_lpk/todolist-python.manifest.yml.in +31 -9
  47. package/template/_lpk/todolist-serverless.manifest.yml.in +38 -0
  48. package/template/blank/lzc-build.dev.yml +4 -0
  49. package/template/blank/lzc-build.yml +0 -2
  50. package/template/blank/lzc-manifest.yml +3 -12
  51. package/template/blank/package.yml +7 -0
  52. package/template/golang/Dockerfile +1 -1
  53. package/template/golang/Dockerfile.dev +20 -0
  54. package/template/golang/README.md +22 -11
  55. package/template/golang/_lzcdevignore +21 -0
  56. package/template/golang/lzc-build.dev.yml +12 -0
  57. package/template/golang/lzc-build.yml +0 -5
  58. package/template/golang/main.go +1 -1
  59. package/template/golang/manifest.dev.page.js +24 -0
  60. package/template/golang/run.sh +7 -0
  61. package/template/gui-vnc/README.md +5 -1
  62. package/template/gui-vnc/lzc-build.dev.yml +4 -0
  63. package/template/gui-vnc/lzc-build.yml +0 -5
  64. package/template/python/Dockerfile +2 -2
  65. package/template/python/Dockerfile.dev +18 -0
  66. package/template/python/README.md +28 -11
  67. package/template/python/_lzcdevignore +21 -0
  68. package/template/python/app.py +1 -1
  69. package/template/python/lzc-build.dev.yml +12 -0
  70. package/template/python/lzc-build.yml +0 -5
  71. package/template/python/manifest.dev.page.js +25 -0
  72. package/template/python/run.sh +12 -1
  73. package/template/springboot/Dockerfile +1 -1
  74. package/template/springboot/Dockerfile.dev +20 -0
  75. package/template/springboot/README.md +22 -11
  76. package/template/springboot/_lzcdevignore +21 -0
  77. package/template/springboot/lzc-build.dev.yml +12 -0
  78. package/template/springboot/lzc-build.yml +0 -5
  79. package/template/springboot/manifest.dev.page.js +24 -0
  80. package/template/springboot/run.sh +7 -0
  81. package/template/vue/README.md +14 -27
  82. package/template/vue/_gitignore +0 -1
  83. package/template/vue/lzc-build.dev.yml +7 -0
  84. package/template/vue/lzc-build.yml +0 -2
  85. package/template/vue/manifest.dev.page.js +50 -0
  86. package/template/vue/src/App.vue +1 -1
  87. package/template/vue-minidb/README.md +11 -19
  88. package/template/vue-minidb/_gitignore +0 -1
  89. package/template/vue-minidb/lzc-build.dev.yml +7 -0
  90. package/template/vue-minidb/lzc-build.yml +0 -2
  91. package/template/vue-minidb/manifest.dev.page.js +50 -0
  92. package/template/blank/_gitignore +0 -1
@@ -1,6 +1,6 @@
1
1
  import logger from 'loglevel';
2
2
  import inquirer from 'inquirer';
3
- import { contextDirname, loadFromYaml, ensureDir, ensureDirectoryExists, dumpToYaml, isFileExist, isValidAppId, fakeLoadManifestYml, envsubstr } from '../utils.js';
3
+ import { contextDirname, ensureDir, ensureDirectoryExists, dumpToYaml, isFileExist, isValidAppId, envsubstr } from '../utils.js';
4
4
  import path from 'node:path';
5
5
  import { TemplateConfig } from './lpk_create_generator.js';
6
6
  import fs from 'node:fs';
@@ -8,103 +8,113 @@ import yaml from 'js-yaml';
8
8
  import chalk from 'chalk';
9
9
  import { t } from '../i18n/index.js';
10
10
 
11
- let fsPromises = fs.promises;
12
11
 
13
- const PROJECT_TEMPLATE_OPTIONS = [
14
- {
15
- i18nKey: 'lzc_cli.lib.app.lpk_create.template_option_hello_vue',
16
- defaultText: 'hello-vue (Vue基础模板)',
17
- value: 'hello-vue',
18
- },
19
- {
20
- i18nKey: 'lzc_cli.lib.app.lpk_create.template_option_todolist_java',
21
- defaultText: 'todolist-java (Java Todo示例)',
22
- value: 'todolist-java',
23
- },
24
- {
25
- i18nKey: 'lzc_cli.lib.app.lpk_create.template_option_todolist_python',
26
- defaultText: 'todolist-python (Python Todo示例)',
27
- value: 'todolist-python',
28
- },
29
- {
30
- i18nKey: 'lzc_cli.lib.app.lpk_create.template_option_todolist_golang',
31
- defaultText: 'todolist-golang (Golang Todo示例)',
32
- value: 'todolist-golang',
33
- },
34
- {
35
- i18nKey: 'lzc_cli.lib.app.lpk_create.template_option_gui_vnc',
36
- defaultText: 'gui-vnc (GUI VNC Embed示例)',
37
- value: 'gui-vnc',
38
- },
39
- {
40
- i18nKey: 'lzc_cli.lib.app.lpk_create.template_option_todolist_serverless',
41
- defaultText: 'todolist-serverless (Serverless Todo示例)',
42
- value: 'todolist-serverless',
43
- },
44
- ];
12
+ function fakeLoadManifestText(text) {
13
+ let obj = {
14
+ application: {
15
+ subdomain: undefined,
16
+ },
17
+ };
45
18
 
46
- export function listProjectTemplateValues() {
47
- return PROJECT_TEMPLATE_OPTIONS.map((item) => item.value);
19
+ const normalizeValue = (value) => {
20
+ const commentIndex = value.indexOf(' #');
21
+ if (commentIndex >= 0) {
22
+ value = value.slice(0, commentIndex);
23
+ }
24
+ return value.trim();
25
+ };
26
+
27
+ String(text ?? '').split('\n').forEach((rawLine) => {
28
+ const line = rawLine.trim();
29
+ const arr = line.split(':');
30
+ if (arr.length !== 2) {
31
+ return;
32
+ }
33
+ let [key, value] = arr;
34
+ value = normalizeValue(value);
35
+ if (!obj.package && key === 'package') {
36
+ obj.package = value;
37
+ }
38
+ if (!obj.application.subdomain && key === 'subdomain') {
39
+ obj.application.subdomain = value;
40
+ }
41
+ if (!obj.version && key === 'version') {
42
+ obj.version = value;
43
+ }
44
+ });
45
+ return obj;
48
46
  }
49
47
 
50
- export function normalizeTemplateType(rawType) {
51
- const type = String(rawType ?? '').trim();
52
- if (!type) {
53
- return '';
54
- }
55
- const allowedTypes = listProjectTemplateValues();
56
- if (!allowedTypes.includes(type)) {
57
- throw new Error(`Unsupported template "${type}". Available templates: ${allowedTypes.join(', ')}`);
48
+ async function renderTemplateText(templateFilePath, templateVars) {
49
+ const templateText = fs.readFileSync(templateFilePath, 'utf-8');
50
+ return await envsubstr(templateText, {
51
+ options: {
52
+ envs: Object.entries(templateVars).map(([key, value]) => ({
53
+ name: key.replace(/-/g, '_'),
54
+ value,
55
+ })),
56
+ syntax: 'default',
57
+ protect: false,
58
+ },
59
+ });
60
+ }
61
+
62
+ function loadTemplateYaml(text, fallbackLoader) {
63
+ try {
64
+ return {
65
+ data: yaml.load(text),
66
+ excp: false,
67
+ };
68
+ } catch {
69
+ return {
70
+ data: fallbackLoader(text),
71
+ excp: true,
72
+ };
58
73
  }
59
- return type;
60
74
  }
61
75
 
62
- export class LpkManifest {
63
- constructor(defaultAppID) {
76
+ export class LpkTemplateFile {
77
+ constructor(defaultAppID, outputFileName, fallbackLoader = (text) => yaml.load(text)) {
64
78
  this.pwd = contextDirname(import.meta.url);
65
- this.manifest = {};
66
- this.manifestText = '';
67
79
  this.defaultAppID = defaultAppID;
80
+ this.outputFileName = outputFileName;
81
+ this.fallbackLoader = fallbackLoader;
82
+ this.data = {};
83
+ this.text = '';
84
+ this.excpTemplate = false;
68
85
  }
69
86
 
70
- async init(manifestFilePath, askInfo = false) {
71
- if (!manifestFilePath) {
72
- manifestFilePath = path.join(this.pwd, '..', '..', 'template', '_lpk', 'manifest.yml.in');
87
+ async init(templateFilePath, askInfo = false, templateVars = null) {
88
+ if (!templateFilePath) {
89
+ throw new Error(`template file is required for ${this.outputFileName}`);
73
90
  }
74
91
 
92
+ let vars = templateVars;
75
93
  if (askInfo) {
76
- let answer = await this.askLpkInfo();
77
- const manifestTemplate = fs.readFileSync(manifestFilePath, 'utf-8');
78
- this.manifestText = await envsubstr(manifestTemplate, {
79
- options: {
80
- envs: Object.entries(answer).map(([key, value]) => ({
81
- name: key.replace(/-/g, '_'),
82
- value,
83
- })),
84
- syntax: 'default',
85
- protect: false,
86
- },
87
- });
88
- this.manifest = yaml.load(this.manifestText);
89
- } else {
90
- try {
91
- this.manifest = loadFromYaml(manifestFilePath);
92
- } catch (err) {
93
- this.manifest = fakeLoadManifestYml(manifestFilePath);
94
- this.excpManifest = true;
95
- }
94
+ vars = vars || (await this.askLpkInfo());
95
+ this.text = await renderTemplateText(templateFilePath, vars);
96
+ const loaded = loadTemplateYaml(this.text, this.fallbackLoader);
97
+ this.data = loaded.data;
98
+ this.excpTemplate = loaded.excp;
99
+ return vars;
96
100
  }
101
+
102
+ const templateText = fs.readFileSync(templateFilePath, 'utf-8');
103
+ const loaded = loadTemplateYaml(templateText, this.fallbackLoader);
104
+ this.data = loaded.data;
105
+ this.excpTemplate = loaded.excp;
106
+ return vars;
97
107
  }
98
108
 
99
109
  async create(outputDir) {
100
- let outputFilePath = path.join(outputDir, 'lzc-manifest.yml');
110
+ let outputFilePath = path.join(outputDir, this.outputFileName);
101
111
  logger.debug(`create file ${outputFilePath}`);
102
112
  ensureDir(outputFilePath);
103
- if (this.manifestText) {
104
- fs.writeFileSync(outputFilePath, this.manifestText);
113
+ if (this.text) {
114
+ fs.writeFileSync(outputFilePath, this.text);
105
115
  return;
106
116
  }
107
- return dumpToYaml(this.manifest, outputFilePath);
117
+ return dumpToYaml(this.data, outputFilePath);
108
118
  }
109
119
 
110
120
  async askLpkInfo() {
@@ -126,34 +136,99 @@ export class LpkManifest {
126
136
  },
127
137
  },
128
138
  ]);
129
- const answer = {
139
+ return {
130
140
  name: appId,
131
141
  package: `cloud.lazycat.app.${appId}`,
132
142
  subdomain: appId,
133
143
  };
134
- return answer;
135
144
  }
136
145
  }
137
146
 
147
+ export class LpkManifest extends LpkTemplateFile {
148
+ constructor(defaultAppID) {
149
+ super(defaultAppID, 'lzc-manifest.yml', fakeLoadManifestText);
150
+ }
151
+ }
152
+
153
+ export class LpkPackageFile extends LpkTemplateFile {
154
+ constructor(defaultAppID) {
155
+ super(defaultAppID, 'package.yml');
156
+ }
157
+ }
158
+
159
+ const PROJECT_TEMPLATE_OPTIONS = [
160
+ {
161
+ i18nKey: 'lzc_cli.lib.app.lpk_create.template_option_hello_vue',
162
+ defaultText: 'hello-vue (Vue基础模板)',
163
+ value: 'hello-vue',
164
+ },
165
+ {
166
+ i18nKey: 'lzc_cli.lib.app.lpk_create.template_option_todolist_java',
167
+ defaultText: 'todolist-java (Java Todo示例)',
168
+ value: 'todolist-java',
169
+ },
170
+ {
171
+ i18nKey: 'lzc_cli.lib.app.lpk_create.template_option_todolist_python',
172
+ defaultText: 'todolist-python (Python Todo示例)',
173
+ value: 'todolist-python',
174
+ },
175
+ {
176
+ i18nKey: 'lzc_cli.lib.app.lpk_create.template_option_todolist_golang',
177
+ defaultText: 'todolist-golang (Golang Todo示例)',
178
+ value: 'todolist-golang',
179
+ },
180
+ {
181
+ i18nKey: 'lzc_cli.lib.app.lpk_create.template_option_gui_vnc',
182
+ defaultText: 'gui-vnc (GUI VNC Embed示例)',
183
+ value: 'gui-vnc',
184
+ },
185
+ {
186
+ i18nKey: 'lzc_cli.lib.app.lpk_create.template_option_todolist_serverless',
187
+ defaultText: 'todolist-serverless (Serverless Todo示例)',
188
+ value: 'todolist-serverless',
189
+ },
190
+ ];
191
+
192
+ export function listProjectTemplateValues() {
193
+ return PROJECT_TEMPLATE_OPTIONS.map((item) => item.value);
194
+ }
195
+
196
+ export function normalizeTemplateType(rawType) {
197
+ const type = String(rawType ?? '').trim();
198
+ if (!type) {
199
+ return '';
200
+ }
201
+ const allowedTypes = listProjectTemplateValues();
202
+ if (!allowedTypes.includes(type)) {
203
+ throw new Error(`Unsupported template "${type}". Available templates: ${allowedTypes.join(', ')}`);
204
+ }
205
+ return type;
206
+ }
207
+
138
208
  export class TemplateInit {
139
209
  constructor(cwd, name, type) {
140
210
  this.cwd = cwd;
141
211
  this.name = name;
142
212
  this.type = normalizeTemplateType(type);
143
213
  this.lpkManifest = new LpkManifest(name);
214
+ this.lpkPackageFile = new LpkPackageFile(name);
144
215
  }
145
216
 
146
217
  async init() {
147
218
  this.type = await chooseTemplate(this.type);
148
219
 
149
- // 优先使用模板项目自定义的模板文件
150
- const typeTemplatePath = path.join(contextDirname(import.meta.url), '..', '..', 'template', '_lpk', `${this.type}.manifest.yml.in`);
151
- const templatePath = isFileExist(typeTemplatePath) ? typeTemplatePath : '';
152
- await this.lpkManifest.init(templatePath, true);
220
+ const templateRoot = path.join(contextDirname(import.meta.url), '..', '..', 'template', '_lpk');
221
+ const typeTemplatePath = path.join(templateRoot, `${this.type}.manifest.yml.in`);
222
+ const manifestTemplatePath = isFileExist(typeTemplatePath) ? typeTemplatePath : path.join(templateRoot, 'manifest.yml.in');
223
+ const packageTemplatePath = path.join(templateRoot, 'package.yml.in');
224
+ const templateVars = await this.lpkManifest.init(manifestTemplatePath, true);
225
+ await this.lpkPackageFile.init(packageTemplatePath, true, templateVars);
153
226
  }
154
227
 
155
228
  async create() {
156
- await this.lpkManifest.create(path.resolve(this.cwd, this.name));
229
+ const outputDir = path.resolve(this.cwd, this.name);
230
+ await this.lpkManifest.create(outputDir);
231
+ await this.lpkPackageFile.create(outputDir);
157
232
  }
158
233
  }
159
234
 
@@ -4,7 +4,6 @@ import fs from 'fs';
4
4
  import { isBinaryFileSync } from 'isbinaryfile';
5
5
  import { contextDirname } from '../utils.js';
6
6
  import chalk from 'chalk';
7
- // import spawn from "cross-spawn"
8
7
  import logger from 'loglevel';
9
8
  import { t } from '../i18n/index.js';
10
9
 
@@ -27,7 +26,6 @@ async function loadFiles(dirPath, prefix = '') {
27
26
  } else {
28
27
  content = fs.readFileSync(sourcePath, 'utf8');
29
28
  }
30
- // change _ prefix file into dot file
31
29
  if (p.startsWith('_')) {
32
30
  p = p.replace('_', '.');
33
31
  }
@@ -56,28 +54,6 @@ export async function generate(type, to, opts = {}) {
56
54
  writeFileTree(to, files);
57
55
  }
58
56
 
59
- function appCreateSuccessTip(name) {
60
- console.log(
61
- chalk.green(
62
- t(
63
- 'lzc_cli.lib.app.lpk_create_generator.app_create_success_tip',
64
- `
65
- ✨ 懒猫微服应用已创建成功 !
66
- ✨ 先完成第一次部署,确认手机/PC都可以访问
67
- cd {{ name }}
68
- npm install
69
- lzc-cli project deploy
70
- lzc-cli project info
71
- `,
72
- {
73
- name,
74
- interpolation: { escapeValue: false }
75
- },
76
- ).trim(),
77
- ),
78
- );
79
- }
80
-
81
57
  function appCreateSuccessTipByCommands(name, installCommand = '') {
82
58
  const installBlock = installCommand ? `\n ${installCommand}` : '';
83
59
  console.log(
@@ -86,10 +62,13 @@ function appCreateSuccessTipByCommands(name, installCommand = '') {
86
62
  'lzc_cli.lib.app.lpk_create_generator.app_create_success_tip_common',
87
63
  `
88
64
  ✨ 懒猫微服应用已创建成功 !
89
- 先完成第一次部署,确认手机/PC都可以访问
65
+ First deploy and open the app once
90
66
  cd {{ name }}{{ installBlock }}
91
67
  lzc-cli project deploy
92
68
  lzc-cli project info
69
+ ℹ By default, project commands use lzc-build.dev.yml when it exists.
70
+ Each command prints the active Build config.
71
+ Use --release if you want to operate on lzc-build.yml.
93
72
  `,
94
73
  {
95
74
  name,
@@ -113,11 +92,13 @@ export const TemplateConfig = {
113
92
  t(
114
93
  'lzc_cli.lib.app.lpk_create_generator.template_config_vue3_green',
115
94
  `
116
- 🛠 修改源码后,重新执行部署即可查看效果:
117
- lzc-cli project deploy
118
- 🔎 如果要排查问题:
95
+ 🛠 Open the app first.
96
+ If the frontend dev server is not running yet, the app page will show the expected port and next step.
97
+ Then start local frontend dev:
98
+ npm run dev
99
+ The command output prints the active Build config.
100
+ 🔎 For troubleshooting:
119
101
  lzc-cli project log -f
120
- lzc-cli project exec /bin/sh
121
102
  `,
122
103
  ).trim(),
123
104
  ),
@@ -135,11 +116,14 @@ export const TemplateConfig = {
135
116
  t(
136
117
  'lzc_cli.lib.app.lpk_create_generator.template_config_springboot_green',
137
118
  `
138
- 🛠 Spring Boot app starts inside image build/runtime:
139
- lzc-cli project deploy
119
+ 🛠 Open the app first.
120
+ If the backend dev service is not ready yet, the app page will tell you how to sync or copy code and how to start the service.
121
+ Recommended backend dev loop:
122
+ lzc-cli project sync --watch
123
+ lzc-cli project exec /bin/sh
124
+ The command output prints the active Build config.
140
125
  🔎 For troubleshooting:
141
126
  lzc-cli project log -f
142
- lzc-cli project exec /bin/sh
143
127
  `,
144
128
  ).trim(),
145
129
  ),
@@ -157,11 +141,14 @@ export const TemplateConfig = {
157
141
  t(
158
142
  'lzc_cli.lib.app.lpk_create_generator.template_config_python_green',
159
143
  `
160
- 🛠 Flask app starts in container runtime:
161
- lzc-cli project deploy
144
+ 🛠 Open the app first.
145
+ If the backend dev service is not ready yet, the app page will tell you how to sync or copy code and how to start the service.
146
+ Recommended backend dev loop:
147
+ lzc-cli project sync --watch
148
+ lzc-cli project exec /bin/sh
149
+ The command output prints the active Build config.
162
150
  🔎 For troubleshooting:
163
151
  lzc-cli project log -f
164
- lzc-cli project exec /bin/sh
165
152
  `,
166
153
  ).trim(),
167
154
  ),
@@ -179,12 +166,15 @@ export const TemplateConfig = {
179
166
  t(
180
167
  'lzc_cli.lib.app.lpk_create_generator.template_config_golang_green',
181
168
  `
182
- 🛠 Golang app starts in container runtime:
183
- lzc-cli project deploy
169
+ 🛠 Open the app first.
170
+ If the backend dev service is not ready yet, the app page will tell you how to sync or copy code and how to start the service.
171
+ Recommended backend dev loop:
172
+ lzc-cli project sync --watch
173
+ lzc-cli project exec /bin/sh
174
+ The command output prints the active Build config.
184
175
  🔎 For troubleshooting:
185
176
  lzc-cli project log -f
186
- lzc-cli project exec /bin/sh
187
- `,
177
+ `,
188
178
  ).trim(),
189
179
  ),
190
180
  );
@@ -203,6 +193,7 @@ export const TemplateConfig = {
203
193
  `
204
194
  🛠 GUI VNC app (embedded image template):
205
195
  lzc-cli project deploy
196
+ The command output prints the active Build config.
206
197
  🔎 For troubleshooting:
207
198
  lzc-cli project log -f
208
199
  lzc-cli project exec -s desktop /bin/sh
@@ -223,11 +214,13 @@ export const TemplateConfig = {
223
214
  t(
224
215
  'lzc_cli.lib.app.lpk_create_generator.template_config_vue_minidb_green',
225
216
  `
226
- 🛠 This template includes @lazycatcloud/minidb:
227
- lzc-cli project deploy
217
+ 🛠 Open the app first.
218
+ If the frontend dev server is not running yet, the app page will show the expected port and next step.
219
+ Then start local frontend dev:
220
+ npm run dev
221
+ The command output prints the active Build config.
228
222
  🔎 For troubleshooting:
229
223
  lzc-cli project log -f
230
- lzc-cli project exec /bin/sh
231
224
  `,
232
225
  ).trim(),
233
226
  ),
@@ -384,11 +384,15 @@ class DevShell {
384
384
  const rsyncCmd = isWindows ? path.join(contextDirname(import.meta.url), '..', '..', 'template', '_lpk', 'win-rsync', 'rsync.exe') : `rsync`;
385
385
 
386
386
  try {
387
- const rsyncArgs = [`${rsyncDebug}`, `--recursive`, `--relative`, `--perms`, `--update`, `-F --filter=':- .gitignore'`, `--ignore-errors`, `. ${dest}`];
387
+ const rsyncArgs = [];
388
+ if (rsyncDebug) {
389
+ rsyncArgs.push(rsyncDebug);
390
+ }
391
+ rsyncArgs.push('--recursive', '--relative', '--perms', '--update', '-F', '--filter=:- .gitignore', '--ignore-errors', '.', dest);
388
392
  logger.debug(t('lzc_cli.lib.app.lpk_devshell.sync_project_code_tips', '同步代码: '), rsyncCmd, rsyncArgs.join(' '));
389
393
  const rsyncStream = spawn.sync(rsyncCmd, rsyncArgs, {
390
394
  env: { ...process.env, RSYNC_PASSWORD: 'fakefakefake' },
391
- shell: true,
395
+ shell: false,
392
396
  stdio: ['ignore', isDebugMode() ? 'inherit' : 'ignore', 'inherit'],
393
397
  });
394
398
  } catch (err) {
@@ -1,7 +1,8 @@
1
1
  import logger from 'loglevel';
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
- import { loadFromYaml, Downloader, unzipSync } from '../utils.js';
4
+ import { Downloader, extractLpkSync } from '../utils.js';
5
+ import { loadEffectiveManifestFromFiles } from '../package_info.js';
5
6
  import { DebugBridge } from '../debug_bridge.js';
6
7
  import shellapi from '../shellapi.js';
7
8
  import { t } from '../i18n/index.js';
@@ -95,8 +96,8 @@ export class LpkInstaller {
95
96
  const tempDir = fs.mkdtempSync('.lzc-cli-install');
96
97
  let manifest;
97
98
  try {
98
- unzipSync(pkgPath, tempDir, ['manifest.yml', 'icon.png']);
99
- manifest = loadFromYaml(path.join(tempDir, 'manifest.yml'));
99
+ extractLpkSync(pkgPath, tempDir, ['manifest.yml', 'package.yml', 'icon.png']);
100
+ manifest = loadEffectiveManifestFromFiles(path.join(tempDir, 'manifest.yml')).manifest;
100
101
  if (!manifest['application']['subdomain']) {
101
102
  throw t('lzc_cli.lib.app.lpk_installer.install_from_file_manifest_not_exists_app_subdomain_fail', 'manifest.yml 中的 `application.subdomain` 字段不能为空');
102
103
  }