esa-cli 0.0.1-beta.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 (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +175 -0
  3. package/bin/enter.cjs +52 -0
  4. package/dist/README.md +175 -0
  5. package/dist/bin/enter.cjs +52 -0
  6. package/dist/cliconfig.toml +6 -0
  7. package/dist/commands/commit/index.js +147 -0
  8. package/dist/commands/commit/prodBuild.js +50 -0
  9. package/dist/commands/common/constant.js +54 -0
  10. package/dist/commands/config.js +51 -0
  11. package/dist/commands/deploy/helper.js +108 -0
  12. package/dist/commands/deploy/index.js +217 -0
  13. package/dist/commands/deployments/delete.js +92 -0
  14. package/dist/commands/deployments/index.js +27 -0
  15. package/dist/commands/deployments/list.js +89 -0
  16. package/dist/commands/dev/config/devBuild.js +26 -0
  17. package/dist/commands/dev/config/devEntry.js +71 -0
  18. package/dist/commands/dev/config/mock/cache.js +31 -0
  19. package/dist/commands/dev/config/mock/kv.js +87 -0
  20. package/dist/commands/dev/devPack.js +113 -0
  21. package/dist/commands/dev/doProcess.js +73 -0
  22. package/dist/commands/dev/index.js +240 -0
  23. package/dist/commands/dev/server.js +100 -0
  24. package/dist/commands/domain/add.js +72 -0
  25. package/dist/commands/domain/delete.js +74 -0
  26. package/dist/commands/domain/index.js +29 -0
  27. package/dist/commands/domain/list.js +51 -0
  28. package/dist/commands/init/index.js +149 -0
  29. package/dist/commands/lang.js +32 -0
  30. package/dist/commands/login/index.js +108 -0
  31. package/dist/commands/logout.js +44 -0
  32. package/dist/commands/route/add.js +115 -0
  33. package/dist/commands/route/delete.js +74 -0
  34. package/dist/commands/route/index.js +29 -0
  35. package/dist/commands/route/list.js +63 -0
  36. package/dist/commands/routine/delete.js +54 -0
  37. package/dist/commands/routine/index.js +27 -0
  38. package/dist/commands/routine/list.js +101 -0
  39. package/dist/commands/site/index.js +25 -0
  40. package/dist/commands/site/list.js +47 -0
  41. package/dist/commands/utils.js +139 -0
  42. package/dist/components/descriptionInput.js +38 -0
  43. package/dist/components/filterSelector.js +132 -0
  44. package/dist/components/mutiSelectTable.js +95 -0
  45. package/dist/components/selectInput.js +17 -0
  46. package/dist/components/selectItem.js +6 -0
  47. package/dist/components/yesNoPrompt.js +9 -0
  48. package/dist/docs/Commands_en.md +224 -0
  49. package/dist/docs/Commands_zh_CN.md +224 -0
  50. package/dist/docs/dev.png +0 -0
  51. package/dist/i18n/index.js +27 -0
  52. package/dist/i18n/locales.json +766 -0
  53. package/dist/index.js +80 -0
  54. package/dist/libs/apiService.js +914 -0
  55. package/dist/libs/git/index.js +52 -0
  56. package/dist/libs/interface.js +21 -0
  57. package/dist/libs/logger.js +149 -0
  58. package/dist/libs/request.js +98 -0
  59. package/dist/libs/templates/index.js +16 -0
  60. package/dist/package.json +93 -0
  61. package/dist/utils/checkDevPort.js +113 -0
  62. package/dist/utils/checkIsRoutineCreated.js +56 -0
  63. package/dist/utils/checkVersion.js +26 -0
  64. package/dist/utils/debounce.js +18 -0
  65. package/dist/utils/fileUtils/index.js +219 -0
  66. package/dist/utils/fileUtils/interface.js +1 -0
  67. package/dist/utils/install/install.ps1 +33 -0
  68. package/dist/utils/install/install.sh +53 -0
  69. package/dist/utils/installRuntime.js +80 -0
  70. package/dist/utils/openInBrowser.js +24 -0
  71. package/dist/utils/sleep.js +6 -0
  72. package/dist/zh_CN.md +177 -0
  73. package/package.json +93 -0
@@ -0,0 +1,219 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import toml from '@iarna/toml';
11
+ import path from 'path';
12
+ import fs from 'fs';
13
+ import { promises as fsPromises } from 'fs';
14
+ import { fileURLToPath } from 'url';
15
+ import logger from '../../libs/logger.js';
16
+ import t from '../../i18n/index.js';
17
+ import os from 'os';
18
+ const projectConfigFile = 'esa.toml';
19
+ const cliConfigFile = 'cliconfig.toml';
20
+ export const getDirName = (metaUrl) => {
21
+ const __filename = fileURLToPath(metaUrl);
22
+ const __dirname = path.dirname(__filename);
23
+ return __dirname;
24
+ };
25
+ const __dirname = getDirName(import.meta.url);
26
+ export const getRoot = (root) => {
27
+ if (typeof root === 'undefined') {
28
+ root = process.cwd();
29
+ }
30
+ if (root === '/') {
31
+ return process.cwd();
32
+ }
33
+ const file = path.join(root, cliConfigFile);
34
+ const prev = path.resolve(root, '../');
35
+ try {
36
+ const hasToml = fs.existsSync(file);
37
+ if (hasToml) {
38
+ return root;
39
+ }
40
+ else {
41
+ return getRoot(prev);
42
+ }
43
+ }
44
+ catch (err) {
45
+ return getRoot(prev);
46
+ }
47
+ };
48
+ const root = getRoot();
49
+ export const projectConfigPath = path.join(root, projectConfigFile);
50
+ // export const cliConfigPath = path.join(__dirname, '..', '..', cliConfigFile);
51
+ export const cliConfigPath = path.join(os.homedir(), '.esa/config/default.toml');
52
+ export const hiddenConfigDir = path.join(os.homedir(), '.esa/config');
53
+ export const generateHiddenConfigDir = () => {
54
+ if (!fs.existsSync(hiddenConfigDir)) {
55
+ fs.mkdirSync(hiddenConfigDir, { recursive: true });
56
+ }
57
+ };
58
+ export const generateToml = (path) => {
59
+ if (!fs.existsSync(path)) {
60
+ fs.writeFileSync(path, '', 'utf-8');
61
+ // 添加默认的endpoint
62
+ const defaultConfig = {
63
+ endpoint: 'esa.cn-hangzhou.aliyuncs.com'
64
+ };
65
+ updateCliConfigFile(defaultConfig);
66
+ }
67
+ };
68
+ export const generateDefaultConfig = () => {
69
+ generateHiddenConfigDir();
70
+ generateToml(cliConfigPath);
71
+ };
72
+ export function updateProjectConfigFile(configUpdate_1) {
73
+ return __awaiter(this, arguments, void 0, function* (configUpdate, filePath = root) {
74
+ const configPath = path.join(filePath, projectConfigFile);
75
+ try {
76
+ let configFileContent = yield fsPromises.readFile(configPath, 'utf8');
77
+ let config = toml.parse(configFileContent);
78
+ config = Object.assign(Object.assign({}, config), configUpdate);
79
+ const updatedConfigString = toml.stringify(config);
80
+ yield fsPromises.writeFile(configPath, updatedConfigString);
81
+ }
82
+ catch (error) {
83
+ logger.error(`Error updating TOML file: ${error}`);
84
+ logger.pathEacces(__dirname);
85
+ }
86
+ });
87
+ }
88
+ export function updateCliConfigFile(configUpdate) {
89
+ return __awaiter(this, void 0, void 0, function* () {
90
+ try {
91
+ let configFileContent = yield fsPromises.readFile(cliConfigPath, 'utf8');
92
+ let config = toml.parse(configFileContent);
93
+ config = Object.assign(Object.assign({}, config), configUpdate);
94
+ const updatedConfigString = toml.stringify(config);
95
+ yield fsPromises.writeFile(cliConfigPath, updatedConfigString);
96
+ }
97
+ catch (error) {
98
+ logger.error(`Error updating TOML file: ${error}`);
99
+ logger.pathEacces(__dirname);
100
+ throw new Error('Login error');
101
+ }
102
+ });
103
+ }
104
+ export function readConfigFile(configPath) {
105
+ if (fs.existsSync(configPath)) {
106
+ const configFileContent = fs.readFileSync(configPath, 'utf-8');
107
+ try {
108
+ const config = toml.parse(configFileContent);
109
+ return config;
110
+ }
111
+ catch (error) {
112
+ logger.error(`Error parsing TOML file: ${error}`);
113
+ return null;
114
+ }
115
+ }
116
+ return null;
117
+ }
118
+ export function getCliConfig() {
119
+ const res = readConfigFile(cliConfigPath);
120
+ if (!res) {
121
+ return null;
122
+ }
123
+ return res;
124
+ }
125
+ export function getProjectConfig(filePath = root) {
126
+ const res = readConfigFile(path.join(filePath, projectConfigFile));
127
+ if (!res) {
128
+ return null;
129
+ }
130
+ return res;
131
+ }
132
+ export function readEdgeRoutineFile(projectPath = root) {
133
+ const projectConfig = getProjectConfig(projectPath);
134
+ if (!projectConfig) {
135
+ return null;
136
+ }
137
+ const pubFilePath = `.dev/pub.js`;
138
+ const edgeRoutinePath = path.join(projectPath, pubFilePath);
139
+ if (fs.existsSync(edgeRoutinePath)) {
140
+ const edgeRoutineFile = fs.readFileSync(edgeRoutinePath, 'utf-8');
141
+ return edgeRoutineFile;
142
+ }
143
+ return null;
144
+ }
145
+ export function getConfigurations() {
146
+ try {
147
+ const [cliConfig, projectConfig] = [getCliConfig(), getProjectConfig()];
148
+ return [cliConfig, projectConfig];
149
+ }
150
+ catch (error) {
151
+ return [null, null];
152
+ }
153
+ }
154
+ export function generateConfigFile(projectName, initConfigs) {
155
+ return __awaiter(this, void 0, void 0, function* () {
156
+ var _a;
157
+ const newFilePath = path.join(process.cwd(), 'esa.toml');
158
+ const currentDirName = path.basename(process.cwd());
159
+ const entry = ((_a = initConfigs === null || initConfigs === void 0 ? void 0 : initConfigs.dev) === null || _a === void 0 ? void 0 : _a.entry) || 'src/index.js';
160
+ const port = (initConfigs === null || initConfigs === void 0 ? void 0 : initConfigs.port) || 18080;
161
+ const name = projectName || currentDirName;
162
+ if (fs.existsSync(newFilePath)) {
163
+ logger.error(t('generate_config_error').d('esa.toml already exists'));
164
+ return;
165
+ }
166
+ else {
167
+ const genConfig = `name = "${name}"
168
+ entry = "${entry}"
169
+ [dev]
170
+ port = ${port}
171
+ `;
172
+ yield fsPromises.writeFile(newFilePath, genConfig, 'utf-8');
173
+ logger.success(t('generate_config_success').d('Generated esa.toml'));
174
+ }
175
+ });
176
+ }
177
+ export function getConfigValue(globalValue, configValue, defaultValue) {
178
+ if (globalValue !== undefined) {
179
+ return globalValue;
180
+ }
181
+ if (configValue !== undefined) {
182
+ return configValue;
183
+ }
184
+ return defaultValue;
185
+ }
186
+ export function getDevConf(name, type, defaultValue) {
187
+ const projectConfig = getProjectConfig();
188
+ const configPath = `${type ? type + '.' : ''}${name}`;
189
+ const userConf = configPath.split('.').reduce((current, key) => {
190
+ return current && key in current ? current[key] : undefined;
191
+ }, projectConfig);
192
+ // @ts-ignore
193
+ const globalConf = global[name];
194
+ return getConfigValue(globalConf, userConf, defaultValue);
195
+ }
196
+ export function getDevOpenBrowserUrl() {
197
+ const port = getDevConf('port', 'dev', 18080);
198
+ return `http://localhost:${port}`;
199
+ }
200
+ export const getApiConfig = () => {
201
+ var _a, _b;
202
+ const [cliConfig, projectConfig] = getConfigurations();
203
+ let defaultConfig = {
204
+ auth: {
205
+ accessKeyId: '',
206
+ accessKeySecret: ''
207
+ },
208
+ endpoint: `esa.cn-hangzhou.aliyuncs.com`
209
+ };
210
+ const combinedConfig = Object.assign(Object.assign(Object.assign({}, defaultConfig), cliConfig), projectConfig);
211
+ const config = {
212
+ auth: {
213
+ accessKeyId: (_a = combinedConfig.auth) === null || _a === void 0 ? void 0 : _a.accessKeyId,
214
+ accessKeySecret: (_b = combinedConfig.auth) === null || _b === void 0 ? void 0 : _b.accessKeySecret
215
+ },
216
+ endpoint: combinedConfig.endpoint
217
+ };
218
+ return config;
219
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env pwsh
2
+ # Modified the official Deno download script to adapt to the domestic CDN
3
+
4
+ $ErrorActionPreference = 'Stop'
5
+
6
+ $DenoInstall = $env:DENO_INSTALL
7
+ $BinDir = if ($DenoInstall) {
8
+ "${DenoInstall}\bin"
9
+ } else {
10
+ "${Home}\.deno\bin"
11
+ }
12
+
13
+ $DenoZip = "$BinDir\deno.zip"
14
+ $Target = 'x86_64-pc-windows-msvc'
15
+
16
+ $DownloadUrl = "http://esa-runtime.myalicdn.com/runtime/deno-${Target}.zip"
17
+
18
+ if (!(Test-Path $BinDir)) {
19
+ New-Item $BinDir -ItemType Directory | Out-Null
20
+ }
21
+
22
+ curl.exe -Lo $DenoZip $DownloadUrl
23
+
24
+ tar.exe xf $DenoZip -C $BinDir
25
+
26
+ Remove-Item $DenoZip
27
+
28
+ $User = [System.EnvironmentVariableTarget]::User
29
+ $Path = [System.Environment]::GetEnvironmentVariable('Path', $User)
30
+ if (!(";${Path};".ToLower() -like "*;${BinDir};*".ToLower())) {
31
+ [System.Environment]::SetEnvironmentVariable('Path', "${Path};${BinDir}", $User)
32
+ $Env:Path += ";${BinDir}"
33
+ }
@@ -0,0 +1,53 @@
1
+ #!/bin/sh
2
+ # Modified the official Deno download script to adapt to the domestic CDN
3
+
4
+ set -e
5
+
6
+ if ! command -v unzip >/dev/null && ! command -v 7z >/dev/null; then
7
+ echo "Error: either unzip or 7z is required to install Deno." 1>&2
8
+ exit 1
9
+ fi
10
+
11
+ if [ "$OS" = "Windows_NT" ]; then
12
+ target="x86_64-pc-windows-msvc"
13
+ else
14
+ case $(uname -sm) in
15
+ "Darwin x86_64") target="x86_64-apple-darwin" ;;
16
+ "Darwin arm64") target="aarch64-apple-darwin" ;;
17
+ "Linux aarch64") target="aarch64-unknown-linux-gnu" ;;
18
+ *) target="x86_64-unknown-linux-gnu" ;;
19
+ esac
20
+ fi
21
+
22
+ deno_uri="http://esa-runtime.myalicdn.com/runtime/deno-${target}.zip"
23
+ deno_install="${DENO_INSTALL:-$HOME/.deno}"
24
+ bin_dir="$deno_install/bin"
25
+ exe="$bin_dir/deno"
26
+
27
+ if [ ! -d "$bin_dir" ]; then
28
+ mkdir -p "$bin_dir"
29
+ fi
30
+ echo $deno_uri
31
+ curl --fail --location --progress-bar --output "$exe.zip" "$deno_uri"
32
+ if command -v unzip >/dev/null; then
33
+ unzip -d "$bin_dir" -o "$exe.zip"
34
+ else
35
+ 7z x -o"$bin_dir" -y "$exe.zip"
36
+ fi
37
+ chmod +x "$exe"
38
+ rm "$exe.zip"
39
+
40
+ echo "Runtime was installed successfully to $exe"
41
+ if command -v deno >/dev/null; then
42
+ echo "Run 'deno --help' to get started"
43
+ else
44
+ case $SHELL in
45
+ /bin/zsh) shell_profile=".zshrc" ;;
46
+ *) shell_profile=".bashrc" ;;
47
+ esac
48
+ echo "Manually add the directory to your \$HOME/$shell_profile (or similar)"
49
+ echo " export DENO_INSTALL=\"$deno_install\""
50
+ echo " export PATH=\"\$DENO_INSTALL/bin:\$PATH\""
51
+ echo "Run '$exe --help' to get started"
52
+ fi
53
+
@@ -0,0 +1,80 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { exec, execSync } from 'child_process';
11
+ import os from 'os';
12
+ import { getDirName } from './fileUtils/index.js';
13
+ import logger from '../libs/logger.js';
14
+ import path from 'path';
15
+ import t from '../i18n/index.js';
16
+ export function preCheckRuntime() {
17
+ return __awaiter(this, void 0, void 0, function* () {
18
+ const command = yield checkRuntimeInstalled();
19
+ if (!command) {
20
+ logger.error(t('install_runtime_explain').d('Under the beta phase, we are temporarily using Deno as the local development runtime. It needs to be installed first.'));
21
+ installDeno();
22
+ return false;
23
+ }
24
+ return command;
25
+ });
26
+ }
27
+ function checkDeno(command) {
28
+ return new Promise((resolve, reject) => {
29
+ exec(`${command} --version`, (err) => {
30
+ if (err) {
31
+ reject();
32
+ }
33
+ else {
34
+ resolve(command);
35
+ }
36
+ });
37
+ });
38
+ }
39
+ export function checkRuntimeInstalled() {
40
+ const homeDeno = path.resolve(os.homedir(), '.deno/bin/deno');
41
+ return new Promise((resolve) => {
42
+ // @ts-ignore
43
+ Promise.any([checkDeno('deno'), checkDeno(homeDeno)])
44
+ .then((res) => {
45
+ resolve(res);
46
+ })
47
+ .catch((err) => {
48
+ console.log(err);
49
+ resolve(false);
50
+ });
51
+ });
52
+ }
53
+ export function installDeno() {
54
+ return __awaiter(this, void 0, void 0, function* () {
55
+ let installCommand;
56
+ const __dirname = getDirName(import.meta.url);
57
+ const p = path.join(__dirname, './install');
58
+ switch (os.platform()) {
59
+ case 'win32':
60
+ installCommand = `powershell.exe -Command "Get-Content '${p}/install.ps1' | iex"`;
61
+ break;
62
+ case 'darwin':
63
+ case 'linux':
64
+ installCommand = `sh ${p}/install.sh`;
65
+ break;
66
+ default:
67
+ installCommand = `sh ${p}/install.sh`;
68
+ }
69
+ logger.warn(t('install_runtime_tip').d(`🔔 Runtime must be installed to use esa dev. Installing...`));
70
+ try {
71
+ execSync(installCommand, { stdio: 'inherit' });
72
+ logger.success(t('install_runtime_success').d(`Runtime installed.`));
73
+ return true;
74
+ }
75
+ catch (error) {
76
+ logger.error(`Failed to install: ${error.message}`);
77
+ return false;
78
+ }
79
+ });
80
+ }
@@ -0,0 +1,24 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import open from 'open';
11
+ import logger from '../libs/logger.js';
12
+ import t from '../i18n/index.js';
13
+ /**
14
+ * Open url in browser
15
+ * @param {string} url
16
+ */
17
+ const openInBrowser = (url) => __awaiter(void 0, void 0, void 0, function* () {
18
+ const childProcess = yield open(url);
19
+ logger.success(t('open_in_browser_success', { url }).d(`Opened: ${url}`));
20
+ childProcess.on('error', () => {
21
+ logger.error(t('open_in_browser_error').d(`Failed to open: ${url}`));
22
+ });
23
+ });
24
+ export default openInBrowser;
@@ -0,0 +1,6 @@
1
+ const sleep = (ms = 1000) => {
2
+ return new Promise((resolve) => {
3
+ setTimeout(resolve, ms);
4
+ });
5
+ };
6
+ export default sleep;
package/dist/zh_CN.md ADDED
@@ -0,0 +1,177 @@
1
+ # ESA CLI
2
+
3
+ 用于操作阿里云 ESA EdgeRoutine(边缘函数) 的 CLI,支持快速创建边缘函数、本地调试、版本发布与部署、触发器管理。
4
+
5
+ 简体中文 | [English](./README.md)
6
+
7
+ #### 参考
8
+
9
+ - [边缘安全加速(ESA)](https://www.aliyun.com/product/esa)
10
+ - [边缘函数概述](https://help.aliyun.com/document_detail/2710021.html)
11
+
12
+ > **注意**: ESA CLI 处于公测阶段,如果您在使用中遇到任何问题,或者有任何建议,请随时提交 issue 或 pull request。
13
+ >
14
+ > 我们正在积极改进,并欢迎任何反馈。感谢您的理解与支持!
15
+
16
+ ## 安装
17
+
18
+ 使用npm安装并运行CLI命令:
19
+
20
+ ```bash
21
+ $ npm install esa-cli -g # 全局安装
22
+ $ esa -v # 查看版本
23
+ ```
24
+
25
+ ## 使用指南
26
+
27
+ ### 1. 初始化Routine项目
28
+
29
+ ```bash
30
+ & esa init
31
+ ```
32
+
33
+ 初始化命令有完整的流程引导,根据提示输入项目名称、选择模板即可。
34
+
35
+ ### 2. 开始本地调试
36
+
37
+ 本地调试功能是 CLI 的核心,提供了比阿里云 ESA 控制台更便捷的调试体验。
38
+
39
+ ```bash
40
+ & cd <Project Name>
41
+ & esa dev [options]
42
+ ```
43
+
44
+ #### 编写代码
45
+
46
+ 在EdgeRoutine 项目中,入口文件为 `src/index.js`,基本结构如下:
47
+
48
+ ```javascript
49
+ const html = `<!DOCTYPE html>
50
+ <body>
51
+ <h1>Hello World!</h1>
52
+ </body>`;
53
+
54
+ async function handleRequest(request) {
55
+ return new Response(html, {
56
+ headers: {
57
+ 'content-type': 'text/html;charset=UTF-8'
58
+ }
59
+ });
60
+ }
61
+
62
+ export default {
63
+ async fetch(event) {
64
+ return event.respondWith(handleRequest(event));
65
+ }
66
+ };
67
+ ```
68
+
69
+ 更多 EdgeRoutine 的 API 与语法,请参考[API文档](https://help.aliyun.com/document_detail/2710024.html)
70
+
71
+ #### 本地调试
72
+
73
+ 执行 `esa dev` 后,会自动打包入口文件,并启动本地调试服务,界面样式如下:
74
+
75
+ ![调试界面](./docs/dev.png)
76
+
77
+ - 在界面上按 `b` 即可在浏览器中打开调试页面。
78
+ - 在界面上按 `d` 可以查看调试引导。**Chrome 不允许命令行打开调试页面。**
79
+ - 在 Chrome 浏览器中打开 `Chrome://inspect#devices` 页面,可以看到一个运行的`Remote Target`,点击下面的`inspect`即可查看 console 信息。**注意,EdgeRoutine 的代码为服务端代码,所以预览页面的控制台并不会输出入口文件中的 `console`,只能通过`inspect`调试。**
80
+ - 在界面上按 `c` 可以清空面板。
81
+ - 在界面上按 `x` 可以退出调试。
82
+ - 可以用 `esa dev --port <port>` 临时指定端口,也可以使用 `esa config -l` 按照项目配置端口。
83
+
84
+ ### 3. 登录阿里云账号
85
+
86
+ 需要先登录阿里云账号,才能进行远程管理操作。
87
+
88
+ 首先请访问[阿里云RAM控制台](https://ram.console.aliyun.com/manage/ak)获取您的AccessKey ID和AccessKey Secret,再执行`esa login`根据提示输入。
89
+
90
+ ```bash
91
+ & esa login # 登录
92
+ & esa logout # 登出
93
+ ```
94
+
95
+ ### 4. 生成版本
96
+
97
+ 当本地调试完成后,需要生成一个代码版本用于部署。
98
+
99
+ ```bash
100
+ & esa commit # 生成版本
101
+ ```
102
+
103
+ ### 5. 部署到环境 & 管理版本与部署
104
+
105
+ 当代码版本生成后,需要部署到边缘节点。
106
+
107
+ 通过`esa deployments [script]`命令可以管理版本与部署情况。
108
+
109
+ ```bash
110
+ & esa deploy # 根据提示选择版本、目标环境即可部署
111
+ & esa deployments list # 查看部署情况
112
+ & esa deployments delete <versionId> # 删除版本
113
+ ```
114
+
115
+ _注意:已经被部署的版本无法删除。_
116
+
117
+ ### 6. 管理触发器
118
+
119
+ 当被部署到节点后,您可以配置触发器,通过触发器可以访问您的边缘函数。触发器有两种:
120
+
121
+ - 域名:为您的函数绑定域名,该域名必须是您ESA站点的子域名,您可以通过域名直接访问到该函数,此时边缘函数将作为该域名的源站。
122
+ - 路由:为您的ESA站点绑定函数路由,访问该路由可触发边缘函数执行,此时边缘函数可以和站点的源站进行通信。
123
+
124
+ ```bash
125
+ # 域名
126
+ & esa domain list
127
+ & esa domain add <domainName> # 需要是您的已备案域名
128
+ & esa domain delete <domainName>
129
+
130
+ # 路由
131
+ & esa route list
132
+ & esa route add [route] [site]
133
+ & esa route delete <route>
134
+ ```
135
+
136
+ ### 7. 管理函数
137
+
138
+ 可以通过CLI查看、删除Routine函数。
139
+
140
+ ```bash
141
+ & esa routine list # 查看函数
142
+ & esa routine delete <routineName> # 删除函数
143
+ ```
144
+
145
+ ## 命令
146
+
147
+ 查看[命令](./docs/Commands.md)
148
+
149
+ ## 配置文件
150
+
151
+ ### 全局配置
152
+
153
+ ```toml
154
+ endpoint = "" # ESA API Endpoint
155
+ lang = "zh_CN" # 语言
156
+
157
+ [auth]
158
+ accessKeyId = "" # AccessKey ID
159
+ accessKeySecret = "" # AccessKey Secret
160
+ ```
161
+
162
+ ### 项目配置
163
+
164
+ ```toml
165
+ name = "Hello World" # 项目名称
166
+ description = "Hello World" # 项目描述
167
+ entry = "src/index.js" # 入口文件
168
+ codeVersions = [ ] # 代码版本
169
+
170
+ [dev]
171
+ port = 18080 # 调试端口
172
+ localUpstream = '' # 本地调试上游源站,会替换掉回源时当前的origin
173
+ ```
174
+
175
+ ## LICENSE
176
+
177
+ [The MIT License](./LICENSE)
package/package.json ADDED
@@ -0,0 +1,93 @@
1
+ {
2
+ "name": "esa-cli",
3
+ "version": "0.0.1-beta.1",
4
+ "description": "A CLI for operating Alibaba Cloud ESA EdgeRoutine (Edge Functions).",
5
+ "main": "bin/enter.cjs",
6
+ "type": "module",
7
+ "files": [
8
+ "dist/"
9
+ ],
10
+ "bin": {
11
+ "esa": "bin/enter.cjs"
12
+ },
13
+ "scripts": {
14
+ "build": "rm -rf ./dist && rm -rf ./build && node ./genLocale.cjs && tsc && node ./copy.cjs",
15
+ "watch": "tsc --watch",
16
+ "eslint": "eslint src/ --ext .js,.jsx,.ts,.tsx",
17
+ "lint-staged": "lint-staged",
18
+ "prepare": "npm run build",
19
+ "test": "vitest",
20
+ "coverage": "vitest --coverage",
21
+ "preuninstall": "rm -rf ~/.esa/config"
22
+ },
23
+ "keywords": [
24
+ "cli",
25
+ "edge",
26
+ "esa",
27
+ "alibaba",
28
+ "ali"
29
+ ],
30
+ "author": "Kinice",
31
+ "license": "MIT",
32
+ "devDependencies": {
33
+ "@babel/plugin-syntax-import-attributes": "^7.24.7",
34
+ "@types/cli-table": "^0.3.4",
35
+ "@types/cross-spawn": "^6.0.6",
36
+ "@types/fs-extra": "^11.0.4",
37
+ "@types/jest": "^29.5.12",
38
+ "@types/lodash": "^4.14.202",
39
+ "@types/node": "^20.8.10",
40
+ "@types/node-fetch": "^2.6.9",
41
+ "@types/portscanner": "^2.1.4",
42
+ "@types/react": "^18.2.47",
43
+ "@types/yargs": "^17.0.32",
44
+ "@typescript-eslint/parser": "^6.9.1",
45
+ "eslint": "^8.52.0",
46
+ "eslint-config-prettier": "^9.0.0",
47
+ "eslint-plugin-react": "^7.33.2",
48
+ "eslint-plugin-react-hooks": "^4.6.0",
49
+ "husky": "^8.0.3",
50
+ "lint-staged": "^15.0.2",
51
+ "prettier": "^3.0.3",
52
+ "react": "^18.2.0",
53
+ "react-dom": "^18.2.0",
54
+ "rimraf": "^6.0.1",
55
+ "tsc-files": "^1.1.4",
56
+ "typescript": "^5.2.2",
57
+ "vitest": "^2.0.4"
58
+ },
59
+ "dependencies": {
60
+ "@alicloud/openapi-client": "^0.4.7",
61
+ "@alife/esa-cli-test": "^0.0.1-beta.1",
62
+ "@babel/parser": "^7.24.4",
63
+ "@babel/traverse": "^7.24.1",
64
+ "@iarna/toml": "^2.2.5",
65
+ "@tqman/ink-table": "^0.0.0-development",
66
+ "@types/inquirer": "^9.0.7",
67
+ "@vitest/coverage-istanbul": "^2.0.4",
68
+ "chalk": "^5.3.0",
69
+ "chokidar": "^3.5.3",
70
+ "cli-table3": "^0.6.5",
71
+ "cross-spawn": "^7.0.3",
72
+ "esbuild": "^0.21.1",
73
+ "esbuild-plugin-less": "^1.3.8",
74
+ "form-data": "^4.0.0",
75
+ "fs-extra": "^11.2.0",
76
+ "ink": "^5.0.1",
77
+ "ink-select-input": "^6.0.0",
78
+ "ink-text-input": "^6.0.0",
79
+ "inquirer": "^9.2.15",
80
+ "js-base64": "^3.7.7",
81
+ "node-fetch": "^3.3.2",
82
+ "open": "^9.1.0",
83
+ "ora": "^7.0.1",
84
+ "os-locale": "^6.0.2",
85
+ "portscanner": "^2.2.0",
86
+ "winston": "^3.11.0",
87
+ "yargs": "^17.7.2"
88
+ },
89
+ "repository": {
90
+ "type": "git",
91
+ "url": "git+ssh://git@github.com/aliyun/alibabacloud-esa-cli.git"
92
+ }
93
+ }