@thinkingai/ae-cli 1.0.24 → 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.
Files changed (106) hide show
  1. package/README.md +17 -2
  2. package/README.zh.md +18 -2
  3. package/dist/auth-5NSDQFQB.js +454 -0
  4. package/dist/{auth-4OPL4YWG.js → auth-H2376DEF.js} +2 -2
  5. package/dist/chunk-AD5V3ZPJ.js +217 -0
  6. package/dist/chunk-CAQYQA4R.js +399 -0
  7. package/dist/{chunk-JJCRURTR.js → chunk-CLJF7MQA.js} +1 -1
  8. package/dist/chunk-GSJGPNKK.js +49 -0
  9. package/dist/chunk-IMBKVXKY.js +478 -0
  10. package/dist/chunk-KAEZTSXN.js +99 -0
  11. package/dist/{chunk-MZW6NOVS.js → chunk-LU7SXK4Q.js} +27 -8
  12. package/dist/chunk-OJDNO5QY.js +63 -0
  13. package/dist/{chunk-DC66L5AM.js → chunk-SRJIAOBN.js} +76 -91
  14. package/dist/{chunk-OVHL4JAC.js → chunk-WCHI7725.js} +44 -47
  15. package/dist/{client-TDH6MUWM.js → client-FCMK3XEK.js} +5 -3
  16. package/dist/config-64G36TJL.js +130 -0
  17. package/dist/index.js +50 -17
  18. package/dist/model-EZ6K7FXI.js +134 -0
  19. package/dist/{raw-EM7AP3RT.js → raw-VEF3D6UD.js} +11 -6
  20. package/dist/sync-NRODEVNH.js +385 -0
  21. package/dist/te-agent-7U7JLJKU.js +524 -0
  22. package/dist/{te-analysis-3KFSNUCJ.js → te-analysis-A2OSGHQ4.js} +136 -20
  23. package/dist/{te-audience-GMYJFCSC.js → te-audience-LI67PPGO.js} +5 -4
  24. package/dist/{te-common-YLERCMQI.js → te-common-35WVU3C5.js} +19 -8
  25. package/dist/{te-community-PYDN5A6B.js → te-community-LEAP6PWT.js} +4 -3
  26. package/dist/{te-dataops-ETWS5O3X.js → te-dataops-N2LGZBOX.js} +4 -3
  27. package/dist/{te-engage-ROJVZHDA.js → te-engage-H7C72Z46.js} +105 -49
  28. package/dist/{te-kb-VUNS2CC7.js → te-kb-I2OKTBAI.js} +225 -29
  29. package/dist/{te-meta-3EFWLM3A.js → te-meta-HBTIEDRO.js} +4 -3
  30. package/dist/te-team-EM5OWRQL.js +529 -0
  31. package/package.json +10 -2
  32. package/skills/ae-agent/SKILL.md +134 -0
  33. package/skills/ae-analysis/SKILL.md +1 -1
  34. package/skills/ae-analysis/references/drilldown_user_events.md +1 -1
  35. package/skills/ae-analysis/references/drilldown_users.md +2 -2
  36. package/skills/ae-analysis/references/list_clusters.md +4 -2
  37. package/skills/ae-analysis/references/query_adhoc.md +3 -2
  38. package/skills/ae-analysis/references/query_entity_details.md +1 -1
  39. package/skills/ae-analysis-global/SKILL.md +51 -0
  40. package/skills/ae-analysis-global/references/list_query_clusters.md +67 -0
  41. package/skills/ae-community/SKILL.md +2 -3
  42. package/skills/ae-dataops/SKILL.md +1 -2
  43. package/skills/ae-engage/SKILL.md +26 -2
  44. package/skills/ae-engage/references/add-approver.md +0 -1
  45. package/skills/ae-engage/references/add-channel.md +0 -1
  46. package/skills/ae-engage/references/approver-list.md +0 -1
  47. package/skills/ae-engage/references/build-task-save-guide.md +283 -0
  48. package/skills/ae-engage/references/cancel-query-by-request-id.md +0 -1
  49. package/skills/ae-engage/references/channel-detail.md +0 -1
  50. package/skills/ae-engage/references/channel-list.md +0 -1
  51. package/skills/ae-engage/references/config-channel-detail.md +0 -1
  52. package/skills/ae-engage/references/config-channel-list.md +0 -1
  53. package/skills/ae-engage/references/config-item-analysis-report.md +0 -1
  54. package/skills/ae-engage/references/config-item-detail.md +0 -1
  55. package/skills/ae-engage/references/config-item-list.md +0 -1
  56. package/skills/ae-engage/references/config-item-strategy-comparison.md +0 -1
  57. package/skills/ae-engage/references/config-item-trigger-report.md +0 -1
  58. package/skills/ae-engage/references/copy-config-template.md +0 -1
  59. package/skills/ae-engage/references/delete-channel.md +0 -1
  60. package/skills/ae-engage/references/delete-config-channel.md +0 -1
  61. package/skills/ae-engage/references/delete-config-item.md +0 -1
  62. package/skills/ae-engage/references/delete-flow.md +0 -1
  63. package/skills/ae-engage/references/flow-ab-split-node-report.md +0 -1
  64. package/skills/ae-engage/references/flow-detail.md +0 -1
  65. package/skills/ae-engage/references/flow-list.md +0 -1
  66. package/skills/ae-engage/references/flow-node-config-schema.md +0 -1
  67. package/skills/ae-engage/references/flow-node-detail-report.md +0 -1
  68. package/skills/ae-engage/references/flow-node-overview-report.md +0 -1
  69. package/skills/ae-engage/references/flow-process-report.md +0 -1
  70. package/skills/ae-engage/references/manage-flow.md +0 -1
  71. package/skills/ae-engage/references/manage-strategy.md +0 -1
  72. package/skills/ae-engage/references/manage-task.md +0 -1
  73. package/skills/ae-engage/references/modify-flow-base-info.md +0 -1
  74. package/skills/ae-engage/references/save-task.md +304 -0
  75. package/skills/ae-engage/references/strategy-detail.md +0 -1
  76. package/skills/ae-engage/references/strategy-list.md +0 -1
  77. package/skills/ae-engage/references/task-data-detail.md +0 -1
  78. package/skills/ae-engage/references/task-data-overview.md +0 -1
  79. package/skills/ae-engage/references/task-detail.md +0 -1
  80. package/skills/ae-engage/references/task-experiment-report.md +0 -1
  81. package/skills/ae-engage/references/task-list.md +0 -1
  82. package/skills/ae-engage/references/task-metric-detail.md +0 -1
  83. package/skills/ae-engage/references/task-stats.md +0 -1
  84. package/skills/ae-engage/references/update-channel-status.md +0 -1
  85. package/skills/ae-engage/references/update-config-channel-status.md +0 -1
  86. package/skills/ae-engage/references/validate-flow-node-config.md +0 -1
  87. package/skills/ae-engage/references/whitelist-list.md +0 -1
  88. package/skills/ae-kb/SKILL.md +284 -0
  89. package/skills/ae-team/SKILL.md +164 -0
  90. package/skills/ae-team/references/ai-generate.md +39 -0
  91. package/skills/ae-team/references/create.md +94 -0
  92. package/skills/ae-team/references/delete.md +39 -0
  93. package/skills/ae-team/references/list-projects.md +45 -0
  94. package/skills/ae-team/references/list-templates.md +38 -0
  95. package/skills/ae-team/references/list.md +39 -0
  96. package/skills/ae-team/references/run-artifacts.md +51 -0
  97. package/skills/ae-team/references/run-cancel.md +38 -0
  98. package/skills/ae-team/references/run-chat.md +57 -0
  99. package/skills/ae-team/references/run-reply.md +41 -0
  100. package/skills/ae-team/references/run-result.md +75 -0
  101. package/skills/ae-team/references/run-start.md +73 -0
  102. package/skills/ae-team/references/run-watch.md +82 -0
  103. package/skills/ae-team/references/update.md +47 -0
  104. package/dist/auth-PBPAZQWP.js +0 -168
  105. package/dist/chunk-47MTN54I.js +0 -285
  106. package/dist/config-5ELRYAIH.js +0 -257
