@volc-emr/emr-cli 0.1.0-beta.0 → 0.1.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 (47) hide show
  1. package/README.md +13 -325
  2. package/bin/emr-cli +3 -0
  3. package/dist/auth/index.d.ts +2 -0
  4. package/dist/auth/index.js +116 -0
  5. package/dist/client/index.d.ts +17 -0
  6. package/dist/client/index.js +129 -0
  7. package/dist/cluster/index.d.ts +13 -0
  8. package/dist/cluster/index.js +174 -0
  9. package/dist/index.d.ts +2 -0
  10. package/dist/index.js +15 -176
  11. package/dist/utils/auth.d.ts +7 -0
  12. package/dist/utils/auth.js +18 -0
  13. package/dist/utils/client-state.d.ts +12 -0
  14. package/dist/utils/client-state.js +80 -0
  15. package/dist/utils/config.d.ts +17 -0
  16. package/dist/utils/config.js +103 -0
  17. package/dist/utils/http.d.ts +13 -0
  18. package/dist/utils/http.js +76 -0
  19. package/dist/utils/volc/emr.d.ts +30 -0
  20. package/dist/utils/volc/emr.js +50 -0
  21. package/dist/utils/volc/openapi.d.ts +19 -0
  22. package/dist/utils/volc/openapi.js +122 -0
  23. package/dist/version.d.ts +1 -0
  24. package/dist/version.js +6 -0
  25. package/package.json +13 -40
  26. package/LICENSE +0 -21
  27. package/dist/agent/agent.js +0 -19
  28. package/dist/agent/executor.js +0 -25
  29. package/dist/agent/llmPlanner.js +0 -131
  30. package/dist/agent/planner.js +0 -12
  31. package/dist/agent/types.js +0 -2
  32. package/dist/runtime/config.js +0 -73
  33. package/dist/runtime/confirm.js +0 -92
  34. package/dist/runtime/createClusterMemory.js +0 -56
  35. package/dist/runtime/llm.js +0 -64
  36. package/dist/runtime/logger.js +0 -8
  37. package/dist/runtime/memory.js +0 -4
  38. package/dist/services/emrApi.js +0 -181
  39. package/dist/services/volcApi.js +0 -53
  40. package/dist/tools/base.js +0 -2
  41. package/dist/tools/emr/createCluster.js +0 -335
  42. package/dist/tools/emr/deleteCluster.js +0 -15
  43. package/dist/tools/emr/findClustersToCleanup.js +0 -18
  44. package/dist/tools/emr/index.js +0 -15
  45. package/dist/tools/emr/listClusters.js +0 -68
  46. package/dist/tools/registry.js +0 -11
  47. package/dist/utils/prompt.js +0 -9
