esa-cli 1.0.3-beta.4 → 1.0.4-beta.0

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.
@@ -13,6 +13,7 @@ import chalk from 'chalk';
13
13
  import t from '../../i18n/index.js';
14
14
  import logger from '../../libs/logger.js';
15
15
  import promptParameter from '../../utils/prompt.js';
16
+ import { checkAndCleanupVersions } from '../utils.js';
16
17
  import { validateAndInitializeProject, generateCodeVersion } from '../common/utils.js';
17
18
  const commit = {
18
19
  command: 'commit [entry]',
@@ -44,6 +45,10 @@ const commit = {
44
45
  describe: 'Bundle with esbuild (use --no-bundle to skip)',
45
46
  type: 'boolean',
46
47
  default: true
48
+ })
49
+ .option('version-limit', {
50
+ describe: t('commit_option_version_limit').d('Maximum number of versions to keep. Oldest unreleased versions will be deleted if exceeded.'),
51
+ type: 'number'
47
52
  });
48
53
  },
49
54
  handler: (argv) => __awaiter(void 0, void 0, void 0, function* () {
@@ -59,7 +64,12 @@ export function handleCommit(argv) {
59
64
  const projectInfo = yield validateAndInitializeProject(argv === null || argv === void 0 ? void 0 : argv.name);
60
65
  if (!projectInfo)
61
66
  return;
62
- const { projectName } = projectInfo;
67
+ const { projectName, projectConfig } = projectInfo;
68
+ // Cleanup old versions if limit is specified
69
+ const limit = argv['version-limit'] || (projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.versionLimit);
70
+ if (limit) {
71
+ yield checkAndCleanupVersions(projectName, limit, true);
72
+ }
63
73
  let description;
64
74
  if (argv.description) {
65
75
  description = argv.description;
@@ -15,7 +15,7 @@ import { ensureRoutineExists } from '../../utils/checkIsRoutineCreated.js';
15
15
  import compress from '../../utils/compress.js';
16
16
  import { getProjectConfig } from '../../utils/fileUtils/index.js';
17
17
  import sleep from '../../utils/sleep.js';
18
- import { checkIsLoginSuccess } from '../utils.js';
18
+ import { checkAndCleanupVersions, checkIsLoginSuccess } from '../utils.js';
19
19
  function normalizeNotFoundStrategy(value) {
20
20
  if (!value)
21
21
  return undefined;
@@ -260,21 +260,26 @@ export function deployToEnvironments(name, codeVersion, env) {
260
260
  * 结合了压缩、提交和部署的完整流程
261
261
  */
262
262
  export function commitAndDeployVersion(projectName_1, scriptEntry_1, assets_1) {
263
- return __awaiter(this, arguments, void 0, function* (projectName, scriptEntry, assets, description = '', projectPath, env = 'production', minify = false, version, noBundle = false) {
263
+ return __awaiter(this, arguments, void 0, function* (projectName, scriptEntry, assets, description = '', projectPath, env = 'production', minify = false, version, noBundle = false, versionLimit) {
264
264
  var _a, _b, _c;
265
265
  const projectInfo = yield validateAndInitializeProject(projectName, projectPath);
266
266
  if (!projectInfo) {
267
267
  return false;
268
268
  }
269
- const { projectConfig } = projectInfo;
269
+ const { projectConfig, projectName: resolvedProjectName } = projectInfo;
270
+ // Cleanup old versions if limit is specified
271
+ const limit = versionLimit || (projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.versionLimit);
272
+ if (limit) {
273
+ yield checkAndCleanupVersions(resolvedProjectName, limit, !version);
274
+ }
270
275
  // 2) Use existing version or generate a new one
271
276
  if (version) {
272
277
  logger.startSubStep(`Using existing version ${version}`);
273
- const deployed = yield deployToEnvironments(projectInfo.projectName, version, env);
278
+ const deployed = yield deployToEnvironments(resolvedProjectName, version, env);
274
279
  logger.endSubStep(deployed ? 'Deploy finished' : 'Deploy failed');
275
280
  return deployed;
276
281
  }
277
- const res = yield generateCodeVersion(projectInfo.projectName, description, scriptEntry || (projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.entry), assets || ((_a = projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.assets) === null || _a === void 0 ? void 0 : _a.directory), minify || (projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.minify), projectPath, noBundle);
282
+ const res = yield generateCodeVersion(resolvedProjectName, description, scriptEntry || (projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.entry), assets || ((_a = projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.assets) === null || _a === void 0 ? void 0 : _a.directory), minify || (projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.minify), projectPath, noBundle);
278
283
  const isCommitSuccess = res === null || res === void 0 ? void 0 : res.isSuccess;
279
284
  if (!isCommitSuccess) {
280
285
  logger.endSubStep('Generate version failed');
@@ -61,6 +61,10 @@ const deploy = {
61
61
  .option('versions', {
62
62
  describe: 'Deploy two versions with percentages, format: v1:80,v2:20 or repeat --versions v1:80 --versions v2:20',
63
63
  type: 'array'
64
+ })
65
+ .option('version-limit', {
66
+ describe: t('deploy_option_version_limit').d('Maximum number of versions to keep. Oldest unreleased versions will be deleted if exceeded.'),
67
+ type: 'number'
64
68
  });
65
69
  },
66
70
  describe: `🚀 ${t('deploy_describe').d('Deploy your project')}`,
@@ -82,7 +86,7 @@ export function handleDeploy(argv) {
82
86
  outro(ok ? 'Deploy finished' : 'Deploy failed');
83
87
  exit(ok ? 0 : 1);
84
88
  }
85
- const success = yield commitAndDeployVersion(argv.name || undefined, entry, assets, argv.description || '', getRoot(), argv.environment || 'all', argv.minify, argv.version, (argv.bundle === false));
89
+ const success = yield commitAndDeployVersion(argv.name || undefined, entry, assets, argv.description || '', getRoot(), argv.environment || 'all', argv.minify, argv.version, (argv.bundle === false), argv['version-limit']);
86
90
  outro(success ? 'Deploy finished' : 'Deploy failed');
87
91
  if (success) {
88
92
  const projectConfig = getProjectConfig(getRoot());
@@ -1,10 +1,8 @@
1
- import worker from '$userPath';
2
1
  import Cache from './mock/cache.js';
3
2
  import mockKV from './mock/kv.js';
3
+ import worker from '$userPath';
4
4
 
5
- var mock_cache = new Cache($userPort);
6
- globalThis.mockCache = mock_cache;
5
+ Cache.port = $userPort;
7
6
  mockKV.port = $userPort;
8
- globalThis.mockKV = mockKV;
9
7
 
10
8
  export default worker;
@@ -1,7 +1,7 @@
1
1
  class MockCache {
2
- constructor(port) {
3
- this.port = port;
4
- }
2
+ static port = 0;
3
+
4
+ constructor() {}
5
5
 
6
6
  async put(reqOrUrl, response) {
7
7
  if (arguments.length < 2) {
@@ -30,7 +30,7 @@ class MockCache {
30
30
 
31
31
  const key = this.normalizeKey(reqOrUrl);
32
32
  const fetchRes = await fetch(
33
- `http://localhost:${this.port}/mock_cache/put`,
33
+ `http://localhost:${MockCache.port}/mock_cache/put`,
34
34
  {
35
35
  method: 'POST',
36
36
  headers: { 'Content-Type': 'application/json' },
@@ -58,7 +58,7 @@ class MockCache {
58
58
  async get(reqOrUrl) {
59
59
  const key = this.normalizeKey(reqOrUrl);
60
60
  const fetchRes = await fetch(
61
- `http://localhost:${this.port}/mock_cache/get`,
61
+ `http://localhost:${MockCache.port}/mock_cache/get`,
62
62
  {
63
63
  method: 'POST',
64
64
  headers: { 'Content-Type': 'application/json' },
@@ -85,7 +85,7 @@ class MockCache {
85
85
  async delete(reqOrUrl) {
86
86
  const key = this.normalizeKey(reqOrUrl);
87
87
  const fetchRes = await fetch(
88
- `http://localhost:${this.port}/mock_cache/delete`,
88
+ `http://localhost:${MockCache.port}/mock_cache/delete`,
89
89
  {
90
90
  method: 'POST',
91
91
  headers: { 'Content-Type': 'application/json' },
@@ -113,4 +113,7 @@ class MockCache {
113
113
  }
114
114
  }
115
115
 
116
+ var mock_cache = new MockCache();
117
+ globalThis.mockCache = mock_cache;
118
+
116
119
  export default MockCache;
@@ -163,4 +163,6 @@ class EdgeKV {
163
163
  }
164
164
  }
165
165
 
166
+ globalThis.mockKV = EdgeKV;
167
+
166
168
  export default EdgeKV;
@@ -17,7 +17,7 @@ import Haikunator from 'haikunator';
17
17
  import t from '../../i18n/index.js';
18
18
  import logger from '../../libs/logger.js';
19
19
  import Template from '../../libs/templates/index.js';
20
- import { execCommand } from '../../utils/command.js';
20
+ import { execCommand, execWithLoginShell } from '../../utils/command.js';
21
21
  import { getDirName } from '../../utils/fileUtils/base.js';
22
22
  import { generateConfigFile, getCliConfig, getProjectConfig, getTemplatesConfig, templateHubPath, updateProjectConfigFile } from '../../utils/fileUtils/index.js';
23
23
  import promptParameter from '../../utils/prompt.js';
@@ -382,7 +382,7 @@ export const createProject = (initParams) => __awaiter(void 0, void 0, void 0, f
382
382
  const templateFlag = ((_a = frameworkConfig.language) === null || _a === void 0 ? void 0 : _a[initParams.language || 'typescript']) || '';
383
383
  const extraParams = frameworkConfig.params || '';
384
384
  const full = `${command} ${initParams.name} ${templateFlag} ${extraParams}`.trim();
385
- const res = yield execCommand(['sh', '-lc', full], {
385
+ const res = yield execWithLoginShell(full, {
386
386
  interactive: true,
387
387
  startText: `Starting to execute framework command ${chalk.gray(full)}`,
388
388
  doneText: `Framework command executed ${chalk.gray(full)}`
@@ -153,3 +153,61 @@ export const getRoutineCodeVersions = (projectName) => __awaiter(void 0, void 0,
153
153
  const productionVersions = ((_m = (_l = (_k = (_j = (_h = routineDetail === null || routineDetail === void 0 ? void 0 : routineDetail.data) === null || _h === void 0 ? void 0 : _h.Envs) === null || _j === void 0 ? void 0 : _j.find((item) => item.Env === 'production')) === null || _k === void 0 ? void 0 : _k.CodeDeploy) === null || _l === void 0 ? void 0 : _l.CodeVersions) === null || _m === void 0 ? void 0 : _m.map((item) => item.CodeVersion)) || [];
154
154
  return { allVersions, stagingVersions, productionVersions };
155
155
  });
156
+ /**
157
+ * Check the total number of versions and delete the oldest unreleased ones if they exceed the limit.
158
+ * @param projectName Name of the project
159
+ * @param limit The maximum number of versions to keep
160
+ * @param isCreatingNewVersion Whether a new version will be created after cleanup
161
+ */
162
+ export function checkAndCleanupVersions(projectName_1, limit_1) {
163
+ return __awaiter(this, arguments, void 0, function* (projectName, limit, isCreatingNewVersion = true) {
164
+ if (limit <= 0)
165
+ return;
166
+ const { allVersions, stagingVersions, productionVersions } = yield getRoutineCodeVersions(projectName);
167
+ const currentCount = allVersions.length;
168
+ const targetCount = isCreatingNewVersion ? limit - 1 : limit;
169
+ if (currentCount <= targetCount) {
170
+ return;
171
+ }
172
+ const deployedVersions = new Set([...stagingVersions, ...productionVersions]);
173
+ // Filter versions that are not currently deployed
174
+ const undeployedVersions = allVersions.filter((v) => v.codeVersion && !deployedVersions.has(v.codeVersion));
175
+ // Sort by createTime (oldest first)
176
+ undeployedVersions.sort((a, b) => {
177
+ const timeA = a.createTime ? new Date(a.createTime).getTime() : 0;
178
+ const timeB = b.createTime ? new Date(b.createTime).getTime() : 0;
179
+ return timeA - timeB;
180
+ });
181
+ const numToDelete = currentCount - targetCount;
182
+ const toDelete = undeployedVersions.slice(0, numToDelete);
183
+ if (toDelete.length > 0) {
184
+ logger.info(t('cleanup_versions_start', {
185
+ count: toDelete.length.toString(),
186
+ limit: limit.toString()
187
+ }).d(`Cleaning up ${toDelete.length} oldest unreleased versions (limit: ${limit})...`));
188
+ const server = yield ApiService.getInstance();
189
+ for (const version of toDelete) {
190
+ if (!version.codeVersion)
191
+ continue;
192
+ try {
193
+ const res = yield server.deleteRoutineCodeVersion({
194
+ Name: projectName,
195
+ CodeVersion: version.codeVersion
196
+ });
197
+ if ((res === null || res === void 0 ? void 0 : res.Status) === 'OK') {
198
+ logger.success(t('deployments_delete_success').d('Delete success') +
199
+ `: ${version.codeVersion}`);
200
+ }
201
+ else {
202
+ logger.warn(t('deployments_delete_failed').d('Delete failed') +
203
+ `: ${version.codeVersion}`);
204
+ }
205
+ }
206
+ catch (error) {
207
+ logger.warn(t('deployments_delete_failed').d('Delete failed') +
208
+ `: ${version.codeVersion}`);
209
+ }
210
+ }
211
+ }
212
+ });
213
+ }
@@ -143,6 +143,9 @@ Description for Functions & Pages/version (skip interactive input)
143
143
  **--name, -n** _optional_
144
144
  Functions & Pages name
145
145
 
146
+ **--version-limit** _optional_
147
+ Maximum number of versions to keep. Oldest unreleased versions (not in staging or production) will be deleted if exceeded.
148
+
146
149
  ---
147
150
 
148
151
  ## deploy
@@ -174,6 +177,9 @@ Description of the version
174
177
  **--minify, -m** _optional_
175
178
  Whether to minify the code
176
179
 
180
+ **--version-limit** _optional_
181
+ Maximum number of versions to keep. Oldest unreleased versions (not in staging or production) will be deleted if exceeded.
182
+
177
183
  ---
178
184
 
179
185
  ## deployments
@@ -155,6 +155,9 @@ esa-cli commit [<ENTRY>] [OPTIONS]
155
155
  **--name, -n** _可选_
156
156
  **函数和Pages名称**
157
157
 
158
+ **--version-limit** _可选_
159
+ **保持的最大版本数量。如果超过此限制,将删除最旧的未发布版本(不处于仿真环境或线上环境的版本)。**
160
+
158
161
  ---
159
162
 
160
163
  ## deploy
@@ -186,6 +189,9 @@ esa-cli deploy [<ENTRY>] [OPTIONS]
186
189
  **--minify, -m** _可选_
187
190
  **是否压缩代码**
188
191
 
192
+ **--version-limit** _可选_
193
+ **保持的最大版本数量。如果超过此限制,将删除最旧的未发布版本(不处于仿真环境或线上环境的版本)。**
194
+
189
195
  ---
190
196
 
191
197
  ## deployments
@@ -1322,5 +1322,17 @@
1322
1322
  "login_get_credentials_from_environment_variables": {
1323
1323
  "en": "Get credentials from environment variables",
1324
1324
  "zh_CN": ""
1325
+ },
1326
+ "commit_option_version_limit": {
1327
+ "en": "Maximum number of versions to keep. Oldest unreleased versions will be deleted if exceeded.",
1328
+ "zh_CN": "保留的最大版本数量。如果超过此限制,最旧的未发布版本将被删除。"
1329
+ },
1330
+ "deploy_option_version_limit": {
1331
+ "en": "Maximum number of versions to keep. Oldest unreleased versions will be deleted if exceeded.",
1332
+ "zh_CN": "保留的最大版本数量。如果超过此限制,最旧的未发布版本将被删除。"
1333
+ },
1334
+ "cleanup_versions_start": {
1335
+ "en": "Cleaning up ${toDelete.length} oldest unreleased versions (limit: ${limit})...",
1336
+ "zh_CN": "正在清理 ${count} 个最旧的未发布版本(限制数量:${limit})..."
1325
1337
  }
1326
1338
  }
package/dist/index.js CHANGED
@@ -10,7 +10,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import chalk from 'chalk';
11
11
  import yargs from 'yargs';
12
12
  import { hideBin } from 'yargs/helpers';
13
- import logger from './libs/logger.js';
14
13
  import commit from './commands/commit/index.js';
15
14
  import config from './commands/config.js';
16
15
  import deploy from './commands/deploy/index.js';
@@ -25,6 +24,7 @@ import routeCommand from './commands/route/index.js';
25
24
  import routine from './commands/routine/index.js';
26
25
  import site from './commands/site/index.js';
27
26
  import t from './i18n/index.js';
27
+ import logger from './libs/logger.js';
28
28
  import { handleCheckVersion, checkCLIVersion } from './utils/checkVersion.js';
29
29
  import { getCliConfig } from './utils/fileUtils/index.js';
30
30
  const main = () => __awaiter(void 0, void 0, void 0, function* () {
package/dist/libs/api.js CHANGED
@@ -11,6 +11,7 @@ import ESA, * as $ESA from '@alicloud/esa20240910';
11
11
  import * as $OpenApi from '@alicloud/openapi-client';
12
12
  import * as $Util from '@alicloud/tea-util';
13
13
  import { getApiConfig } from '../utils/fileUtils/index.js';
14
+ import { CLI_USER_AGENT } from './constants.js';
14
15
  import logger from './logger.js';
15
16
  class Client {
16
17
  constructor() {
@@ -77,7 +78,8 @@ class Client {
77
78
  const apiConfig = new $OpenApi.Config({
78
79
  accessKeyId: (_a = config.auth) === null || _a === void 0 ? void 0 : _a.accessKeyId,
79
80
  accessKeySecret: (_b = config.auth) === null || _b === void 0 ? void 0 : _b.accessKeySecret,
80
- endpoint: config.endpoint
81
+ endpoint: config.endpoint,
82
+ userAgent: CLI_USER_AGENT
81
83
  });
82
84
  return new ESA.default(apiConfig);
83
85
  }
@@ -12,6 +12,7 @@ import FormData from 'form-data';
12
12
  import fetch from 'node-fetch';
13
13
  import t from '../i18n/index.js';
14
14
  import { getApiConfig } from '../utils/fileUtils/index.js';
15
+ import { CLI_USER_AGENT } from './constants.js';
15
16
  import { Environment } from './interface.js';
16
17
  export class ApiService {
17
18
  constructor(cliConfig) {
@@ -19,7 +20,8 @@ export class ApiService {
19
20
  let apiConfig = new $OpenApi.Config({
20
21
  accessKeyId: (_a = cliConfig.auth) === null || _a === void 0 ? void 0 : _a.accessKeyId,
21
22
  accessKeySecret: (_b = cliConfig.auth) === null || _b === void 0 ? void 0 : _b.accessKeySecret,
22
- endpoint: cliConfig.endpoint
23
+ endpoint: cliConfig.endpoint,
24
+ userAgent: CLI_USER_AGENT
23
25
  });
24
26
  this.client = new $OpenApi.default.default(apiConfig);
25
27
  }
@@ -37,7 +39,8 @@ export class ApiService {
37
39
  let apiConfig = new $OpenApi.Config({
38
40
  accessKeyId: (_a = newConfig.auth) === null || _a === void 0 ? void 0 : _a.accessKeyId,
39
41
  accessKeySecret: (_b = newConfig.auth) === null || _b === void 0 ? void 0 : _b.accessKeySecret,
40
- endpoint: newConfig.endpoint
42
+ endpoint: newConfig.endpoint,
43
+ userAgent: CLI_USER_AGENT
41
44
  });
42
45
  this.client = new $OpenApi.default.default(apiConfig);
43
46
  }
@@ -0,0 +1,13 @@
1
+ import { createRequire } from 'module';
2
+ const require = createRequire(import.meta.url);
3
+ const packageJson = require('../../package.json');
4
+ /**
5
+ * CLI User-Agent string for API requests
6
+ * Format: esa-cli/{version}
7
+ * This helps identify requests originating from the CLI
8
+ */
9
+ export const CLI_USER_AGENT = `esa-cli/${packageJson.version}`;
10
+ /**
11
+ * CLI version from package.json
12
+ */
13
+ export const CLI_VERSION = packageJson.version;
@@ -63,9 +63,6 @@ class Logger {
63
63
  get ora() {
64
64
  return this.spinner;
65
65
  }
66
- get isDebug() {
67
- return this.logger.level === 'debug';
68
- }
69
66
  setLogLevel(level) {
70
67
  this.logger.level = level;
71
68
  }
@@ -7,15 +7,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
+ import { spawn } from 'child_process';
11
+ import { platform } from 'os';
10
12
  import { cancel, spinner } from '@clack/prompts';
11
13
  import chalk from 'chalk';
12
- import spawn from 'cross-spawn';
13
- import logger from '../libs/logger.js';
14
+ export const isWindows = platform() === 'win32';
14
15
  /**
15
16
  * Execute a shell command with rich options (spinner, capture, env, cwd).
16
17
  */
17
18
  export const execCommand = (command_1, ...args_1) => __awaiter(void 0, [command_1, ...args_1], void 0, function* (command, options = {}) {
18
- const { startText, doneText, silent = false, captureOutput = false, useSpinner = true, realtimeOutput = false, interactive = false, env, cwd, transformOutput, fallbackOutput, errorMessage, debug = logger.isDebug } = options;
19
+ const { startText, doneText, silent = false, captureOutput = false, useSpinner = true, realtimeOutput = false, interactive = false, env, cwd, transformOutput, fallbackOutput, errorMessage, shell = false } = options;
19
20
  // Determine stdio mode based on options
20
21
  // If realtimeOutput is true, we need to pipe to capture and display output in real-time
21
22
  // If spinner is used without realtimeOutput, pipe to avoid TTY contention
@@ -56,7 +57,7 @@ export const execCommand = (command_1, ...args_1) => __awaiter(void 0, [command_
56
57
  stdio,
57
58
  cwd,
58
59
  env: Object.assign(Object.assign({}, process.env), env),
59
- shell: false
60
+ shell
60
61
  });
61
62
  if (stdio === 'pipe') {
62
63
  (_a = child.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (chunk) => {
@@ -141,27 +142,27 @@ export const execCommand = (command_1, ...args_1) => __awaiter(void 0, [command_
141
142
  if (stderr)
142
143
  process.stderr.write(stderr);
143
144
  }
144
- if (debug) {
145
- console.error(chalk.red('\n--- DEBUG INFO ---'));
146
- console.error(chalk.red(`Command: ${command.join(' ')}`));
147
- if (cwd)
148
- console.error(chalk.red(`CWD: ${cwd}`));
149
- if (stdout)
150
- console.error(chalk.red(`Stdout:\n${stdout}`));
151
- if (stderr)
152
- console.error(chalk.red(`Stderr:\n${stderr}`));
153
- if (e instanceof Error && e.stack) {
154
- console.error(chalk.red(`Stack:\n${e.stack}`));
155
- }
156
- else if (e && typeof e === 'object') {
157
- console.error(chalk.red(`Error Object:\n${JSON.stringify(e, null, 2)}`));
158
- }
159
- console.error(chalk.red('------------------\n'));
160
- }
161
145
  if (errorMessage) {
162
146
  cancel(errorMessage);
163
147
  }
164
148
  return { success: false, stdout, stderr };
165
149
  }
166
150
  });
151
+ /**
152
+ * Execute a command with login shell on Unix (to load ~/.profile, ~/.zshrc etc.)
153
+ * On Windows, directly execute the command since login shell is not typically needed.
154
+ *
155
+ * @param commandStr - The full command string to execute
156
+ * @param options - ExecCommandOptions
157
+ */
158
+ export const execWithLoginShell = (commandStr_1, ...args_1) => __awaiter(void 0, [commandStr_1, ...args_1], void 0, function* (commandStr, options = {}) {
159
+ if (isWindows) {
160
+ // Windows: use cmd /c to execute the command
161
+ return execCommand(['cmd', '/c', commandStr], options);
162
+ }
163
+ else {
164
+ // Unix: use login shell to ensure PATH is properly set (nvm, fnm, etc.)
165
+ return execCommand(['sh', '-lc', commandStr], options);
166
+ }
167
+ });
167
168
  export default execCommand;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "esa-cli",
3
- "version": "1.0.3-beta.4",
3
+ "version": "1.0.4-beta.0",
4
4
  "description": "A CLI for operating Alibaba Cloud ESA Functions and Pages.",
5
5
  "main": "bin/enter.cjs",
6
6
  "type": "module",