@@ -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
+ };
@@ -30,7 +30,7 @@ var Logger = class {
30
30
  return path.join(this.logDir, `${this.date}_${level}.log`);
31
31
  }
32
32
  /**
33
- * 写入日志到文件(不输出到控制台,控制台由现有 process.stderr.write 负责)
33
+ * Write a log entry to file (not printed to the console; console output is handled by existing process.stderr.write calls)
34
34
  */
35
35
  write(level, message) {
36
36
  const ts = this.timestamp();
@@ -41,19 +41,19 @@ var Logger = class {
41
41
  } catch {
42
42
  }
43
43
  }
44
- /** 常规操作日志 */
44
+ /** Informational log */
45
45
  info(message) {
46
46
  this.write("info", message);
47
47
  }
48
- /** 警告日志 */
48
+ /** Warning log */
49
49
  warn(message) {
50
50
  this.write("warning", message);
51
51
  }
52
- /** 错误日志 */
52
+ /** Error log */
53
53
  error(message) {
54
54
  this.write("error", message);
55
55
  }
56
- /** 记录 HTTP API 请求详情 */
56
+ /** Log HTTP API request details */
57
57
  api(method, url, status, reqBody, respBody) {
58
58
  const parts = [`API ${method} ${url} \u2192 HTTP ${status}`];
59
59
  if (reqBody !== void 0 && reqBody !== null) {
@@ -66,7 +66,7 @@ var Logger = class {
66
66
  }
67
67
  this.info(parts.join(" | "));
68
68
  }
69
- /** 记录命令执行 */
69
+ /** Log command execution */
70
70
  command(name, args) {
71
71
  const filtered = Object.entries(args).filter(([, v]) => v !== void 0 && v !== null && v !== "").map(([k, v]) => `--${k}=${JSON.stringify(v)}`).join(" ");
72
72
  this.info(`CMD ${name}${filtered ? " " + filtered : ""}`);
@@ -91,48 +91,76 @@ function safeReadJsonFile(filePath) {
91
91
  // src/core/config.ts
92
92
  import fs3 from "fs";
93
93
  import path2 from "path";
94
- var CONFIG_DIR = path2.join(process.env.HOME || "", ".ae-cli");
95
- var CONFIG_FILE = path2.join(CONFIG_DIR, "config.json");
96
- var MCP_TOKENS_FILE = path2.join(CONFIG_DIR, "mcp-tokens.json");
97
- var FALLBACK_MCP_TOKEN_FILE = "/data/app/te_agent_ta/.ae-config/mcp-token.json";
98
- function ensureDir() {
99
- if (!fs3.existsSync(CONFIG_DIR)) fs3.mkdirSync(CONFIG_DIR, { recursive: true });
100
- }
101
- function migrateFromFallback() {
102
- if (!fs3.existsSync(FALLBACK_MCP_TOKEN_FILE) || fs3.existsSync(MCP_TOKENS_FILE)) {
103
- return null;
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;
104
102
  }
105
- return doMigrateFromFallback();
103
+ return homedir();
106
104
  }
107
- function forceMigrateFromFallback() {
108
- if (!fs3.existsSync(FALLBACK_MCP_TOKEN_FILE)) {
109
- return null;
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);
110
126
  }
111
- return doMigrateFromFallback();
127
+ return paths;
112
128
  }
113
- function doMigrateFromFallback() {
114
- try {
115
- const fallbackData = safeReadJsonFile(FALLBACK_MCP_TOKEN_FILE);
116
- const tokenStore = {};
117
- if (fallbackData && typeof fallbackData === "object" && !Array.isArray(fallbackData)) {
118
- const url = fallbackData.url;
119
- const token = fallbackData["mcp-token"];
120
- if (url && token) {
121
- tokenStore[url] = token;
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
+ }
122
140
  }
141
+ } catch {
123
142
  }
124
- if (Object.keys(tokenStore).length > 0) {
125
- ensureDir();
126
- fs3.writeFileSync(MCP_TOKENS_FILE, JSON.stringify(tokenStore, null, 2));
127
- return tokenStore;
128
- }
129
- } catch {
130
143
  }
131
144
  return null;
132
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
+ }
133
162
  function loadConfig() {
134
163
  try {
135
- const migratedTokens = migrateFromFallback();
136
164
  if (fs3.existsSync(CONFIG_FILE)) {
137
165
  const raw = safeReadJsonFile(CONFIG_FILE);
138
166
  if (raw.defaultHost && !raw.activeHost) {
@@ -142,23 +170,6 @@ function loadConfig() {
142
170
  }
143
171
  return raw;
144
172
  }
145
- if (migratedTokens && Object.keys(migratedTokens).length > 0) {
146
- const hosts = {};
147
- for (const url of Object.keys(migratedTokens)) {
148
- hosts[url] = { label: url };
149
- process.stderr.write(`[ae-cli] Added host: ${url}
150
- `);
151
- }
152
- const activeHost = Object.keys(migratedTokens)[0];
153
- process.stderr.write(`[ae-cli] Active host set to: ${activeHost}
154
- `);
155
- process.stderr.write(`[ae-cli] MCP tokens migrated from ${FALLBACK_MCP_TOKEN_FILE}
156
- `);
157
- logger.info(`MCP tokens migrated from fallback: activeHost=${activeHost}, hosts=${Object.keys(hosts).length}`);
158
- const config = { activeHost, hosts };
159
- saveConfig(config);
160
- return config;
161
- }
162
173
  } catch (err) {
163
174
  logger.error(`Error loading config: ${err.message}`);
164
175
  console.error(`Error loading config: ${err.message}`);
@@ -187,7 +198,8 @@ function saveConfig(config) {
187
198
  }
188
199
  function getActiveHost() {
189
200
  const config = loadConfig();
190
- return config.activeHost;
201
+ if (config.activeHost) return config.activeHost;
202
+ return readSandboxMcpTokenEntry()?.url ?? "";
191
203
  }
192
204
  function setActiveHost(url) {
193
205
  const config = loadConfig();
@@ -199,61 +211,34 @@ function setActiveHost(url) {
199
211
  }
200
212
  function addHost(url, label) {
201
213
  const config = loadConfig();
214
+ assertUniqueHostLabel(config, label, url);
202
215
  config.hosts[url] = { label };
203
216
  if (!config.activeHost) {
204
217
  config.activeHost = url;
205
218
  }
206
219
  saveConfig(config);
207
220
  }
208
- function updateHostLabel(url, label) {
209
- const config = loadConfig();
210
- if (config.hosts[url]) {
211
- config.hosts[url].label = label;
212
- saveConfig(config);
213
- }
214
- }
215
- function removeHost(url) {
216
- const config = loadConfig();
217
- delete config.hosts[url];
218
- if (config.activeHost === url) {
219
- const remaining = Object.keys(config.hosts);
220
- config.activeHost = remaining.length > 0 ? remaining[0] : "";
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]}`);
221
225
  }
222
- saveConfig(config);
223
- }
224
- function listHosts() {
225
- const config = loadConfig();
226
- return Object.entries(config.hosts).map(([url, entry]) => ({
227
- url,
228
- label: entry.label,
229
- active: url === config.activeHost
230
- }));
231
226
  }
232
227
  function getConfigDir() {
233
228
  return CONFIG_DIR;
234
229
  }
235
- function extractHostname(fullUrl) {
236
- try {
237
- const u = new URL(fullUrl);
238
- return u.host;
239
- } catch {
240
- return fullUrl;
241
- }
242
- }
243
230
 
244
231
  export {
245
232
  logger,
246
233
  safeJsonParse,
247
234
  safeReadJsonFile,
248
- forceMigrateFromFallback,
235
+ getSandboxRuntimeRoot,
236
+ getSandboxClusterInfoFilePath,
237
+ getFallbackMcpToken,
249
238
  loadConfig,
250
239
  saveConfig,
251
240
  getActiveHost,
252
241
  setActiveHost,
253
242
  addHost,
254
- updateHostLabel,
255
- removeHost,
256
- listHosts,
257
- getConfigDir,
258
- extractHostname
243
+ getConfigDir
259
244
  };
@@ -1,18 +1,18 @@
1
1
  import {
2
- getToken
3
- } from "./chunk-47MTN54I.js";
2
+ getAnalysisMappingPathForClusterMode
3
+ } from "./chunk-GSJGPNKK.js";
4
+ import {
5
+ getToken,
6
+ loadMcpToken
7
+ } from "./chunk-CAQYQA4R.js";
4
8
  import {
5
- forceMigrateFromFallback,
6
9
  getActiveHost,
7
- getConfigDir,
10
+ getFallbackMcpToken,
8
11
  logger,
9
- safeJsonParse,
10
- safeReadJsonFile
11
- } from "./chunk-DC66L5AM.js";
12
+ safeJsonParse
13
+ } from "./chunk-SRJIAOBN.js";
12
14
 
13
15
  // src/core/mcp.ts
14
- import fs from "fs";
15
- import path from "path";
16
16
  var MCP_PROTOCOL_VERSION = "2025-11-25";
17
17
  var JSONRPC_VERSION = "2.0";
18
18
  var serviceMappingMap = /* @__PURE__ */ new Map();
@@ -37,6 +37,9 @@ function getMcpMapping(serviceName) {
37
37
  if (!mapping) {
38
38
  throw new Error(`MCP mapping not found for service: '${serviceName}'. Please register it using registerMcpMapping() first.`);
39
39
  }
40
+ if (serviceName === "analysis") {
41
+ return { ...mapping, mappingPath: getAnalysisMappingPathForClusterMode() };
42
+ }
40
43
  return mapping;
41
44
  }
42
45
  function buildMcpUrl(host, serviceName) {
@@ -54,22 +57,8 @@ var requestIdCounter = 1;
54
57
  function genRequestId() {
55
58
  return requestIdCounter++;
56
59
  }
57
- var MCP_TOKEN_FILE = path.join(getConfigDir(), "mcp-tokens.json");
58
60
  var MCP_TOKEN_GENERATE_PATH = "/v1/ta/mcp/token/generate";
59
- function loadMcpTokenStore() {
60
- try {
61
- if (fs.existsSync(MCP_TOKEN_FILE)) {
62
- return safeReadJsonFile(MCP_TOKEN_FILE);
63
- }
64
- } catch {
65
- }
66
- return {};
67
- }
68
- function saveMcpTokenStore(store) {
69
- const dir = getConfigDir();
70
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
71
- fs.writeFileSync(MCP_TOKEN_FILE, JSON.stringify(store, null, 2));
72
- }
61
+ var _mcpTokenCache = /* @__PURE__ */ new Map();
73
62
  async function generateMcpToken(hostUrl) {
74
63
  const teToken = await getToken(hostUrl);
75
64
  const base = hostUrl.replace(/\/+$/, "");
@@ -95,33 +84,38 @@ async function generateMcpToken(hostUrl) {
95
84
  }
96
85
  async function getMcpToken(hostOverride) {
97
86
  const hostUrl = hostOverride || getActiveHost();
98
- const store = loadMcpTokenStore();
99
- if (store[hostUrl]) {
100
- return store[hostUrl];
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;
101
102
  }
102
103
  logger.info(`Generating MCP token for ${hostUrl}`);
103
104
  const mcpToken = await generateMcpToken(hostUrl);
104
- const updatedStore = { ...store, [hostUrl]: mcpToken };
105
- saveMcpTokenStore(updatedStore);
106
- 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}`);
107
107
  return mcpToken;
108
108
  }
109
109
  function clearMcpToken(hostUrl) {
110
- const store = loadMcpTokenStore();
111
110
  if (hostUrl) {
112
- delete store[hostUrl];
111
+ _mcpTokenCache.delete(hostUrl);
113
112
  } else {
114
- for (const key of Object.keys(store)) {
115
- delete store[key];
116
- }
113
+ _mcpTokenCache.clear();
117
114
  }
118
- saveMcpTokenStore(store);
119
115
  }
120
116
  function setMcpTokenManual(token, hostUrl) {
121
- const store = loadMcpTokenStore();
122
- store[hostUrl] = token;
123
- saveMcpTokenStore(store);
124
- logger.info(`MCP token manually set for ${hostUrl}`);
117
+ _mcpTokenCache.set(hostUrl, token);
118
+ logger.info(`MCP token manually set (in-process) for ${hostUrl}`);
125
119
  }
126
120
  async function validateMcpToken(token, hostUrl) {
127
121
  const mapping = getMcpMapping("analysis");
@@ -184,13 +178,16 @@ async function mcpRequest(url, method, params = {}, hostOverride) {
184
178
  body: JSON.stringify(body)
185
179
  });
186
180
  if (!resp.ok) {
187
- logger.warn(`MCP request failed (HTTP ${resp.status}) for ${hostUrl}, trying fallback`);
188
- const migrated = forceMigrateFromFallback();
189
- if (migrated && migrated[hostUrl]) {
190
- logger.info(`MCP token refreshed from fallback for ${hostUrl}`);
191
- process.stderr.write(`[ae-cli] MCP token refreshed from fallback for ${hostUrl}
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}
192
189
  `);
193
- headers["mcp-token"] = migrated[hostUrl];
190
+ headers["mcp-token"] = newToken;
194
191
  const retryResp = await fetch(url, {
195
192
  method: "POST",
196
193
  headers,
@@ -251,7 +248,7 @@ function parseMcpResult(result) {
251
248
  export {
252
249
  registerMcpMappings,
253
250
  resolveMcpUrl,
254
- loadMcpTokenStore,
251
+ getMcpToken,
255
252
  clearMcpToken,
256
253
  setMcpTokenManual,
257
254
  validateMcpToken,
@@ -2,17 +2,19 @@ import {
2
2
  httpDelete,
3
3
  httpGet,
4
4
  httpPost,
5
+ httpRequest,
5
6
  httpUpload,
6
7
  queryReportData,
7
8
  querySql,
8
9
  wsQuery
9
- } from "./chunk-MZW6NOVS.js";
10
- import "./chunk-47MTN54I.js";
11
- import "./chunk-DC66L5AM.js";
10
+ } from "./chunk-LU7SXK4Q.js";
11
+ import "./chunk-CAQYQA4R.js";
12
+ import "./chunk-SRJIAOBN.js";
12
13
  export {
13
14
  httpDelete,
14
15
  httpGet,
15
16
  httpPost,
17
+ httpRequest,
16
18
  httpUpload,
17
19
  queryReportData,
18
20
  querySql,
@@ -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
+ };