@@ -1,92 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.confirm = confirm;
7
- exports.createPromptSession = createPromptSession;
8
- const readline_1 = __importDefault(require("readline"));
9
- function createLineSource() {
10
- const stdin = process.stdin;
11
- stdin.setEncoding("utf8");
12
- let buffer = "";
13
- const queue = [];
14
- const waiters = [];
15
- let ended = false;
16
- const flush = () => {
17
- let idx;
18
- while ((idx = buffer.indexOf("\n")) !== -1) {
19
- const line = buffer.slice(0, idx).replace(/\r$/, "");
20
- buffer = buffer.slice(idx + 1);
21
- const w = waiters.shift();
22
- if (w)
23
- w(line);
24
- else
25
- queue.push(line);
26
- }
27
- };
28
- stdin.on("data", (chunk) => {
29
- buffer += chunk;
30
- flush();
31
- });
32
- stdin.on("end", () => {
33
- ended = true;
34
- if (buffer.length) {
35
- const w = waiters.shift();
36
- if (w)
37
- w(buffer);
38
- else
39
- queue.push(buffer);
40
- buffer = "";
41
- }
42
- while (waiters.length) {
43
- const w = waiters.shift();
44
- w && w("");
45
- }
46
- });
47
- return {
48
- nextLine() {
49
- if (queue.length)
50
- return Promise.resolve(queue.shift());
51
- if (ended)
52
- return Promise.resolve("");
53
- return new Promise((resolve) => waiters.push(resolve));
54
- },
55
- stop() {
56
- stdin.pause();
57
- }
58
- };
59
- }
60
- function confirm(question) {
61
- const rl = readline_1.default.createInterface({
62
- input: process.stdin,
63
- output: process.stdout
64
- });
65
- return new Promise((resolve) => {
66
- rl.question(`${question} (y/n): `, (answer) => {
67
- rl.close();
68
- resolve(answer.toLowerCase() === "y");
69
- });
70
- });
71
- }
72
- function createPromptSession() {
73
- const source = createLineSource();
74
- process.stdin.resume();
75
- return {
76
- async ask(question, options = {}) {
77
- const { defaultValue, silent } = options;
78
- const label = defaultValue
79
- ? `${question} [${defaultValue}]: `
80
- : `${question}: `;
81
- process.stdout.write(label);
82
- const line = await source.nextLine();
83
- const value = (line || "").trim();
84
- if (silent)
85
- process.stdout.write("\n");
86
- return value || defaultValue || "";
87
- },
88
- close() {
89
- source.stop();
90
- }
91
- };
92
- }
@@ -1,56 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.readCreateClusterMemory = readCreateClusterMemory;
7
- exports.writeCreateClusterMemory = writeCreateClusterMemory;
8
- exports.clearCreateClusterMemory = clearCreateClusterMemory;
9
- exports.createClusterMemoryPath = createClusterMemoryPath;
10
- const fs_1 = __importDefault(require("fs"));
11
- const os_1 = __importDefault(require("os"));
12
- const path_1 = __importDefault(require("path"));
13
- const MEMORY_DIR = process.env.VOLC_EMR_CONFIG_DIR || path_1.default.join(os_1.default.homedir(), ".volc-emr");
14
- const MEMORY_FILE = path_1.default.join(MEMORY_DIR, "create-cluster-memory.json");
15
- function readCreateClusterMemory() {
16
- try {
17
- if (!fs_1.default.existsSync(MEMORY_FILE))
18
- return {};
19
- const parsed = JSON.parse(fs_1.default.readFileSync(MEMORY_FILE, "utf-8"));
20
- return parsed && typeof parsed === "object" ? parsed : {};
21
- }
22
- catch {
23
- return {};
24
- }
25
- }
26
- function writeCreateClusterMemory(patch) {
27
- fs_1.default.mkdirSync(MEMORY_DIR, { recursive: true, mode: 0o700 });
28
- const prev = readCreateClusterMemory();
29
- const merged = { ...prev };
30
- for (const [k, v] of Object.entries(patch)) {
31
- if (v === undefined || v === null)
32
- continue;
33
- if (typeof v === "string" && !v.trim())
34
- continue;
35
- if (Array.isArray(v) && v.length === 0)
36
- continue;
37
- merged[k] = v;
38
- }
39
- merged.updatedAt = Date.now();
40
- fs_1.default.writeFileSync(MEMORY_FILE, JSON.stringify(merged, null, 2), {
41
- mode: 0o600
42
- });
43
- return merged;
44
- }
45
- function clearCreateClusterMemory() {
46
- try {
47
- if (fs_1.default.existsSync(MEMORY_FILE))
48
- fs_1.default.unlinkSync(MEMORY_FILE);
49
- }
50
- catch {
51
- /* ignore */
52
- }
53
- }
54
- function createClusterMemoryPath() {
55
- return MEMORY_FILE;
56
- }
@@ -1,64 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.chatCompletion = chatCompletion;
7
- const https_1 = __importDefault(require("https"));
8
- const http_1 = __importDefault(require("http"));
9
- const url_1 = require("url");
10
- async function chatCompletion(llm, options) {
11
- if (!llm.endpoint)
12
- throw new Error("LLM endpoint is not configured");
13
- const body = {
14
- model: llm.model || "default",
15
- messages: options.messages,
16
- temperature: options.temperature ?? 0
17
- };
18
- if (options.jsonMode) {
19
- body.response_format = { type: "json_object" };
20
- }
21
- const payload = JSON.stringify(body);
22
- const url = new url_1.URL(llm.endpoint);
23
- const headers = {
24
- "Content-Type": "application/json",
25
- "Content-Length": Buffer.byteLength(payload).toString()
26
- };
27
- if (llm.apiKey)
28
- headers["Authorization"] = `Bearer ${llm.apiKey}`;
29
- const client = url.protocol === "https:" ? https_1.default : http_1.default;
30
- return new Promise((resolve, reject) => {
31
- const req = client.request({
32
- method: "POST",
33
- hostname: url.hostname,
34
- port: url.port || (url.protocol === "https:" ? 443 : 80),
35
- path: `${url.pathname}${url.search}`,
36
- headers
37
- }, (res) => {
38
- const chunks = [];
39
- res.on("data", (c) => chunks.push(c));
40
- res.on("end", () => {
41
- const raw = Buffer.concat(chunks).toString("utf-8");
42
- if ((res.statusCode || 500) >= 400) {
43
- reject(new Error(`LLM HTTP ${res.statusCode}: ${raw}`));
44
- return;
45
- }
46
- try {
47
- const json = JSON.parse(raw);
48
- const content = json?.choices?.[0]?.message?.content;
49
- if (typeof content !== "string") {
50
- reject(new Error(`Unexpected LLM response shape: ${raw.slice(0, 200)}`));
51
- return;
52
- }
53
- resolve(content);
54
- }
55
- catch (e) {
56
- reject(new Error(`Parse LLM response failed: ${e.message}`));
57
- }
58
- });
59
- });
60
- req.on("error", reject);
61
- req.write(payload);
62
- req.end();
63
- });
64
- }
@@ -1,8 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.logStep = logStep;
4
- function logStep(i, msg, data) {
5
- console.log(`Step ${i + 1}: ${msg}`);
6
- if (data)
7
- console.log(" ↳", data);
8
- }
@@ -1,4 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.memory = void 0;
4
- exports.memory = {};
@@ -1,181 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.emrApi = exports.CLUSTER_STATES = exports.EmrClusterState = void 0;
4
- exports.listClusters = listClusters;
5
- exports.listAllClusters = listAllClusters;
6
- exports.releaseCluster = releaseCluster;
7
- exports.createCluster = createCluster;
8
- exports.findClustersToCleanup = findClustersToCleanup;
9
- const volcApi_1 = require("./volcApi");
10
- var EmrClusterState;
11
- (function (EmrClusterState) {
12
- EmrClusterState["PENDING_FOR_PAYMENT"] = "PENDING_FOR_PAYMENT";
13
- EmrClusterState["CREATING"] = "CREATING";
14
- EmrClusterState["RUNNING"] = "RUNNING";
15
- EmrClusterState["WARNING"] = "WARNING";
16
- EmrClusterState["EXCEPTION"] = "EXCEPTION";
17
- EmrClusterState["RESTORING"] = "RESTORING";
18
- EmrClusterState["PAUSING"] = "PAUSING";
19
- EmrClusterState["PAUSED"] = "PAUSED";
20
- EmrClusterState["TERMINATING"] = "TERMINATING";
21
- EmrClusterState["TERMINATED"] = "TERMINATED";
22
- EmrClusterState["TERMINATED_WITH_ERROR"] = "TERMINATED_WITH_ERROR";
23
- EmrClusterState["FAILED"] = "FAILED";
24
- EmrClusterState["SHUTDOWN"] = "SHUTDOWN";
25
- })(EmrClusterState || (exports.EmrClusterState = EmrClusterState = {}));
26
- exports.CLUSTER_STATES = Object.values(EmrClusterState);
27
- function compactBody(input) {
28
- const out = {};
29
- for (const [k, v] of Object.entries(input)) {
30
- if (v === undefined || v === null || v === "")
31
- continue;
32
- if (Array.isArray(v) && v.length === 0)
33
- continue;
34
- out[k] = v;
35
- }
36
- return out;
37
- }
38
- function toMs(v) {
39
- if (v === undefined || v === null || v === "")
40
- return undefined;
41
- const n = typeof v === "number" ? v : Number(v);
42
- if (!Number.isFinite(n))
43
- return undefined;
44
- // 10 位 = 秒级,13 位 = 毫秒级,统一转毫秒
45
- return n < 1e12 ? Math.trunc(n) * 1000 : Math.trunc(n);
46
- }
47
- function toMsString(v) {
48
- const ms = toMs(v);
49
- return ms === undefined ? undefined : String(ms);
50
- }
51
- async function listClusters(params = {}) {
52
- const body = compactBody({
53
- ClusterName: params.ClusterName,
54
- ClusterId: params.ClusterId,
55
- ReleaseVersion: params.ReleaseVersion,
56
- ProjectName: params.ProjectName,
57
- CreateTimeBefore: toMsString(params.CreateTimeBefore),
58
- CreateTimeAfter: toMsString(params.CreateTimeAfter),
59
- ClusterIds: params.ClusterIds,
60
- ClusterTypes: params.ClusterTypes,
61
- ClusterStates: params.ClusterStates,
62
- ChargeTypes: params.ChargeTypes,
63
- Tags: params.Tags,
64
- MaxResults: params.MaxResults,
65
- NextToken: params.NextToken
66
- });
67
- console.log(body);
68
- const result = await (0, volcApi_1.callEmr)("ListClusters", body);
69
- return {
70
- Items: Array.isArray(result?.Items) ? result.Items : [],
71
- TotalCount: typeof result?.TotalCount === "number" ? result.TotalCount : 0,
72
- MaxResults: typeof result?.MaxResults === "number" ? result.MaxResults : 0,
73
- NextToken: result?.NextToken || undefined
74
- };
75
- }
76
- async function listAllClusters(params = {}) {
77
- const { hardLimit, NextToken: initialToken, MaxResults, ...rest } = params;
78
- const pageSize = Math.min(Math.max(MaxResults ?? 50, 1), 100);
79
- const all = [];
80
- let token = initialToken;
81
- let safety = 0;
82
- do {
83
- const page = await listClusters({
84
- ...rest,
85
- MaxResults: pageSize,
86
- NextToken: token
87
- });
88
- all.push(...page.Items);
89
- if (hardLimit && all.length >= hardLimit) {
90
- return all.slice(0, hardLimit);
91
- }
92
- token = page.NextToken;
93
- safety++;
94
- if (safety > 1000) {
95
- throw new Error("[listAllClusters] safety break: more than 1000 pages, check NextToken loop");
96
- }
97
- } while (token);
98
- return all;
99
- }
100
- function matchArchived(msg) {
101
- return /marked\s+archived|could not operate before fixed/i.test(msg);
102
- }
103
- function matchNotFound(msg) {
104
- return /not\s*found|does\s*not\s*exist|不存在/i.test(msg);
105
- }
106
- function matchAlreadyReleased(msg) {
107
- return /already\s+released|已释放|已删除/i.test(msg);
108
- }
109
- async function releaseCluster(params) {
110
- try {
111
- const resp = await (0, volcApi_1.callEmr)("ReleaseCluster", params);
112
- return {
113
- ClusterId: params.ClusterId,
114
- OperationId: resp?.OperationId,
115
- OperateId: resp?.OperateId,
116
- Skipped: false
117
- };
118
- }
119
- catch (e) {
120
- const msg = String(e?.message || e);
121
- if (matchArchived(msg)) {
122
- return {
123
- ClusterId: params.ClusterId,
124
- Skipped: true,
125
- Reason: "ARCHIVED",
126
- Message: "集群已被归档(archived),无法再操作;需要联系火山侧解除归档后再释放。"
127
- };
128
- }
129
- if (matchNotFound(msg)) {
130
- return {
131
- ClusterId: params.ClusterId,
132
- Skipped: true,
133
- Reason: "NOT_FOUND",
134
- Message: "集群不存在(可能已被清理)。"
135
- };
136
- }
137
- if (matchAlreadyReleased(msg)) {
138
- return {
139
- ClusterId: params.ClusterId,
140
- Skipped: true,
141
- Reason: "ALREADY_RELEASED",
142
- Message: "集群已释放,无需重复操作。"
143
- };
144
- }
145
- throw e;
146
- }
147
- }
148
- async function createCluster(params) {
149
- const body = compactBody({ ...params });
150
- console.log("createCluster params:", params);
151
- console.log(body);
152
- const resp = await (0, volcApi_1.callEmr)("CreateCluster", body);
153
- return {
154
- ClusterId: resp?.ClusterId || "",
155
- OperationId: resp?.OperationId
156
- };
157
- }
158
- const DAY = 86400000;
159
- const SHUTDOWN_STATES = [
160
- EmrClusterState.TERMINATED,
161
- EmrClusterState.TERMINATED_WITH_ERROR,
162
- EmrClusterState.FAILED,
163
- EmrClusterState.SHUTDOWN
164
- ];
165
- async function findClustersToCleanup({ olderThanDays = 7, states } = {}) {
166
- const threshold = Date.now() - olderThanDays * DAY;
167
- const targetStates = states && states.length ? states : SHUTDOWN_STATES;
168
- const items = await listAllClusters({
169
- ClusterStates: targetStates,
170
- CreateTimeBefore: threshold
171
- });
172
- console.log(items);
173
- return items;
174
- }
175
- exports.emrApi = {
176
- listClusters,
177
- listAllClusters,
178
- releaseCluster,
179
- findClustersToCleanup,
180
- createCluster
181
- };
@@ -1,53 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getEmrService = getEmrService;
4
- exports.callEmr = callEmr;
5
- const openapi_1 = require("@volcengine/openapi");
6
- const config_1 = require("../runtime/config");
7
- const EMR_API_VERSION = "2023-08-15";
8
- let cachedService = null;
9
- let cachedKey = null;
10
- function buildService(creds) {
11
- const service = new openapi_1.Service({
12
- host: `emr.${creds.region}.volcengineapi.com`,
13
- serviceName: "emr",
14
- region: creds.region,
15
- accessKeyId: creds.accessKey,
16
- secretKey: creds.secretKey,
17
- defaultVersion: EMR_API_VERSION
18
- });
19
- return service;
20
- }
21
- function getEmrService(cli = {}) {
22
- const creds = (0, config_1.resolveCredentials)(cli);
23
- const key = `${creds.region}:${creds.accessKey}`;
24
- if (cachedService && cachedKey === key)
25
- return cachedService;
26
- cachedService = buildService(creds);
27
- cachedKey = key;
28
- return cachedService;
29
- }
30
- async function callEmr(action, body = {}, options = {}) {
31
- const service = getEmrService();
32
- const fetchApi = service.createJSONAPI(action, {
33
- method: "POST",
34
- Version: options.version || EMR_API_VERSION,
35
- contentType: "json"
36
- });
37
- const response = await fetchApi(body);
38
- if (response?.ResponseMetadata?.Error) {
39
- const err = response.ResponseMetadata.Error;
40
- let message = err?.Message || err?.CodeN || err?.Code || "Volcengine EMR error";
41
- if (typeof message === "string" && /\{\d+\}/.test(message)) {
42
- try {
43
- const bodyPreview = JSON.stringify(body);
44
- message = `${message} (body=${bodyPreview})`;
45
- }
46
- catch {
47
- /* ignore */
48
- }
49
- }
50
- throw new Error(`[${action}] ${message}`);
51
- }
52
- return response?.Result;
53
- }
@@ -1,2 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });