@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.
- package/README.md +13 -325
- package/bin/emr-cli +3 -0
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.js +116 -0
- package/dist/client/index.d.ts +17 -0
- package/dist/client/index.js +129 -0
- package/dist/cluster/index.d.ts +13 -0
- package/dist/cluster/index.js +174 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +15 -176
- package/dist/utils/auth.d.ts +7 -0
- package/dist/utils/auth.js +18 -0
- package/dist/utils/client-state.d.ts +12 -0
- package/dist/utils/client-state.js +80 -0
- package/dist/utils/config.d.ts +17 -0
- package/dist/utils/config.js +103 -0
- package/dist/utils/http.d.ts +13 -0
- package/dist/utils/http.js +76 -0
- package/dist/utils/volc/emr.d.ts +30 -0
- package/dist/utils/volc/emr.js +50 -0
- package/dist/utils/volc/openapi.d.ts +19 -0
- package/dist/utils/volc/openapi.js +122 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +6 -0
- package/package.json +13 -40
- package/LICENSE +0 -21
- package/dist/agent/agent.js +0 -19
- package/dist/agent/executor.js +0 -25
- package/dist/agent/llmPlanner.js +0 -131
- package/dist/agent/planner.js +0 -12
- package/dist/agent/types.js +0 -2
- package/dist/runtime/config.js +0 -73
- package/dist/runtime/confirm.js +0 -92
- package/dist/runtime/createClusterMemory.js +0 -56
- package/dist/runtime/llm.js +0 -64
- package/dist/runtime/logger.js +0 -8
- package/dist/runtime/memory.js +0 -4
- package/dist/services/emrApi.js +0 -181
- package/dist/services/volcApi.js +0 -53
- package/dist/tools/base.js +0 -2
- package/dist/tools/emr/createCluster.js +0 -335
- package/dist/tools/emr/deleteCluster.js +0 -15
- package/dist/tools/emr/findClustersToCleanup.js +0 -18
- package/dist/tools/emr/index.js +0 -15
- package/dist/tools/emr/listClusters.js +0 -68
- package/dist/tools/registry.js +0 -11
- package/dist/utils/prompt.js +0 -9
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.httpsJsonRequest = httpsJsonRequest;
|
|
37
|
+
const https = __importStar(require("node:https"));
|
|
38
|
+
const node_url_1 = require("node:url");
|
|
39
|
+
async function httpsJsonRequest(params) {
|
|
40
|
+
const { url, method, headers, body, timeoutMs = 30000 } = params;
|
|
41
|
+
const parsed = new node_url_1.URL(url);
|
|
42
|
+
return new Promise((resolve, reject) => {
|
|
43
|
+
const req = https.request({
|
|
44
|
+
protocol: parsed.protocol,
|
|
45
|
+
hostname: parsed.hostname,
|
|
46
|
+
port: parsed.port ? Number(parsed.port) : undefined,
|
|
47
|
+
path: `${parsed.pathname}${parsed.search}`,
|
|
48
|
+
method,
|
|
49
|
+
headers,
|
|
50
|
+
timeout: timeoutMs,
|
|
51
|
+
}, res => {
|
|
52
|
+
const chunks = [];
|
|
53
|
+
res.on('data', chunk => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
|
|
54
|
+
res.on('end', () => {
|
|
55
|
+
const raw = Buffer.concat(chunks).toString('utf8');
|
|
56
|
+
const statusCode = res.statusCode || 0;
|
|
57
|
+
try {
|
|
58
|
+
const data = raw ? JSON.parse(raw) : {};
|
|
59
|
+
resolve({ statusCode, headers: res.headers, data, raw });
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
reject(new Error(`响应不是合法 JSON(status=${statusCode}):${raw.slice(0, 500)}`));
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
req.on('error', err => reject(err));
|
|
67
|
+
req.on('timeout', () => {
|
|
68
|
+
req.destroy(new Error(`请求超时(>${timeoutMs}ms)`));
|
|
69
|
+
});
|
|
70
|
+
if (body) {
|
|
71
|
+
req.write(body);
|
|
72
|
+
}
|
|
73
|
+
req.end();
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface VolcEmrGetClusterResponse {
|
|
2
|
+
ResponseMetadata?: {
|
|
3
|
+
RequestId?: string;
|
|
4
|
+
Action?: string;
|
|
5
|
+
Version?: string;
|
|
6
|
+
Service?: string;
|
|
7
|
+
Region?: string;
|
|
8
|
+
Error?: {
|
|
9
|
+
Code?: string;
|
|
10
|
+
Message?: string;
|
|
11
|
+
} | null;
|
|
12
|
+
};
|
|
13
|
+
Result?: any;
|
|
14
|
+
}
|
|
15
|
+
export interface VolcOpenApiDebugInfo {
|
|
16
|
+
url: string;
|
|
17
|
+
canonicalRequest: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function volcEmrGetCluster(params: {
|
|
20
|
+
region: string;
|
|
21
|
+
ak: string;
|
|
22
|
+
sk: string;
|
|
23
|
+
clusterId?: string;
|
|
24
|
+
}, options?: {
|
|
25
|
+
debug?: boolean;
|
|
26
|
+
}): Promise<{
|
|
27
|
+
resp: VolcEmrGetClusterResponse;
|
|
28
|
+
debug?: VolcOpenApiDebugInfo;
|
|
29
|
+
}>;
|
|
30
|
+
export declare function formatVolcOpenApiError(resp: VolcEmrGetClusterResponse): string | undefined;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.volcEmrGetCluster = volcEmrGetCluster;
|
|
4
|
+
exports.formatVolcOpenApiError = formatVolcOpenApiError;
|
|
5
|
+
const http_1 = require("../http");
|
|
6
|
+
const openapi_1 = require("./openapi");
|
|
7
|
+
async function volcEmrGetCluster(params, options) {
|
|
8
|
+
const host = `emr.${params.region}.volcengineapi.com`;
|
|
9
|
+
const query = {
|
|
10
|
+
Version: '2023-08-15',
|
|
11
|
+
Action: 'GetCluster',
|
|
12
|
+
};
|
|
13
|
+
const bodyObj = {};
|
|
14
|
+
if (params.clusterId) {
|
|
15
|
+
bodyObj.ClusterId = params.clusterId;
|
|
16
|
+
}
|
|
17
|
+
const body = JSON.stringify(bodyObj);
|
|
18
|
+
const signed = (0, openapi_1.signVolcOpenApiRequest)({
|
|
19
|
+
method: 'POST',
|
|
20
|
+
host,
|
|
21
|
+
path: '/',
|
|
22
|
+
region: params.region,
|
|
23
|
+
service: 'emr',
|
|
24
|
+
ak: params.ak,
|
|
25
|
+
sk: params.sk,
|
|
26
|
+
query,
|
|
27
|
+
body,
|
|
28
|
+
});
|
|
29
|
+
const resp = (await (0, http_1.httpsJsonRequest)({
|
|
30
|
+
url: signed.url,
|
|
31
|
+
method: 'POST',
|
|
32
|
+
headers: signed.headers,
|
|
33
|
+
body,
|
|
34
|
+
})).data;
|
|
35
|
+
return {
|
|
36
|
+
resp,
|
|
37
|
+
debug: options?.debug
|
|
38
|
+
? { url: signed.url, canonicalRequest: signed.canonicalRequest }
|
|
39
|
+
: undefined,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function formatVolcOpenApiError(resp) {
|
|
43
|
+
const err = resp?.ResponseMetadata?.Error;
|
|
44
|
+
if (!err || (!err.Code && !err.Message))
|
|
45
|
+
return undefined;
|
|
46
|
+
const code = err.Code || 'UnknownError';
|
|
47
|
+
const msg = err.Message || '';
|
|
48
|
+
return `${code}${msg ? `: ${msg}` : ''}`;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=emr.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface VolcOpenApiSignInput {
|
|
2
|
+
method: 'POST' | 'GET';
|
|
3
|
+
host: string;
|
|
4
|
+
path: string;
|
|
5
|
+
region: string;
|
|
6
|
+
service: string;
|
|
7
|
+
ak: string;
|
|
8
|
+
sk: string;
|
|
9
|
+
query: Record<string, string>;
|
|
10
|
+
body?: string;
|
|
11
|
+
date?: Date;
|
|
12
|
+
}
|
|
13
|
+
export declare function signVolcOpenApiRequest(input: VolcOpenApiSignInput): {
|
|
14
|
+
url: string;
|
|
15
|
+
headers: Record<string, string>;
|
|
16
|
+
xDate: string;
|
|
17
|
+
xContentSha256: string;
|
|
18
|
+
canonicalRequest: string;
|
|
19
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.signVolcOpenApiRequest = signVolcOpenApiRequest;
|
|
37
|
+
const crypto = __importStar(require("node:crypto"));
|
|
38
|
+
const SAFE_BYTES = (() => {
|
|
39
|
+
const s = new Set();
|
|
40
|
+
for (let i = 0x61; i <= 0x7a; i++)
|
|
41
|
+
s.add(i); // a-z
|
|
42
|
+
for (let i = 0x41; i <= 0x5a; i++)
|
|
43
|
+
s.add(i); // A-Z
|
|
44
|
+
for (let i = 0x30; i <= 0x39; i++)
|
|
45
|
+
s.add(i); // 0-9
|
|
46
|
+
for (const c of ['-', '_', '.', '~'])
|
|
47
|
+
s.add(c.charCodeAt(0));
|
|
48
|
+
return s;
|
|
49
|
+
})();
|
|
50
|
+
function percentEncode(input) {
|
|
51
|
+
const buf = Buffer.from(input, 'utf8');
|
|
52
|
+
let out = '';
|
|
53
|
+
for (const b of buf) {
|
|
54
|
+
if (SAFE_BYTES.has(b)) {
|
|
55
|
+
out += String.fromCharCode(b);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
out += `%${b.toString(16).toUpperCase().padStart(2, '0')}`;
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
function sha256Hex(data) {
|
|
63
|
+
return crypto.createHash('sha256').update(data).digest('hex');
|
|
64
|
+
}
|
|
65
|
+
function hmacSha256(key, data) {
|
|
66
|
+
return crypto.createHmac('sha256', key).update(data, 'utf8').digest();
|
|
67
|
+
}
|
|
68
|
+
function formatXDate(date) {
|
|
69
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
70
|
+
const y = date.getUTCFullYear();
|
|
71
|
+
const m = pad(date.getUTCMonth() + 1);
|
|
72
|
+
const d = pad(date.getUTCDate());
|
|
73
|
+
const hh = pad(date.getUTCHours());
|
|
74
|
+
const mm = pad(date.getUTCMinutes());
|
|
75
|
+
const ss = pad(date.getUTCSeconds());
|
|
76
|
+
const shortDate = `${y}${m}${d}`;
|
|
77
|
+
return { xDate: `${shortDate}T${hh}${mm}${ss}Z`, shortDate };
|
|
78
|
+
}
|
|
79
|
+
function signVolcOpenApiRequest(input) {
|
|
80
|
+
const { method, host, path, region, service, ak, sk, query, body = '', date = new Date(), } = input;
|
|
81
|
+
const contentType = 'application/json; charset=utf-8';
|
|
82
|
+
const { xDate, shortDate } = formatXDate(date);
|
|
83
|
+
const xContentSha256 = sha256Hex(body ? Buffer.from(body, 'utf8') : Buffer.alloc(0));
|
|
84
|
+
const signedHeaders = 'content-type;host;x-content-sha256;x-date';
|
|
85
|
+
const sortedQueryKeys = Object.keys(query).sort();
|
|
86
|
+
const canonicalQuery = sortedQueryKeys
|
|
87
|
+
.map(k => `${percentEncode(k)}=${percentEncode(query[k] ?? '')}`)
|
|
88
|
+
.join('&');
|
|
89
|
+
const canonicalHeaders = `content-type:${contentType}\nhost:${host}\nx-content-sha256:${xContentSha256}\nx-date:${xDate}\n`;
|
|
90
|
+
const canonicalRequest = `${method}\n${path}\n${canonicalQuery}\n${canonicalHeaders}\n${signedHeaders}\n${xContentSha256}`;
|
|
91
|
+
const hashedCanonicalRequest = sha256Hex(canonicalRequest);
|
|
92
|
+
const credentialScope = `${shortDate}/${region}/${service}/request`;
|
|
93
|
+
const stringToSign = `HMAC-SHA256\n${xDate}\n${credentialScope}\n${hashedCanonicalRequest}`;
|
|
94
|
+
const kDate = hmacSha256(Buffer.from(sk, 'utf8'), shortDate);
|
|
95
|
+
const kRegion = hmacSha256(kDate, region);
|
|
96
|
+
const kService = hmacSha256(kRegion, service);
|
|
97
|
+
const signingKey = hmacSha256(kService, 'request');
|
|
98
|
+
const signature = crypto
|
|
99
|
+
.createHmac('sha256', signingKey)
|
|
100
|
+
.update(stringToSign, 'utf8')
|
|
101
|
+
.digest('hex');
|
|
102
|
+
const authorization = `HMAC-SHA256 Credential=${ak}/${credentialScope}, ` +
|
|
103
|
+
`SignedHeaders=${signedHeaders}, ` +
|
|
104
|
+
`Signature=${signature}`;
|
|
105
|
+
const url = `https://${host}${path}?${canonicalQuery}`;
|
|
106
|
+
return {
|
|
107
|
+
url,
|
|
108
|
+
xDate,
|
|
109
|
+
xContentSha256,
|
|
110
|
+
canonicalRequest,
|
|
111
|
+
headers: {
|
|
112
|
+
Authorization: authorization,
|
|
113
|
+
'X-Date': xDate,
|
|
114
|
+
'X-Content-Sha256': xContentSha256,
|
|
115
|
+
Host: host,
|
|
116
|
+
'Content-Type': contentType,
|
|
117
|
+
Accept: 'application/json',
|
|
118
|
+
'User-Agent': 'emr-cli',
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=openapi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const VERSION = "0.1.1";
|
package/dist/version.js
ADDED
package/package.json
CHANGED
|
@@ -1,52 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volc-emr/emr-cli",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
5
|
-
"keywords": [
|
|
6
|
-
"volcengine",
|
|
7
|
-
"emr",
|
|
8
|
-
"cli",
|
|
9
|
-
"agent",
|
|
10
|
-
"openapi",
|
|
11
|
-
"llm",
|
|
12
|
-
"tool-first",
|
|
13
|
-
"plan-execute"
|
|
14
|
-
],
|
|
15
|
-
"license": "MIT",
|
|
16
|
-
"bin": {
|
|
17
|
-
"volc-emr-agent": "dist/index.js"
|
|
18
|
-
},
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "EMR Command Line Interface - 提交机认证、客户端部署、集群信息查询与集群配置同步工具",
|
|
19
5
|
"main": "dist/index.js",
|
|
20
6
|
"types": "dist/index.d.ts",
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"README.md",
|
|
24
|
-
"LICENSE"
|
|
25
|
-
],
|
|
26
|
-
"scripts": {
|
|
27
|
-
"dev": "ts-node src/index.ts",
|
|
28
|
-
"build": "tsc -p tsconfig.json && chmod +x dist/index.js",
|
|
29
|
-
"start": "node dist/index.js",
|
|
30
|
-
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
31
|
-
"prepublishOnly": "npm run typecheck && npm run build"
|
|
32
|
-
},
|
|
33
|
-
"engines": {
|
|
34
|
-
"node": ">=18"
|
|
7
|
+
"bin": {
|
|
8
|
+
"emr-cli": "bin/emr-cli"
|
|
35
9
|
},
|
|
36
10
|
"publishConfig": {
|
|
37
11
|
"access": "public",
|
|
38
|
-
"
|
|
12
|
+
"registry": "https://registry.npmjs.org/"
|
|
39
13
|
},
|
|
14
|
+
"files": ["dist/**/*.js", "dist/**/*.d.ts", "bin/emr-cli", "README.md"],
|
|
15
|
+
"keywords": ["emr", "cli", "volcengine"],
|
|
16
|
+
"license": "MIT",
|
|
40
17
|
"dependencies": {
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"zod": "^3.23.8",
|
|
45
|
-
"zod-to-json-schema": "^3.25.2"
|
|
18
|
+
"commander": "^11.1.0",
|
|
19
|
+
"inquirer": "^8.2.6",
|
|
20
|
+
"ora": "^5.4.1"
|
|
46
21
|
},
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"ts-node": "^10.9.2",
|
|
50
|
-
"typescript": "^5.4.5"
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=16.0.0"
|
|
51
24
|
}
|
|
52
25
|
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 volc-emr-agent contributors
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
package/dist/agent/agent.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Agent = void 0;
|
|
4
|
-
const planner_1 = require("./planner");
|
|
5
|
-
const executor_1 = require("./executor");
|
|
6
|
-
class Agent {
|
|
7
|
-
constructor(ctx) {
|
|
8
|
-
this.ctx = ctx;
|
|
9
|
-
}
|
|
10
|
-
async run(task, options = {}) {
|
|
11
|
-
const plan = await (0, planner_1.planTask)(task);
|
|
12
|
-
console.log("\n📋 Plan:");
|
|
13
|
-
console.log(plan);
|
|
14
|
-
if (options.dryRun)
|
|
15
|
-
return;
|
|
16
|
-
await (0, executor_1.executePlan)(plan, this.ctx, { autoApprove: options.autoApprove });
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
exports.Agent = Agent;
|
package/dist/agent/executor.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.executePlan = executePlan;
|
|
4
|
-
const registry_1 = require("../tools/registry");
|
|
5
|
-
const confirm_1 = require("../runtime/confirm");
|
|
6
|
-
const logger_1 = require("../runtime/logger");
|
|
7
|
-
async function executePlan(plan, ctx, options = {}) {
|
|
8
|
-
for (const [i, step] of plan.entries()) {
|
|
9
|
-
const tool = registry_1.toolList.find((t) => t.name === step.tool);
|
|
10
|
-
if (!tool)
|
|
11
|
-
throw new Error(`Tool not found: ${step.tool}`);
|
|
12
|
-
if (tool.riskLevel === "high") {
|
|
13
|
-
const ok = options.autoApprove
|
|
14
|
-
? true
|
|
15
|
-
: await (0, confirm_1.confirm)(`⚠️ 执行 ${tool.name} (input=${JSON.stringify(step.input)})?`);
|
|
16
|
-
if (!ok) {
|
|
17
|
-
(0, logger_1.logStep)(i, `Skipped ${tool.name}`);
|
|
18
|
-
continue;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
(0, logger_1.logStep)(i, `Running ${tool.name}`, step.input);
|
|
22
|
-
const result = await tool.execute(step.input, ctx);
|
|
23
|
-
(0, logger_1.logStep)(i, `Done ${tool.name}`, result);
|
|
24
|
-
}
|
|
25
|
-
}
|
package/dist/agent/llmPlanner.js
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.llmPlanTask = llmPlanTask;
|
|
4
|
-
const zod_to_json_schema_1 = require("zod-to-json-schema");
|
|
5
|
-
const registry_1 = require("../tools/registry");
|
|
6
|
-
const emr_1 = require("../tools/emr");
|
|
7
|
-
const llm_1 = require("../runtime/llm");
|
|
8
|
-
function buildToolCatalog() {
|
|
9
|
-
return registry_1.toolList.map((t) => ({
|
|
10
|
-
name: t.name,
|
|
11
|
-
description: t.description,
|
|
12
|
-
riskLevel: t.riskLevel || "low",
|
|
13
|
-
inputSchema: (0, zod_to_json_schema_1.zodToJsonSchema)(t.input, t.name)
|
|
14
|
-
}));
|
|
15
|
-
}
|
|
16
|
-
function buildSystemPrompt() {
|
|
17
|
-
const catalog = buildToolCatalog();
|
|
18
|
-
return [
|
|
19
|
-
"You are a CLI Agent Planner for Volcengine EMR.",
|
|
20
|
-
"You MUST NOT execute anything directly.",
|
|
21
|
-
"Your only job is to output an ordered plan of tool calls.",
|
|
22
|
-
"",
|
|
23
|
-
"Available tools (JSON catalog):",
|
|
24
|
-
JSON.stringify(catalog, null, 2),
|
|
25
|
-
"",
|
|
26
|
-
"Response format:",
|
|
27
|
-
'You MUST output ONLY a JSON object with shape {"steps":[{"tool":"<name>","input":{...}}]}.',
|
|
28
|
-
"Do NOT include any prose, markdown fences, or explanations.",
|
|
29
|
-
"The very first character of your reply MUST be `{` and the last MUST be `}`.",
|
|
30
|
-
"Rules:",
|
|
31
|
-
"- Only use tools listed above.",
|
|
32
|
-
"- `input` MUST conform to each tool's inputSchema.",
|
|
33
|
-
"- For destructive actions (riskLevel=high), only plan them if the user task clearly asks for it.",
|
|
34
|
-
"- If the task is ambiguous, return at most one safe, read-only step (e.g. listClusters)."
|
|
35
|
-
].join("\n");
|
|
36
|
-
}
|
|
37
|
-
function buildUserPrompt(task, context) {
|
|
38
|
-
const ctx = context ? `\n\nContext:\n${JSON.stringify(context, null, 2)}` : "";
|
|
39
|
-
return `Task: ${task}${ctx}`;
|
|
40
|
-
}
|
|
41
|
-
function extractJsonObject(raw) {
|
|
42
|
-
const stripped = raw
|
|
43
|
-
.trim()
|
|
44
|
-
.replace(/^```(?:json)?\s*/i, "")
|
|
45
|
-
.replace(/```\s*$/i, "")
|
|
46
|
-
.trim();
|
|
47
|
-
if (stripped.startsWith("{"))
|
|
48
|
-
return stripped;
|
|
49
|
-
const start = stripped.indexOf("{");
|
|
50
|
-
if (start === -1)
|
|
51
|
-
return stripped;
|
|
52
|
-
let depth = 0;
|
|
53
|
-
let inString = false;
|
|
54
|
-
let escape = false;
|
|
55
|
-
for (let i = start; i < stripped.length; i++) {
|
|
56
|
-
const ch = stripped[i];
|
|
57
|
-
if (inString) {
|
|
58
|
-
if (escape) {
|
|
59
|
-
escape = false;
|
|
60
|
-
}
|
|
61
|
-
else if (ch === "\\") {
|
|
62
|
-
escape = true;
|
|
63
|
-
}
|
|
64
|
-
else if (ch === '"') {
|
|
65
|
-
inString = false;
|
|
66
|
-
}
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
if (ch === '"') {
|
|
70
|
-
inString = true;
|
|
71
|
-
}
|
|
72
|
-
else if (ch === "{") {
|
|
73
|
-
depth++;
|
|
74
|
-
}
|
|
75
|
-
else if (ch === "}") {
|
|
76
|
-
depth--;
|
|
77
|
-
if (depth === 0)
|
|
78
|
-
return stripped.slice(start, i + 1);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
return stripped.slice(start);
|
|
82
|
-
}
|
|
83
|
-
function tryParsePlan(raw) {
|
|
84
|
-
const jsonText = extractJsonObject(raw);
|
|
85
|
-
const parsed = JSON.parse(jsonText);
|
|
86
|
-
const steps = Array.isArray(parsed) ? parsed : parsed?.steps;
|
|
87
|
-
if (!Array.isArray(steps)) {
|
|
88
|
-
throw new Error("LLM plan has no `steps` array");
|
|
89
|
-
}
|
|
90
|
-
return steps.map((s, i) => {
|
|
91
|
-
if (!s || typeof s.tool !== "string") {
|
|
92
|
-
throw new Error(`Step ${i} is missing "tool"`);
|
|
93
|
-
}
|
|
94
|
-
return { tool: s.tool, input: s.input ?? {} };
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
function validatePlanAgainstTools(steps) {
|
|
98
|
-
return steps.map((step) => {
|
|
99
|
-
const tool = registry_1.toolList.find((t) => t.name === step.tool);
|
|
100
|
-
if (!tool)
|
|
101
|
-
throw new Error(`LLM chose unknown tool: ${step.tool}`);
|
|
102
|
-
const result = tool.input.safeParse(step.input ?? {});
|
|
103
|
-
if (!result.success) {
|
|
104
|
-
throw new Error(`LLM produced invalid input for ${step.tool}: ${result.error.message}`);
|
|
105
|
-
}
|
|
106
|
-
return { tool: step.tool, input: result.data };
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
async function llmPlanTask(task, llm) {
|
|
110
|
-
const messages = [
|
|
111
|
-
{ role: "system", content: buildSystemPrompt() },
|
|
112
|
-
{ role: "user", content: buildUserPrompt(task) }
|
|
113
|
-
];
|
|
114
|
-
const content = await (0, llm_1.chatCompletion)(llm, { messages });
|
|
115
|
-
const steps = tryParsePlan(content);
|
|
116
|
-
const validated = validatePlanAgainstTools(steps);
|
|
117
|
-
const expanded = [];
|
|
118
|
-
for (const step of validated) {
|
|
119
|
-
expanded.push(step);
|
|
120
|
-
if (step.tool === "findClustersToCleanup") {
|
|
121
|
-
const found = await emr_1.emrApi.findClustersToCleanup(step.input || {});
|
|
122
|
-
for (const c of found) {
|
|
123
|
-
expanded.push({
|
|
124
|
-
tool: "deleteCluster",
|
|
125
|
-
input: { ClusterId: c.ClusterId }
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
return expanded;
|
|
131
|
-
}
|
package/dist/agent/planner.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.planTask = planTask;
|
|
4
|
-
const config_1 = require("../runtime/config");
|
|
5
|
-
const llmPlanner_1 = require("./llmPlanner");
|
|
6
|
-
async function planTask(task, _options = {}) {
|
|
7
|
-
const llm = (0, config_1.resolveLlmConfig)();
|
|
8
|
-
if (!llm) {
|
|
9
|
-
throw new Error("LLM is not configured. Run `volc-emr-agent config set-llm` or set VOLC_LLM_ENDPOINT first.");
|
|
10
|
-
}
|
|
11
|
-
return (0, llmPlanner_1.llmPlanTask)(task, llm);
|
|
12
|
-
}
|
package/dist/agent/types.js
DELETED
package/dist/runtime/config.js
DELETED
|
@@ -1,73 +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.readLocalConfig = readLocalConfig;
|
|
7
|
-
exports.writeLocalConfig = writeLocalConfig;
|
|
8
|
-
exports.clearLlmConfig = clearLlmConfig;
|
|
9
|
-
exports.resolveCredentials = resolveCredentials;
|
|
10
|
-
exports.resolveLlmConfig = resolveLlmConfig;
|
|
11
|
-
exports.configFilePath = configFilePath;
|
|
12
|
-
const fs_1 = __importDefault(require("fs"));
|
|
13
|
-
const os_1 = __importDefault(require("os"));
|
|
14
|
-
const path_1 = __importDefault(require("path"));
|
|
15
|
-
const CONFIG_DIR = process.env.VOLC_EMR_CONFIG_DIR || path_1.default.join(os_1.default.homedir(), ".volc-emr");
|
|
16
|
-
const CONFIG_FILE = path_1.default.join(CONFIG_DIR, "config.json");
|
|
17
|
-
function readLocalConfig() {
|
|
18
|
-
try {
|
|
19
|
-
if (!fs_1.default.existsSync(CONFIG_FILE))
|
|
20
|
-
return {};
|
|
21
|
-
return JSON.parse(fs_1.default.readFileSync(CONFIG_FILE, "utf-8"));
|
|
22
|
-
}
|
|
23
|
-
catch {
|
|
24
|
-
return {};
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
function writeLocalConfig(cfg) {
|
|
28
|
-
fs_1.default.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
29
|
-
const prev = readLocalConfig();
|
|
30
|
-
let mergedLlm;
|
|
31
|
-
if (cfg.llm === null) {
|
|
32
|
-
mergedLlm = undefined;
|
|
33
|
-
}
|
|
34
|
-
else if (cfg.llm || prev.llm) {
|
|
35
|
-
mergedLlm = { ...(prev.llm || {}), ...(cfg.llm || {}) };
|
|
36
|
-
}
|
|
37
|
-
const merged = { ...prev, ...cfg };
|
|
38
|
-
if (mergedLlm && Object.keys(mergedLlm).length > 0) {
|
|
39
|
-
merged.llm = mergedLlm;
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
delete merged.llm;
|
|
43
|
-
}
|
|
44
|
-
fs_1.default.writeFileSync(CONFIG_FILE, JSON.stringify(merged, null, 2), {
|
|
45
|
-
mode: 0o600
|
|
46
|
-
});
|
|
47
|
-
return CONFIG_FILE;
|
|
48
|
-
}
|
|
49
|
-
function clearLlmConfig() {
|
|
50
|
-
return writeLocalConfig({ llm: null });
|
|
51
|
-
}
|
|
52
|
-
function resolveCredentials(cli = {}) {
|
|
53
|
-
const local = readLocalConfig();
|
|
54
|
-
const accessKey = cli.accessKey || process.env.VOLC_ACCESSKEY || local.accessKey;
|
|
55
|
-
const secretKey = cli.secretKey || process.env.VOLC_SECRETKEY || local.secretKey;
|
|
56
|
-
const region = cli.region || process.env.VOLC_REGION || local.region || "cn-beijing";
|
|
57
|
-
if (!accessKey || !secretKey) {
|
|
58
|
-
throw new Error("未找到 Volcengine 凭证。请通过 CLI 参数、环境变量(VOLC_ACCESSKEY/VOLC_SECRETKEY)或本地配置(~/.volc-emr/config.json)提供。");
|
|
59
|
-
}
|
|
60
|
-
return { accessKey, secretKey, region };
|
|
61
|
-
}
|
|
62
|
-
function resolveLlmConfig(cli = {}) {
|
|
63
|
-
const local = readLocalConfig().llm || {};
|
|
64
|
-
const endpoint = cli.endpoint || process.env.VOLC_LLM_ENDPOINT || local.endpoint;
|
|
65
|
-
const apiKey = cli.apiKey || process.env.VOLC_LLM_API_KEY || local.apiKey;
|
|
66
|
-
const model = cli.model || process.env.VOLC_LLM_MODEL || local.model;
|
|
67
|
-
if (!endpoint)
|
|
68
|
-
return null;
|
|
69
|
-
return { endpoint, apiKey, model };
|
|
70
|
-
}
|
|
71
|
-
function configFilePath() {
|
|
72
|
-
return CONFIG_FILE;
|
|
73
|
-
}
|