@thinkingai/ae-cli 1.0.27 → 1.0.28
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/dist/auth-5NSDQFQB.js +454 -0
- package/dist/{auth-TXFJHPXU.js → auth-H2376DEF.js} +2 -3
- package/dist/chunk-AD5V3ZPJ.js +217 -0
- package/dist/chunk-CAQYQA4R.js +399 -0
- package/dist/{chunk-B4UL2VIJ.js → chunk-CLJF7MQA.js} +1 -1
- package/dist/chunk-GSJGPNKK.js +49 -0
- package/dist/{chunk-6T7245YE.js → chunk-IMBKVXKY.js} +8 -8
- package/dist/chunk-KAEZTSXN.js +99 -0
- package/dist/{chunk-VT6VC4PA.js → chunk-LU7SXK4Q.js} +24 -11
- package/dist/chunk-OJDNO5QY.js +63 -0
- package/dist/chunk-SRJIAOBN.js +244 -0
- package/dist/{chunk-2NGWYMLB.js → chunk-WCHI7725.js} +45 -50
- package/dist/{client-IU2E5IOL.js → client-FCMK3XEK.js} +3 -4
- package/dist/config-64G36TJL.js +130 -0
- package/dist/index.js +28 -23
- package/dist/{model-CC7U5XOJ.js → model-EZ6K7FXI.js} +15 -12
- package/dist/{raw-TUHZQ4UB.js → raw-VEF3D6UD.js} +11 -7
- package/dist/{sync-EVEDS7YE.js → sync-NRODEVNH.js} +36 -34
- package/dist/{te-agent-FLTDYV3L.js → te-agent-7U7JLJKU.js} +25 -23
- package/dist/{te-analysis-CMXADIJM.js → te-analysis-A2OSGHQ4.js} +59 -13
- package/dist/{te-audience-LPZFFSXM.js → te-audience-LI67PPGO.js} +5 -5
- package/dist/{te-common-BXQFKTB3.js → te-common-35WVU3C5.js} +19 -9
- package/dist/{te-community-K5S3VOZY.js → te-community-LEAP6PWT.js} +4 -4
- package/dist/{te-dataops-567IMCQG.js → te-dataops-N2LGZBOX.js} +4 -4
- package/dist/{te-engage-TBUTGW5F.js → te-engage-H7C72Z46.js} +4 -4
- package/dist/{te-kb-XHVBWP5E.js → te-kb-I2OKTBAI.js} +5 -5
- package/dist/{te-meta-NQ4U3VJW.js → te-meta-HBTIEDRO.js} +4 -4
- package/dist/{te-team-556APRIU.js → te-team-EM5OWRQL.js} +7 -7
- package/package.json +6 -2
- package/skills/ae-agent/SKILL.md +1 -0
- package/skills/ae-analysis/SKILL.md +1 -1
- package/skills/ae-analysis/references/list_clusters.md +4 -2
- package/skills/ae-analysis-global/SKILL.md +51 -0
- package/skills/ae-analysis-global/references/list_query_clusters.md +67 -0
- package/skills/ae-community/SKILL.md +2 -3
- package/skills/ae-dataops/SKILL.md +1 -2
- package/skills/ae-engage/SKILL.md +1 -0
- package/skills/ae-engage/references/add-approver.md +0 -1
- package/skills/ae-engage/references/add-channel.md +0 -1
- package/skills/ae-engage/references/approver-list.md +0 -1
- package/skills/ae-engage/references/cancel-query-by-request-id.md +0 -1
- package/skills/ae-engage/references/channel-detail.md +0 -1
- package/skills/ae-engage/references/channel-list.md +0 -1
- package/skills/ae-engage/references/config-channel-detail.md +0 -1
- package/skills/ae-engage/references/config-channel-list.md +0 -1
- package/skills/ae-engage/references/config-item-analysis-report.md +0 -1
- package/skills/ae-engage/references/config-item-detail.md +0 -1
- package/skills/ae-engage/references/config-item-list.md +0 -1
- package/skills/ae-engage/references/config-item-strategy-comparison.md +0 -1
- package/skills/ae-engage/references/config-item-trigger-report.md +0 -1
- package/skills/ae-engage/references/copy-config-template.md +0 -1
- package/skills/ae-engage/references/delete-channel.md +0 -1
- package/skills/ae-engage/references/delete-config-channel.md +0 -1
- package/skills/ae-engage/references/delete-config-item.md +0 -1
- package/skills/ae-engage/references/delete-flow.md +0 -1
- package/skills/ae-engage/references/flow-ab-split-node-report.md +0 -1
- package/skills/ae-engage/references/flow-detail.md +0 -1
- package/skills/ae-engage/references/flow-list.md +0 -1
- package/skills/ae-engage/references/flow-node-config-schema.md +0 -1
- package/skills/ae-engage/references/flow-node-detail-report.md +0 -1
- package/skills/ae-engage/references/flow-node-overview-report.md +0 -1
- package/skills/ae-engage/references/flow-process-report.md +0 -1
- package/skills/ae-engage/references/manage-flow.md +0 -1
- package/skills/ae-engage/references/manage-strategy.md +0 -1
- package/skills/ae-engage/references/manage-task.md +0 -1
- package/skills/ae-engage/references/modify-flow-base-info.md +0 -1
- package/skills/ae-engage/references/strategy-detail.md +0 -1
- package/skills/ae-engage/references/strategy-list.md +0 -1
- package/skills/ae-engage/references/task-data-detail.md +0 -1
- package/skills/ae-engage/references/task-data-overview.md +0 -1
- package/skills/ae-engage/references/task-detail.md +0 -1
- package/skills/ae-engage/references/task-experiment-report.md +0 -1
- package/skills/ae-engage/references/task-list.md +0 -1
- package/skills/ae-engage/references/task-metric-detail.md +0 -1
- package/skills/ae-engage/references/task-stats.md +0 -1
- package/skills/ae-engage/references/update-channel-status.md +0 -1
- package/skills/ae-engage/references/update-config-channel-status.md +0 -1
- package/skills/ae-engage/references/validate-flow-node-config.md +0 -1
- package/skills/ae-engage/references/whitelist-list.md +0 -1
- package/skills/ae-kb/SKILL.md +1 -0
- package/skills/ae-team/SKILL.md +1 -1
- package/dist/auth-W2JZ3DKA.js +0 -170
- package/dist/chunk-BI5ZVD6T.js +0 -186
- package/dist/chunk-MNHE6SSI.js +0 -79
- package/dist/chunk-MWWYGZ76.js +0 -130
- package/dist/chunk-SRRUNQIM.js +0 -312
- package/dist/chunk-TG64AQV4.js +0 -262
- package/dist/config-WH3R3IRY.js +0 -258
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getMcpToken
|
|
3
|
+
} from "./chunk-WCHI7725.js";
|
|
4
|
+
import {
|
|
5
|
+
safeJsonParse
|
|
6
|
+
} from "./chunk-SRJIAOBN.js";
|
|
7
|
+
|
|
8
|
+
// src/core/mcp-access.ts
|
|
9
|
+
function buildUrl(host, path, params = {}) {
|
|
10
|
+
const base = host.replace(/\/$/, "");
|
|
11
|
+
const p = path.startsWith("/") ? path : `/${path}`;
|
|
12
|
+
const url = new URL(`${base}${p}`);
|
|
13
|
+
for (const [key, value] of Object.entries(params)) {
|
|
14
|
+
if (value !== void 0 && value !== null) {
|
|
15
|
+
url.searchParams.set(key, String(value));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return url.toString();
|
|
19
|
+
}
|
|
20
|
+
function parseKbResponse(resp, text) {
|
|
21
|
+
if (resp.status === 401 || resp.status === 403) {
|
|
22
|
+
throw new Error(`KB MCP token auth failed: HTTP ${resp.status} ${resp.statusText}`);
|
|
23
|
+
}
|
|
24
|
+
const data = safeJsonParse(text);
|
|
25
|
+
if (data.return_code === -1001) {
|
|
26
|
+
throw new Error(`KB MCP token auth failed: ${data.return_message || "unauthorized"} (code: ${data.return_code})`);
|
|
27
|
+
}
|
|
28
|
+
if (data.return_code !== 0 && data.return_code !== void 0) {
|
|
29
|
+
throw new Error(`AE API error: ${data.return_message || "unknown"} (code: ${data.return_code})`);
|
|
30
|
+
}
|
|
31
|
+
return data.data !== void 0 ? data.data : data;
|
|
32
|
+
}
|
|
33
|
+
async function fetchWithMcpToken(input, init, mcpToken) {
|
|
34
|
+
const headers = new Headers(init.headers);
|
|
35
|
+
headers.set("mcp-token", mcpToken);
|
|
36
|
+
const resp = await fetch(input, { ...init, headers });
|
|
37
|
+
return parseKbResponse(resp, await resp.text());
|
|
38
|
+
}
|
|
39
|
+
async function kbApi(ctx, method, path, params = {}, body) {
|
|
40
|
+
const host = ctx.host();
|
|
41
|
+
const mcpToken = await getMcpToken(host);
|
|
42
|
+
const upperMethod = method.toUpperCase();
|
|
43
|
+
return fetchWithMcpToken(buildUrl(host, path, params), {
|
|
44
|
+
method: upperMethod,
|
|
45
|
+
headers: {
|
|
46
|
+
"Content-Type": "application/json"
|
|
47
|
+
},
|
|
48
|
+
body: upperMethod === "GET" ? void 0 : JSON.stringify(body ?? {})
|
|
49
|
+
}, mcpToken);
|
|
50
|
+
}
|
|
51
|
+
async function kbUpload(ctx, path, form, params = {}) {
|
|
52
|
+
const host = ctx.host();
|
|
53
|
+
const mcpToken = await getMcpToken(host);
|
|
54
|
+
return fetchWithMcpToken(buildUrl(host, path, params), {
|
|
55
|
+
method: "POST",
|
|
56
|
+
body: form
|
|
57
|
+
}, mcpToken);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export {
|
|
61
|
+
kbApi,
|
|
62
|
+
kbUpload
|
|
63
|
+
};
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
// src/core/logger.ts
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
var LOG_DIR = path.join(process.env.HOME || "", ".ae-cli", "log");
|
|
5
|
+
var Logger = class {
|
|
6
|
+
logDir;
|
|
7
|
+
date;
|
|
8
|
+
constructor() {
|
|
9
|
+
this.logDir = LOG_DIR;
|
|
10
|
+
this.date = this.today();
|
|
11
|
+
this.ensureDir();
|
|
12
|
+
}
|
|
13
|
+
today() {
|
|
14
|
+
const d = /* @__PURE__ */ new Date();
|
|
15
|
+
return `${d.getFullYear()}${String(d.getMonth() + 1).padStart(2, "0")}${String(d.getDate()).padStart(2, "0")}`;
|
|
16
|
+
}
|
|
17
|
+
timestamp() {
|
|
18
|
+
return (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").split(".")[0];
|
|
19
|
+
}
|
|
20
|
+
ensureDir() {
|
|
21
|
+
if (!fs.existsSync(this.logDir)) {
|
|
22
|
+
fs.mkdirSync(this.logDir, { recursive: true });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
filePath(level) {
|
|
26
|
+
const today = this.today();
|
|
27
|
+
if (today !== this.date) {
|
|
28
|
+
this.date = today;
|
|
29
|
+
}
|
|
30
|
+
return path.join(this.logDir, `${this.date}_${level}.log`);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Write a log entry to file (not printed to the console; console output is handled by existing process.stderr.write calls)
|
|
34
|
+
*/
|
|
35
|
+
write(level, message) {
|
|
36
|
+
const ts = this.timestamp();
|
|
37
|
+
const line = `[${ts}] ${message}
|
|
38
|
+
`;
|
|
39
|
+
try {
|
|
40
|
+
fs.appendFileSync(this.filePath(level), line);
|
|
41
|
+
} catch {
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** Informational log */
|
|
45
|
+
info(message) {
|
|
46
|
+
this.write("info", message);
|
|
47
|
+
}
|
|
48
|
+
/** Warning log */
|
|
49
|
+
warn(message) {
|
|
50
|
+
this.write("warning", message);
|
|
51
|
+
}
|
|
52
|
+
/** Error log */
|
|
53
|
+
error(message) {
|
|
54
|
+
this.write("error", message);
|
|
55
|
+
}
|
|
56
|
+
/** Log HTTP API request details */
|
|
57
|
+
api(method, url, status, reqBody, respBody) {
|
|
58
|
+
const parts = [`API ${method} ${url} \u2192 HTTP ${status}`];
|
|
59
|
+
if (reqBody !== void 0 && reqBody !== null) {
|
|
60
|
+
const s = typeof reqBody === "string" ? reqBody : JSON.stringify(reqBody);
|
|
61
|
+
parts.push(`REQ: ${s.slice(0, 500)}`);
|
|
62
|
+
}
|
|
63
|
+
if (respBody !== void 0 && respBody !== null) {
|
|
64
|
+
const s = typeof respBody === "string" ? respBody : JSON.stringify(respBody);
|
|
65
|
+
parts.push(`RESP: ${s.slice(0, 500)}`);
|
|
66
|
+
}
|
|
67
|
+
this.info(parts.join(" | "));
|
|
68
|
+
}
|
|
69
|
+
/** Log command execution */
|
|
70
|
+
command(name, args) {
|
|
71
|
+
const filtered = Object.entries(args).filter(([, v]) => v !== void 0 && v !== null && v !== "").map(([k, v]) => `--${k}=${JSON.stringify(v)}`).join(" ");
|
|
72
|
+
this.info(`CMD ${name}${filtered ? " " + filtered : ""}`);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
var logger = new Logger();
|
|
76
|
+
|
|
77
|
+
// src/core/json-utils.ts
|
|
78
|
+
import fs2 from "fs";
|
|
79
|
+
import { createRequire } from "module";
|
|
80
|
+
var require2 = createRequire(import.meta.url);
|
|
81
|
+
var JSONbig = require2("json-bigint");
|
|
82
|
+
var jsonParser = JSONbig({ storeAsString: true });
|
|
83
|
+
function safeJsonParse(text) {
|
|
84
|
+
return jsonParser.parse(text);
|
|
85
|
+
}
|
|
86
|
+
function safeReadJsonFile(filePath) {
|
|
87
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
88
|
+
return safeJsonParse(content);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// src/core/config.ts
|
|
92
|
+
import fs3 from "fs";
|
|
93
|
+
import path2 from "path";
|
|
94
|
+
|
|
95
|
+
// src/core/sandbox-runtime.ts
|
|
96
|
+
import { homedir } from "os";
|
|
97
|
+
import { join } from "path";
|
|
98
|
+
var LINUX_SANDBOX_RUNTIME_ROOT = "/home/ta/te_agent_ta";
|
|
99
|
+
function getDefaultSandboxRuntimeRoot() {
|
|
100
|
+
if (process.platform === "linux") {
|
|
101
|
+
return LINUX_SANDBOX_RUNTIME_ROOT;
|
|
102
|
+
}
|
|
103
|
+
return homedir();
|
|
104
|
+
}
|
|
105
|
+
function getSandboxRuntimeRoot() {
|
|
106
|
+
return process.env.SANDBOX_RUNTIME_ROOT || getDefaultSandboxRuntimeRoot();
|
|
107
|
+
}
|
|
108
|
+
function getSandboxAeConfigDir() {
|
|
109
|
+
return join(getSandboxRuntimeRoot(), ".ae-config");
|
|
110
|
+
}
|
|
111
|
+
function getSandboxMcpTokenFilePath() {
|
|
112
|
+
return join(getSandboxAeConfigDir(), "mcp-token.json");
|
|
113
|
+
}
|
|
114
|
+
function getSandboxClusterInfoFilePath() {
|
|
115
|
+
return join(getSandboxAeConfigDir(), "cluster-info.json");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// src/core/config.ts
|
|
119
|
+
var CONFIG_DIR = path2.join(process.env.HOME || "", ".ae-cli");
|
|
120
|
+
var CONFIG_FILE = path2.join(CONFIG_DIR, "config.json");
|
|
121
|
+
function sandboxMcpTokenCandidatePaths() {
|
|
122
|
+
const paths = [getSandboxMcpTokenFilePath()];
|
|
123
|
+
const homeFallback = path2.join(process.env.HOME || "", ".ae-config", "mcp-token.json");
|
|
124
|
+
if (!paths.includes(homeFallback)) {
|
|
125
|
+
paths.push(homeFallback);
|
|
126
|
+
}
|
|
127
|
+
return paths;
|
|
128
|
+
}
|
|
129
|
+
function readSandboxMcpTokenEntry() {
|
|
130
|
+
for (const file of sandboxMcpTokenCandidatePaths()) {
|
|
131
|
+
if (!fs3.existsSync(file)) continue;
|
|
132
|
+
try {
|
|
133
|
+
const data = safeReadJsonFile(file);
|
|
134
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
135
|
+
const url = data.url;
|
|
136
|
+
const token = data["mcp-token"];
|
|
137
|
+
if (url && token) {
|
|
138
|
+
return { url, token };
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
} catch {
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
function ensureDir() {
|
|
147
|
+
if (!fs3.existsSync(CONFIG_DIR)) fs3.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
148
|
+
}
|
|
149
|
+
function forceMigrateFromFallback() {
|
|
150
|
+
const entry = readSandboxMcpTokenEntry();
|
|
151
|
+
if (!entry) return null;
|
|
152
|
+
return { [entry.url]: entry.token };
|
|
153
|
+
}
|
|
154
|
+
function getFallbackMcpToken(hostUrl) {
|
|
155
|
+
const store = forceMigrateFromFallback();
|
|
156
|
+
if (!store) return null;
|
|
157
|
+
if (hostUrl && store[hostUrl]) return store[hostUrl];
|
|
158
|
+
const entries = Object.values(store);
|
|
159
|
+
if (entries.length === 1) return entries[0];
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
function loadConfig() {
|
|
163
|
+
try {
|
|
164
|
+
if (fs3.existsSync(CONFIG_FILE)) {
|
|
165
|
+
const raw = safeReadJsonFile(CONFIG_FILE);
|
|
166
|
+
if (raw.defaultHost && !raw.activeHost) {
|
|
167
|
+
const migrated = migrateConfig(raw);
|
|
168
|
+
saveConfig(migrated);
|
|
169
|
+
return migrated;
|
|
170
|
+
}
|
|
171
|
+
return raw;
|
|
172
|
+
}
|
|
173
|
+
} catch (err) {
|
|
174
|
+
logger.error(`Error loading config: ${err.message}`);
|
|
175
|
+
console.error(`Error loading config: ${err.message}`);
|
|
176
|
+
}
|
|
177
|
+
return { activeHost: "", hosts: {} };
|
|
178
|
+
}
|
|
179
|
+
function migrateConfig(old) {
|
|
180
|
+
const hosts = {};
|
|
181
|
+
const oldHost = old.defaultHost;
|
|
182
|
+
if (old.hosts) {
|
|
183
|
+
for (const [key, val] of Object.entries(old.hosts)) {
|
|
184
|
+
const url = key.startsWith("http") ? key : `https://${key}`;
|
|
185
|
+
hosts[url] = val;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const activeHost = oldHost.startsWith("http") ? oldHost : `https://${oldHost}`;
|
|
189
|
+
if (!hosts[activeHost]) {
|
|
190
|
+
hosts[activeHost] = { label: "default" };
|
|
191
|
+
}
|
|
192
|
+
return { activeHost, hosts };
|
|
193
|
+
}
|
|
194
|
+
function saveConfig(config) {
|
|
195
|
+
ensureDir();
|
|
196
|
+
fs3.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
197
|
+
logger.info(`Config saved: activeHost=${config.activeHost}, hosts=${Object.keys(config.hosts).length}`);
|
|
198
|
+
}
|
|
199
|
+
function getActiveHost() {
|
|
200
|
+
const config = loadConfig();
|
|
201
|
+
if (config.activeHost) return config.activeHost;
|
|
202
|
+
return readSandboxMcpTokenEntry()?.url ?? "";
|
|
203
|
+
}
|
|
204
|
+
function setActiveHost(url) {
|
|
205
|
+
const config = loadConfig();
|
|
206
|
+
if (!config.hosts[url]) {
|
|
207
|
+
throw new Error(`Host ${url} is not in the configured hosts list. Add it first.`);
|
|
208
|
+
}
|
|
209
|
+
config.activeHost = url;
|
|
210
|
+
saveConfig(config);
|
|
211
|
+
}
|
|
212
|
+
function addHost(url, label) {
|
|
213
|
+
const config = loadConfig();
|
|
214
|
+
assertUniqueHostLabel(config, label, url);
|
|
215
|
+
config.hosts[url] = { label };
|
|
216
|
+
if (!config.activeHost) {
|
|
217
|
+
config.activeHost = url;
|
|
218
|
+
}
|
|
219
|
+
saveConfig(config);
|
|
220
|
+
}
|
|
221
|
+
function assertUniqueHostLabel(config, label, excludeUrl) {
|
|
222
|
+
const duplicate = Object.entries(config.hosts).find(([url, entry]) => url !== excludeUrl && entry.label === label);
|
|
223
|
+
if (duplicate) {
|
|
224
|
+
throw new Error(`Label already exists: ${label}. Rename the existing label first: ${duplicate[0]}`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function getConfigDir() {
|
|
228
|
+
return CONFIG_DIR;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export {
|
|
232
|
+
logger,
|
|
233
|
+
safeJsonParse,
|
|
234
|
+
safeReadJsonFile,
|
|
235
|
+
getSandboxRuntimeRoot,
|
|
236
|
+
getSandboxClusterInfoFilePath,
|
|
237
|
+
getFallbackMcpToken,
|
|
238
|
+
loadConfig,
|
|
239
|
+
saveConfig,
|
|
240
|
+
getActiveHost,
|
|
241
|
+
setActiveHost,
|
|
242
|
+
addHost,
|
|
243
|
+
getConfigDir
|
|
244
|
+
};
|
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
} from "./chunk-
|
|
2
|
+
getAnalysisMappingPathForClusterMode
|
|
3
|
+
} from "./chunk-GSJGPNKK.js";
|
|
4
4
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
safeJsonParse,
|
|
9
|
-
safeReadJsonFile
|
|
10
|
-
} from "./chunk-BI5ZVD6T.js";
|
|
5
|
+
getToken,
|
|
6
|
+
loadMcpToken
|
|
7
|
+
} from "./chunk-CAQYQA4R.js";
|
|
11
8
|
import {
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
getActiveHost,
|
|
10
|
+
getFallbackMcpToken,
|
|
11
|
+
logger,
|
|
12
|
+
safeJsonParse
|
|
13
|
+
} from "./chunk-SRJIAOBN.js";
|
|
14
14
|
|
|
15
15
|
// src/core/mcp.ts
|
|
16
|
-
import fs from "fs";
|
|
17
|
-
import path from "path";
|
|
18
16
|
var MCP_PROTOCOL_VERSION = "2025-11-25";
|
|
19
17
|
var JSONRPC_VERSION = "2.0";
|
|
20
18
|
var serviceMappingMap = /* @__PURE__ */ new Map();
|
|
@@ -39,6 +37,9 @@ function getMcpMapping(serviceName) {
|
|
|
39
37
|
if (!mapping) {
|
|
40
38
|
throw new Error(`MCP mapping not found for service: '${serviceName}'. Please register it using registerMcpMapping() first.`);
|
|
41
39
|
}
|
|
40
|
+
if (serviceName === "analysis") {
|
|
41
|
+
return { ...mapping, mappingPath: getAnalysisMappingPathForClusterMode() };
|
|
42
|
+
}
|
|
42
43
|
return mapping;
|
|
43
44
|
}
|
|
44
45
|
function buildMcpUrl(host, serviceName) {
|
|
@@ -56,22 +57,8 @@ var requestIdCounter = 1;
|
|
|
56
57
|
function genRequestId() {
|
|
57
58
|
return requestIdCounter++;
|
|
58
59
|
}
|
|
59
|
-
var MCP_TOKEN_FILE = path.join(getConfigDir(), "mcp-tokens.json");
|
|
60
60
|
var MCP_TOKEN_GENERATE_PATH = "/v1/ta/mcp/token/generate";
|
|
61
|
-
|
|
62
|
-
try {
|
|
63
|
-
if (fs.existsSync(MCP_TOKEN_FILE)) {
|
|
64
|
-
return safeReadJsonFile(MCP_TOKEN_FILE);
|
|
65
|
-
}
|
|
66
|
-
} catch {
|
|
67
|
-
}
|
|
68
|
-
return {};
|
|
69
|
-
}
|
|
70
|
-
function saveMcpTokenStore(store) {
|
|
71
|
-
const dir = getConfigDir();
|
|
72
|
-
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
73
|
-
fs.writeFileSync(MCP_TOKEN_FILE, JSON.stringify(store, null, 2));
|
|
74
|
-
}
|
|
61
|
+
var _mcpTokenCache = /* @__PURE__ */ new Map();
|
|
75
62
|
async function generateMcpToken(hostUrl) {
|
|
76
63
|
const teToken = await getToken(hostUrl);
|
|
77
64
|
const base = hostUrl.replace(/\/+$/, "");
|
|
@@ -97,33 +84,38 @@ async function generateMcpToken(hostUrl) {
|
|
|
97
84
|
}
|
|
98
85
|
async function getMcpToken(hostOverride) {
|
|
99
86
|
const hostUrl = hostOverride || getActiveHost();
|
|
100
|
-
const
|
|
101
|
-
if (
|
|
102
|
-
return
|
|
87
|
+
const cached = _mcpTokenCache.get(hostUrl);
|
|
88
|
+
if (cached) {
|
|
89
|
+
return cached;
|
|
90
|
+
}
|
|
91
|
+
const storedToken = loadMcpToken(hostUrl);
|
|
92
|
+
if (storedToken) {
|
|
93
|
+
_mcpTokenCache.set(hostUrl, storedToken);
|
|
94
|
+
logger.info(`Using persisted MCP token (secure-store) for ${hostUrl}`);
|
|
95
|
+
return storedToken;
|
|
96
|
+
}
|
|
97
|
+
const fallbackToken = getFallbackMcpToken(hostUrl);
|
|
98
|
+
if (fallbackToken) {
|
|
99
|
+
_mcpTokenCache.set(hostUrl, fallbackToken);
|
|
100
|
+
logger.info(`Using sandbox-provisioned MCP token (fallback file) for ${hostUrl}`);
|
|
101
|
+
return fallbackToken;
|
|
103
102
|
}
|
|
104
103
|
logger.info(`Generating MCP token for ${hostUrl}`);
|
|
105
104
|
const mcpToken = await generateMcpToken(hostUrl);
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
logger.info(`MCP token generated and cached for ${hostUrl}`);
|
|
105
|
+
_mcpTokenCache.set(hostUrl, mcpToken);
|
|
106
|
+
logger.info(`MCP token generated and in-process cached for ${hostUrl}`);
|
|
109
107
|
return mcpToken;
|
|
110
108
|
}
|
|
111
109
|
function clearMcpToken(hostUrl) {
|
|
112
|
-
const store = loadMcpTokenStore();
|
|
113
110
|
if (hostUrl) {
|
|
114
|
-
delete
|
|
111
|
+
_mcpTokenCache.delete(hostUrl);
|
|
115
112
|
} else {
|
|
116
|
-
|
|
117
|
-
delete store[key];
|
|
118
|
-
}
|
|
113
|
+
_mcpTokenCache.clear();
|
|
119
114
|
}
|
|
120
|
-
saveMcpTokenStore(store);
|
|
121
115
|
}
|
|
122
116
|
function setMcpTokenManual(token, hostUrl) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
saveMcpTokenStore(store);
|
|
126
|
-
logger.info(`MCP token manually set for ${hostUrl}`);
|
|
117
|
+
_mcpTokenCache.set(hostUrl, token);
|
|
118
|
+
logger.info(`MCP token manually set (in-process) for ${hostUrl}`);
|
|
127
119
|
}
|
|
128
120
|
async function validateMcpToken(token, hostUrl) {
|
|
129
121
|
const mapping = getMcpMapping("analysis");
|
|
@@ -186,13 +178,16 @@ async function mcpRequest(url, method, params = {}, hostOverride) {
|
|
|
186
178
|
body: JSON.stringify(body)
|
|
187
179
|
});
|
|
188
180
|
if (!resp.ok) {
|
|
189
|
-
logger.warn(`MCP request failed (HTTP ${resp.status}) for ${hostUrl},
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
181
|
+
logger.warn(`MCP request failed (HTTP ${resp.status}) for ${hostUrl}, re-minting token`);
|
|
182
|
+
if (resp.status === 401 || resp.status === 403) {
|
|
183
|
+
_mcpTokenCache.delete(hostUrl);
|
|
184
|
+
const fallbackToken = getFallbackMcpToken(hostUrl);
|
|
185
|
+
const newToken = fallbackToken ?? await generateMcpToken(hostUrl);
|
|
186
|
+
_mcpTokenCache.set(hostUrl, newToken);
|
|
187
|
+
logger.info(`MCP token re-minted for ${hostUrl}`);
|
|
188
|
+
process.stderr.write(`[ae-cli] MCP token re-minted for ${hostUrl}
|
|
194
189
|
`);
|
|
195
|
-
headers["mcp-token"] =
|
|
190
|
+
headers["mcp-token"] = newToken;
|
|
196
191
|
const retryResp = await fetch(url, {
|
|
197
192
|
method: "POST",
|
|
198
193
|
headers,
|
|
@@ -253,7 +248,7 @@ function parseMcpResult(result) {
|
|
|
253
248
|
export {
|
|
254
249
|
registerMcpMappings,
|
|
255
250
|
resolveMcpUrl,
|
|
256
|
-
|
|
251
|
+
getMcpToken,
|
|
257
252
|
clearMcpToken,
|
|
258
253
|
setMcpTokenManual,
|
|
259
254
|
validateMcpToken,
|
|
@@ -7,10 +7,9 @@ import {
|
|
|
7
7
|
queryReportData,
|
|
8
8
|
querySql,
|
|
9
9
|
wsQuery
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-MNHE6SSI.js";
|
|
10
|
+
} from "./chunk-LU7SXK4Q.js";
|
|
11
|
+
import "./chunk-CAQYQA4R.js";
|
|
12
|
+
import "./chunk-SRJIAOBN.js";
|
|
14
13
|
export {
|
|
15
14
|
httpDelete,
|
|
16
15
|
httpGet,
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import {
|
|
2
|
+
printError,
|
|
3
|
+
printOutput
|
|
4
|
+
} from "./chunk-CLJF7MQA.js";
|
|
5
|
+
import {
|
|
6
|
+
GLOBAL_QUERY_CONFIG_KEY,
|
|
7
|
+
getAnalysisMappingPathForClusterMode,
|
|
8
|
+
getClusterInfoFilePath,
|
|
9
|
+
isGlobalQueryModeEnabled,
|
|
10
|
+
setGlobalQueryModeEnabled
|
|
11
|
+
} from "./chunk-GSJGPNKK.js";
|
|
12
|
+
import {
|
|
13
|
+
addHost,
|
|
14
|
+
loadConfig,
|
|
15
|
+
setActiveHost
|
|
16
|
+
} from "./chunk-SRJIAOBN.js";
|
|
17
|
+
|
|
18
|
+
// src/commands/config.ts
|
|
19
|
+
function registerConfig(program) {
|
|
20
|
+
const configCmd = program.command("config").description("AE host and environment configuration").action(() => {
|
|
21
|
+
printError("config", "Missing subcommand.", "Run: ae-cli config --help");
|
|
22
|
+
process.exit(1);
|
|
23
|
+
});
|
|
24
|
+
configCmd.command("list").description("List configured AE environments").action(() => {
|
|
25
|
+
printConfigList(program);
|
|
26
|
+
});
|
|
27
|
+
configCmd.command("current").description("Show the active AE environment").action(() => {
|
|
28
|
+
printCurrentConfig(program);
|
|
29
|
+
});
|
|
30
|
+
configCmd.command("use <env>").description("Switch active AE environment by exact URL or unique label").action((env) => {
|
|
31
|
+
useConfigEnv(program, env);
|
|
32
|
+
});
|
|
33
|
+
configCmd.command("set-host <url>").description("Set the active AE host non-interactively (adds it if not already configured)").option("--label <label>", "Display label for this host (defaults to the host URL)").action((url, opts) => {
|
|
34
|
+
const normalized = normalizeUrl(url);
|
|
35
|
+
const label = opts.label || normalized;
|
|
36
|
+
addHost(normalized, label);
|
|
37
|
+
setActiveHost(normalized);
|
|
38
|
+
printOutput({ activeHost: normalized, label }, program.opts().format || "json");
|
|
39
|
+
});
|
|
40
|
+
const clusterMode = configCmd.command("cluster-mode").description("Manage local multi-cluster analysis MCP mode").action(() => {
|
|
41
|
+
printClusterModeStatus(program);
|
|
42
|
+
});
|
|
43
|
+
clusterMode.command("status").description("Show local multi-cluster analysis MCP mode").action(() => {
|
|
44
|
+
printClusterModeStatus(program);
|
|
45
|
+
});
|
|
46
|
+
clusterMode.command("enable").description("Enable local multi-cluster analysis MCP mode").action(() => {
|
|
47
|
+
setGlobalQueryModeEnabled(true);
|
|
48
|
+
printClusterModeStatus(program);
|
|
49
|
+
});
|
|
50
|
+
clusterMode.command("disable").description("Disable local multi-cluster analysis MCP mode").action(() => {
|
|
51
|
+
setGlobalQueryModeEnabled(false);
|
|
52
|
+
printClusterModeStatus(program);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function printClusterModeStatus(program) {
|
|
56
|
+
const enabled = isGlobalQueryModeEnabled();
|
|
57
|
+
printOutput({
|
|
58
|
+
[GLOBAL_QUERY_CONFIG_KEY]: enabled,
|
|
59
|
+
path: getClusterInfoFilePath(),
|
|
60
|
+
analysisMappingPath: getAnalysisMappingPathForClusterMode(enabled)
|
|
61
|
+
}, program.opts().format || "json", program.opts().jq);
|
|
62
|
+
}
|
|
63
|
+
function readHostViews() {
|
|
64
|
+
const config = loadConfig();
|
|
65
|
+
const hosts = Object.entries(config.hosts).map(([url, entry]) => ({
|
|
66
|
+
url,
|
|
67
|
+
label: entry.label,
|
|
68
|
+
active: url === config.activeHost
|
|
69
|
+
}));
|
|
70
|
+
return { activeHost: config.activeHost, hosts };
|
|
71
|
+
}
|
|
72
|
+
function printConfigList(program) {
|
|
73
|
+
const { activeHost, hosts } = readHostViews();
|
|
74
|
+
printOutput({ activeHost, hosts }, program.opts().format || "json", program.opts().jq);
|
|
75
|
+
}
|
|
76
|
+
function printCurrentConfig(program) {
|
|
77
|
+
const { activeHost, hosts } = readHostViews();
|
|
78
|
+
const current = hosts.find((h) => h.url === activeHost);
|
|
79
|
+
if (!current) {
|
|
80
|
+
failConfig("No active AE environment configured.", "Run: ae-cli config use <env> or ae-cli config list");
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
printOutput({ activeHost: current.url, label: current.label }, program.opts().format || "json", program.opts().jq);
|
|
84
|
+
}
|
|
85
|
+
function useConfigEnv(program, env) {
|
|
86
|
+
const target = resolveHost(env);
|
|
87
|
+
if (target.status === "not-found") {
|
|
88
|
+
failConfig(
|
|
89
|
+
`No AE environment matched: ${env}`,
|
|
90
|
+
target.hosts.length > 0 ? `Available environments: ${formatHostOptions(target.hosts)}` : "Run: ae-cli config list"
|
|
91
|
+
);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (target.status === "duplicate-label") {
|
|
95
|
+
failConfig(
|
|
96
|
+
`Duplicate label: ${env}`,
|
|
97
|
+
`Rename duplicate labels first. Run: ae-cli config list \u2014 ${formatHostOptions(target.matches)}`
|
|
98
|
+
);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
setActiveHost(target.host.url);
|
|
102
|
+
printOutput({ activeHost: target.host.url, label: target.host.label }, program.opts().format || "json", program.opts().jq);
|
|
103
|
+
}
|
|
104
|
+
function resolveHost(env) {
|
|
105
|
+
const raw = env.trim();
|
|
106
|
+
const selector = raw.startsWith("http://") || raw.startsWith("https://") ? normalizeUrl(raw) : raw;
|
|
107
|
+
const { hosts } = readHostViews();
|
|
108
|
+
const byUrl = hosts.find((h) => h.url === selector);
|
|
109
|
+
if (byUrl) return { status: "found", host: byUrl };
|
|
110
|
+
const byLabel = hosts.filter((h) => h.label === raw);
|
|
111
|
+
if (byLabel.length === 1) return { status: "found", host: byLabel[0] };
|
|
112
|
+
if (byLabel.length > 1) return { status: "duplicate-label", matches: byLabel };
|
|
113
|
+
return { status: "not-found", hosts };
|
|
114
|
+
}
|
|
115
|
+
function formatHostOptions(hosts) {
|
|
116
|
+
return hosts.map((h) => `${h.label} (${h.url})`).join(", ");
|
|
117
|
+
}
|
|
118
|
+
function failConfig(message, hint) {
|
|
119
|
+
printError("config", message, hint);
|
|
120
|
+
process.exitCode = 1;
|
|
121
|
+
}
|
|
122
|
+
function normalizeUrl(url) {
|
|
123
|
+
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
|
124
|
+
url = `https://${url}`;
|
|
125
|
+
}
|
|
126
|
+
return url.replace(/\/$/, "");
|
|
127
|
+
}
|
|
128
|
+
export {
|
|
129
|
+
registerConfig
|
|
130
|
+
};
|