@thinkingai/ae-cli 1.0.22 → 1.0.24

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 CHANGED
@@ -60,6 +60,14 @@ ae-cli analysis_meta +list_events --project_id 1 --format table
60
60
 
61
61
  # Raw API call
62
62
  ae-cli api GET /v1/ta/event/catalog/listEvent --params '{"projectId": 1}'
63
+
64
+ # Knowledge base (create → upload → compile → query)
65
+ ae-cli kb +new --scope company --name engineering-handbook --description "Team docs"
66
+ ae-cli kb +add --name engineering-handbook --files '["./docs/guide.md","https://example.com/page"]'
67
+ ae-cli kb +schema --name engineering-handbook
68
+ ae-cli kb +compile --name engineering-handbook
69
+ ae-cli kb +query -q "How to configure sandbox?" \
70
+ --sources '[{"scope":"company","name":"engineering-handbook"}]'
63
71
  ```
64
72
 
65
73
  ## Authentication
@@ -97,8 +105,21 @@ ae-cli auth logout
97
105
  | `dataops_integration` | 20+ | Data integration: datasource management, sync solutions, data synchronization |
98
106
  | `community` | 10+ | Community analysis: posts search, sentiment analysis, topic trends, livestream data |
99
107
  | `analysis_common` | 2 | Cross-module common constraints: resource link completion, project ID gate |
108
+ | `kb` | 7 | Knowledge base lifecycle: query / new / add (md/dir/url) / schema / compile / rm-source / remove |
100
109
  | `auth` / `config` | 2 | Authentication and host configuration |
101
110
 
111
+ ### kb
112
+
113
+ Knowledge base lifecycle (`+new` → `+add` → `+schema` → `+compile` → `+query`):
114
+
115
+ - **Create** (`+new`): `--scope personal|company`, `--name`, optional `--description`, `--tags`, `--project-id`, `--project-name`
116
+ - **Upload** (`+add`): `--name`, `--files` JSON array (`.md` file, directory non-recursive scan, or http(s) URL with HTML-to-Markdown conversion)
117
+ - **Schema** (`+schema`): `--name`, optional `--force`, `--model`
118
+ - **Compile** (`+compile`): `--name`, `--mode incremental|full` (default: incremental)
119
+ - **Query** (`+query`): `--query` / `-q`, `--sources` JSON refs e.g. `[{"scope":"company","name":"engineering-handbook"}]`
120
+ - **Remove source** (`+rm-source`): `--name`, `--display-name`
121
+ - **Remove KB** (`+remove`): `--name`
122
+
102
123
  ### Global Options
103
124
 
104
125
  | Option | Description | Default |
@@ -108,6 +129,7 @@ ae-cli auth logout
108
129
  | `--jq <expr>` | Filter expression | - |
109
130
  | `--dry-run` | Preview request | false |
110
131
  | `--yes` | Skip confirmation | false |
132
+ | `--no-update-check` | Skip checking for newer ae-cli versions | false |
111
133
 
112
134
  ## Skills
113
135
 
@@ -185,7 +207,8 @@ src/
185
207
  ├── te-engage/
186
208
  ├── te-dataops/
187
209
  ├── te-community/
188
- └── te-common/
210
+ ├── te-common/
211
+ └── te-kb/
189
212
  ```
190
213
 
191
214
  ## Verification Scripts
package/README.zh.md CHANGED
@@ -60,6 +60,14 @@ ae-cli analysis_meta +list_events --project_id 1 --format table
60
60
 
61
61
  # 原始 API 调用
62
62
  ae-cli api GET /v1/ta/event/catalog/listEvent --params '{"projectId": 1}'
63
+
64
+ # 知识库(新建 → 上传 → 编译 → 查询)
65
+ ae-cli kb +new --scope company --name engineering-handbook --description "Team docs"
66
+ ae-cli kb +add --name engineering-handbook --files '["./docs/guide.md","https://example.com/page"]'
67
+ ae-cli kb +schema --name engineering-handbook
68
+ ae-cli kb +compile --name engineering-handbook
69
+ ae-cli kb +query -q "如何配置 sandbox 容器?" \
70
+ --sources '[{"scope":"company","name":"engineering-handbook"}]'
63
71
  ```
64
72
 
65
73
  ## 身份验证
@@ -97,8 +105,21 @@ ae-cli auth logout
97
105
  | `dataops_integration` | 20+ | 数据集成:数据源管理、同步方案、数据同步 |
98
106
  | `community` | 10+ | 社区分析:帖子搜索、情感分析、话题趋势、直播数据 |
99
107
  | `analysis_common` | 2 | 跨模块通用约束:资源链接补全、项目 ID 门控 |
108
+ | `kb` | 7 | 知识库生命周期:查询 / 新建 / 添加源(md/目录/url)/ schema / 编译 / 删除源 / 删除 |
100
109
  | `auth` / `config` | 2 | 身份验证和主机配置 |
101
110
 
111
+ ### kb(知识库)
112
+
113
+ 知识库生命周期(`+new` → `+add` → `+schema` → `+compile` → `+query`):
114
+
115
+ - **新建** (`+new`):`--scope personal|company`,`--name`,可选 `--description`、`--tags`、`--project-id`、`--project-name`
116
+ - **上传源** (`+add`):`--name`,`--files` JSON 数组(`.md` 文件、目录非递归扫描、或 http(s) URL 自动转 Markdown)
117
+ - **生成 schema** (`+schema`):`--name`,可选 `--force`、`--model`
118
+ - **编译** (`+compile`):`--name`,`--mode incremental|full`(默认 incremental)
119
+ - **查询** (`+query`):`--query` / `-q`,`--sources` JSON 引用,如 `[{"scope":"company","name":"engineering-handbook"}]`
120
+ - **删除源** (`+rm-source`):`--name`,`--display-name`
121
+ - **删除知识库** (`+remove`):`--name`
122
+
102
123
  ### 全局选项
103
124
 
104
125
  | 选项 | 描述 | 默认值 |
@@ -108,6 +129,7 @@ ae-cli auth logout
108
129
  | `--jq <expr>` | 过滤表达式 | - |
109
130
  | `--dry-run` | 预览请求 | false |
110
131
  | `--yes` | 跳过确认 | false |
132
+ | `--no-update-check` | 跳过 ae-cli 新版本检查 | false |
111
133
 
112
134
  ## Skills
113
135
 
@@ -185,7 +207,8 @@ src/
185
207
  ├── te-engage/
186
208
  ├── te-dataops/
187
209
  ├── te-community/
188
- └── te-common/
210
+ ├── te-common/
211
+ └── te-kb/
189
212
  ```
190
213
 
191
214
  ## 验证脚本
@@ -7,8 +7,8 @@ import {
7
7
  saveToken,
8
8
  setTokenManual,
9
9
  validateToken
10
- } from "./chunk-24ZKQWG2.js";
11
- import "./chunk-TMMUBSKW.js";
10
+ } from "./chunk-47MTN54I.js";
11
+ import "./chunk-DC66L5AM.js";
12
12
  export {
13
13
  clearToken,
14
14
  getAuthStatus,
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  printError,
3
3
  printOutput
4
- } from "./chunk-KM57HI5B.js";
4
+ } from "./chunk-JJCRURTR.js";
5
5
  import {
6
6
  clearMcpToken,
7
7
  loadMcpTokenStore,
8
8
  setMcpTokenManual,
9
9
  validateMcpToken
10
- } from "./chunk-MPFTXJFG.js";
10
+ } from "./chunk-OVHL4JAC.js";
11
11
  import {
12
12
  clearToken,
13
13
  getAuthStatus,
@@ -15,11 +15,12 @@ import {
15
15
  resolveHost,
16
16
  setTokenManual,
17
17
  validateToken
18
- } from "./chunk-24ZKQWG2.js";
18
+ } from "./chunk-47MTN54I.js";
19
19
  import {
20
20
  loadConfig,
21
+ logger,
21
22
  saveConfig
22
- } from "./chunk-TMMUBSKW.js";
23
+ } from "./chunk-DC66L5AM.js";
23
24
 
24
25
  // src/commands/auth.ts
25
26
  function registerAuth(program) {
@@ -37,11 +38,13 @@ function registerAuth(program) {
37
38
  config.activeHost = host;
38
39
  }
39
40
  saveConfig(config);
41
+ logger.info(`Host config auto-saved for ${host}`);
40
42
  process.stderr.write(`[ae-cli] Config saved for ${host}
41
43
  `);
42
44
  }
43
45
  try {
44
46
  const token = await getToken(host);
47
+ logger.info(`Auth login successful for ${host}`);
45
48
  process.stderr.write(`[ae-cli] Authenticated to ${host}
46
49
  `);
47
50
  printOutput({ authenticated: true, host, token: token.slice(0, 8) + "..." }, program.opts().format || "json");
@@ -71,12 +74,14 @@ function registerAuth(program) {
71
74
  `);
72
75
  const isValid = await validateToken(token, host);
73
76
  if (!isValid) {
77
+ logger.warn(`Token validation failed for ${host}`);
74
78
  process.stderr.write(`[ae-cli] Token validation failed
75
79
  `);
76
80
  printError("auth", "Invalid token", "Please check your token and try again");
77
81
  process.exit(1);
78
82
  }
79
83
  setTokenManual(token, host);
84
+ logger.info(`Token manually set and validated for ${host}`);
80
85
  process.stderr.write(`[ae-cli] Token verified and saved for ${host}
81
86
  `);
82
87
  printOutput({ saved: true, host, verified: true }, program.opts().format || "json");
@@ -2,9 +2,10 @@ import {
2
2
  extractHostname,
3
3
  getActiveHost,
4
4
  getConfigDir,
5
+ logger,
5
6
  safeJsonParse,
6
7
  safeReadJsonFile
7
- } from "./chunk-TMMUBSKW.js";
8
+ } from "./chunk-DC66L5AM.js";
8
9
 
9
10
  // src/core/auth.ts
10
11
  import fs from "fs";
@@ -90,6 +91,7 @@ function saveToken(token, hostUrl) {
90
91
  const tokens = loadAllTokens();
91
92
  tokens[hostUrl] = { token, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
92
93
  saveAllTokens(tokens);
94
+ logger.info(`Token saved for ${hostUrl}`);
93
95
  }
94
96
  function clearToken(hostUrl) {
95
97
  const tokens = loadAllTokens();
@@ -172,6 +174,7 @@ function extractTokenFromAllTabs() {
172
174
  }
173
175
  }
174
176
  async function requestTokenViaOsascript(hostUrl) {
177
+ logger.info(`Opening Chrome to ${hostUrl} for token extraction`);
175
178
  process.stderr.write(`[ae-cli] No AE tab found in Chrome. Opening ${hostUrl} ...
176
179
  `);
177
180
  process.stderr.write(`[ae-cli] Please login, then your token will be captured automatically.
@@ -189,24 +192,36 @@ async function requestTokenViaOsascript(hostUrl) {
189
192
  ];
190
193
  execFileSync("osascript", openScript.flatMap((line) => ["-e", line]), { timeout: 5e3 });
191
194
  } catch {
195
+ logger.warn(`Failed to open Chrome, user may need to open ${hostUrl} manually`);
192
196
  process.stderr.write(`[ae-cli] Could not open browser. Please open ${hostUrl} in Chrome manually.
193
197
  `);
194
198
  }
195
199
  const deadline = Date.now() + OSASCRIPT_POLL_TIMEOUT_MS;
200
+ let pollCount = 0;
196
201
  while (Date.now() < deadline) {
197
202
  await new Promise((r) => setTimeout(r, OSASCRIPT_POLL_INTERVAL_MS));
203
+ pollCount++;
198
204
  let result = extractTokenViaOsascript(hostUrl);
199
205
  if (result.error === "no_tab") {
200
206
  const fallback = extractTokenFromAllTabs();
201
- if (fallback.token) result = fallback;
207
+ if (fallback.token) {
208
+ logger.info("Token captured via all-tabs fallback search");
209
+ result = fallback;
210
+ }
202
211
  }
203
212
  if (result.token) {
213
+ saveToken(result.token, hostUrl);
214
+ logger.info(`Token captured after ${pollCount} poll(s)`);
204
215
  process.stderr.write(`[ae-cli] Token captured from Chrome for ${hostUrl}.
205
216
  `);
206
217
  return result.token;
207
218
  }
208
- if (result.error === "no_js_permission") return null;
219
+ if (result.error === "no_js_permission") {
220
+ logger.warn("Chrome JS permission denied during polling");
221
+ return null;
222
+ }
209
223
  }
224
+ logger.warn(`Token polling timed out after ${OSASCRIPT_POLL_TIMEOUT_MS / 1e3}s (${pollCount} polls)`);
210
225
  process.stderr.write(`[ae-cli] Polling timed out after ${OSASCRIPT_POLL_TIMEOUT_MS / 1e3}s.
211
226
  `);
212
227
  return null;
@@ -219,12 +234,17 @@ Run: ae-cli config set-host`
219
234
  );
220
235
  }
221
236
  if (process.env.TE_TOKEN) {
237
+ logger.info("Using token from env:TE_TOKEN");
222
238
  return process.env.TE_TOKEN;
223
239
  }
224
240
  const cached = loadToken(hostUrl);
225
- if (cached && cached.token) return cached.token;
241
+ if (cached && cached.token) {
242
+ logger.info(`Using cached token for ${hostUrl}`);
243
+ return cached.token;
244
+ }
226
245
  const { token: osascriptToken, error } = extractTokenViaOsascript(hostUrl);
227
246
  if (error === "no_js_permission") {
247
+ logger.warn("Chrome JS Apple Events not enabled");
228
248
  throw new Error(
229
249
  `Chrome JavaScript from Apple Events is not enabled.
230
250
  Enable it: Chrome menu \u2192 View \u2192 Developer \u2192 Allow JavaScript from Apple Events`
@@ -1,5 +1,81 @@
1
- // src/core/json-utils.ts
1
+ // src/core/logger.ts
2
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
+ * 写入日志到文件(不输出到控制台,控制台由现有 process.stderr.write 负责)
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
+ /** 常规操作日志 */
45
+ info(message) {
46
+ this.write("info", message);
47
+ }
48
+ /** 警告日志 */
49
+ warn(message) {
50
+ this.write("warning", message);
51
+ }
52
+ /** 错误日志 */
53
+ error(message) {
54
+ this.write("error", message);
55
+ }
56
+ /** 记录 HTTP API 请求详情 */
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
+ /** 记录命令执行 */
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";
3
79
  import { createRequire } from "module";
4
80
  var require2 = createRequire(import.meta.url);
5
81
  var JSONbig = require2("json-bigint");
@@ -8,28 +84,28 @@ function safeJsonParse(text) {
8
84
  return jsonParser.parse(text);
9
85
  }
10
86
  function safeReadJsonFile(filePath) {
11
- const content = fs.readFileSync(filePath, "utf-8");
87
+ const content = fs2.readFileSync(filePath, "utf-8");
12
88
  return safeJsonParse(content);
13
89
  }
14
90
 
15
91
  // src/core/config.ts
16
- import fs2 from "fs";
17
- import path from "path";
18
- var CONFIG_DIR = path.join(process.env.HOME || "", ".ae-cli");
19
- var CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
20
- var MCP_TOKENS_FILE = path.join(CONFIG_DIR, "mcp-tokens.json");
92
+ import fs3 from "fs";
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");
21
97
  var FALLBACK_MCP_TOKEN_FILE = "/data/app/te_agent_ta/.ae-config/mcp-token.json";
22
98
  function ensureDir() {
23
- if (!fs2.existsSync(CONFIG_DIR)) fs2.mkdirSync(CONFIG_DIR, { recursive: true });
99
+ if (!fs3.existsSync(CONFIG_DIR)) fs3.mkdirSync(CONFIG_DIR, { recursive: true });
24
100
  }
25
101
  function migrateFromFallback() {
26
- if (!fs2.existsSync(FALLBACK_MCP_TOKEN_FILE) || fs2.existsSync(MCP_TOKENS_FILE)) {
102
+ if (!fs3.existsSync(FALLBACK_MCP_TOKEN_FILE) || fs3.existsSync(MCP_TOKENS_FILE)) {
27
103
  return null;
28
104
  }
29
105
  return doMigrateFromFallback();
30
106
  }
31
107
  function forceMigrateFromFallback() {
32
- if (!fs2.existsSync(FALLBACK_MCP_TOKEN_FILE)) {
108
+ if (!fs3.existsSync(FALLBACK_MCP_TOKEN_FILE)) {
33
109
  return null;
34
110
  }
35
111
  return doMigrateFromFallback();
@@ -47,7 +123,7 @@ function doMigrateFromFallback() {
47
123
  }
48
124
  if (Object.keys(tokenStore).length > 0) {
49
125
  ensureDir();
50
- fs2.writeFileSync(MCP_TOKENS_FILE, JSON.stringify(tokenStore, null, 2));
126
+ fs3.writeFileSync(MCP_TOKENS_FILE, JSON.stringify(tokenStore, null, 2));
51
127
  return tokenStore;
52
128
  }
53
129
  } catch {
@@ -57,7 +133,7 @@ function doMigrateFromFallback() {
57
133
  function loadConfig() {
58
134
  try {
59
135
  const migratedTokens = migrateFromFallback();
60
- if (fs2.existsSync(CONFIG_FILE)) {
136
+ if (fs3.existsSync(CONFIG_FILE)) {
61
137
  const raw = safeReadJsonFile(CONFIG_FILE);
62
138
  if (raw.defaultHost && !raw.activeHost) {
63
139
  const migrated = migrateConfig(raw);
@@ -78,11 +154,13 @@ function loadConfig() {
78
154
  `);
79
155
  process.stderr.write(`[ae-cli] MCP tokens migrated from ${FALLBACK_MCP_TOKEN_FILE}
80
156
  `);
157
+ logger.info(`MCP tokens migrated from fallback: activeHost=${activeHost}, hosts=${Object.keys(hosts).length}`);
81
158
  const config = { activeHost, hosts };
82
159
  saveConfig(config);
83
160
  return config;
84
161
  }
85
162
  } catch (err) {
163
+ logger.error(`Error loading config: ${err.message}`);
86
164
  console.error(`Error loading config: ${err.message}`);
87
165
  }
88
166
  return { activeHost: "", hosts: {} };
@@ -104,7 +182,8 @@ function migrateConfig(old) {
104
182
  }
105
183
  function saveConfig(config) {
106
184
  ensureDir();
107
- fs2.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
185
+ fs3.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
186
+ logger.info(`Config saved: activeHost=${config.activeHost}, hosts=${Object.keys(config.hosts).length}`);
108
187
  }
109
188
  function getActiveHost() {
110
189
  const config = loadConfig();
@@ -163,6 +242,7 @@ function extractHostname(fullUrl) {
163
242
  }
164
243
 
165
244
  export {
245
+ logger,
166
246
  safeJsonParse,
167
247
  safeReadJsonFile,
168
248
  forceMigrateFromFallback,
@@ -1,3 +1,7 @@
1
+ import {
2
+ logger
3
+ } from "./chunk-DC66L5AM.js";
4
+
1
5
  // src/framework/output.ts
2
6
  import Table from "cli-table3";
3
7
  var KNOWN_ARRAY_FIELDS = [
@@ -95,7 +99,9 @@ function formatError(type, message, hint, code) {
95
99
  return JSON.stringify(envelope, null, 2);
96
100
  }
97
101
  function printError(type, message, hint, code) {
98
- process.stderr.write(formatError(type, message, hint, code) + "\n");
102
+ const formatted = formatError(type, message, hint, code);
103
+ process.stderr.write(formatted + "\n");
104
+ logger.error(`[${type}] ${message}${hint ? " | " + hint : ""}`);
99
105
  }
100
106
  function printOutput(data, format, jqExpr) {
101
107
  process.stdout.write(formatOutput(data, format, jqExpr) + "\n");
@@ -2,10 +2,11 @@ import {
2
2
  clearToken,
3
3
  getToken,
4
4
  resolveHost
5
- } from "./chunk-24ZKQWG2.js";
5
+ } from "./chunk-47MTN54I.js";
6
6
  import {
7
+ logger,
7
8
  safeJsonParse
8
- } from "./chunk-TMMUBSKW.js";
9
+ } from "./chunk-DC66L5AM.js";
9
10
 
10
11
  // src/core/client.ts
11
12
  import WebSocket from "ws";
@@ -39,11 +40,13 @@ async function request(method, modulePath, params = {}, body = null, retry = tru
39
40
  const options = { method, headers };
40
41
  if (body && method !== "GET") options.body = JSON.stringify(body);
41
42
  const resp = await fetch(url, options);
43
+ const text = await resp.text();
44
+ const data = safeJsonParse(text);
45
+ logger.api(method, url, resp.status, body, data);
42
46
  if ((resp.status === 401 || resp.status === 403) && retry) {
43
47
  clearToken(resolvedHost);
44
48
  return request(method, modulePath, params, body, false, resolvedHost);
45
49
  }
46
- const data = safeJsonParse(await resp.text());
47
50
  if (data.return_code === -1001 && retry) {
48
51
  clearToken(resolvedHost);
49
52
  return request(method, modulePath, params, body, false, resolvedHost);
@@ -70,11 +73,13 @@ async function uploadRequest(modulePath, form, params = {}, retry = true, hostUr
70
73
  "Authorization": `bearer ${token}`
71
74
  };
72
75
  const resp = await fetch(url, { method: "POST", headers, body: form });
76
+ const text = await resp.text();
77
+ const data = safeJsonParse(text);
78
+ logger.api("POST", url, resp.status, { upload: true }, data);
73
79
  if ((resp.status === 401 || resp.status === 403) && retry) {
74
80
  clearToken(resolvedHost);
75
81
  return uploadRequest(modulePath, form, params, false, resolvedHost);
76
82
  }
77
- const data = safeJsonParse(await resp.text());
78
83
  if (data.return_code === -1001 && retry) {
79
84
  clearToken(resolvedHost);
80
85
  return uploadRequest(modulePath, form, params, false, resolvedHost);
@@ -1,13 +1,14 @@
1
1
  import {
2
2
  getToken
3
- } from "./chunk-24ZKQWG2.js";
3
+ } from "./chunk-47MTN54I.js";
4
4
  import {
5
5
  forceMigrateFromFallback,
6
6
  getActiveHost,
7
7
  getConfigDir,
8
+ logger,
8
9
  safeJsonParse,
9
10
  safeReadJsonFile
10
- } from "./chunk-TMMUBSKW.js";
11
+ } from "./chunk-DC66L5AM.js";
11
12
 
12
13
  // src/core/mcp.ts
13
14
  import fs from "fs";
@@ -98,9 +99,11 @@ async function getMcpToken(hostOverride) {
98
99
  if (store[hostUrl]) {
99
100
  return store[hostUrl];
100
101
  }
102
+ logger.info(`Generating MCP token for ${hostUrl}`);
101
103
  const mcpToken = await generateMcpToken(hostUrl);
102
104
  const updatedStore = { ...store, [hostUrl]: mcpToken };
103
105
  saveMcpTokenStore(updatedStore);
106
+ logger.info(`MCP token generated and cached for ${hostUrl}`);
104
107
  return mcpToken;
105
108
  }
106
109
  function clearMcpToken(hostUrl) {
@@ -118,6 +121,7 @@ function setMcpTokenManual(token, hostUrl) {
118
121
  const store = loadMcpTokenStore();
119
122
  store[hostUrl] = token;
120
123
  saveMcpTokenStore(store);
124
+ logger.info(`MCP token manually set for ${hostUrl}`);
121
125
  }
122
126
  async function validateMcpToken(token, hostUrl) {
123
127
  const mapping = getMcpMapping("analysis");
@@ -180,8 +184,10 @@ async function mcpRequest(url, method, params = {}, hostOverride) {
180
184
  body: JSON.stringify(body)
181
185
  });
182
186
  if (!resp.ok) {
187
+ logger.warn(`MCP request failed (HTTP ${resp.status}) for ${hostUrl}, trying fallback`);
183
188
  const migrated = forceMigrateFromFallback();
184
189
  if (migrated && migrated[hostUrl]) {
190
+ logger.info(`MCP token refreshed from fallback for ${hostUrl}`);
185
191
  process.stderr.write(`[ae-cli] MCP token refreshed from fallback for ${hostUrl}
186
192
  `);
187
193
  headers["mcp-token"] = migrated[hostUrl];
@@ -212,7 +218,11 @@ async function callMcpTool(url, toolName, args = {}, hostOverride) {
212
218
  name: toolName,
213
219
  arguments: args
214
220
  };
221
+ logger.info(`MCP tool call: ${toolName}`);
215
222
  const result = await mcpRequest(url, "tools/call", params, hostOverride);
223
+ if (result.isError) {
224
+ logger.warn(`MCP tool call failed: ${toolName}`);
225
+ }
216
226
  return {
217
227
  content: result.content.map((item) => {
218
228
  if (item.type === "text") {
@@ -6,9 +6,9 @@ import {
6
6
  queryReportData,
7
7
  querySql,
8
8
  wsQuery
9
- } from "./chunk-NBFTPIAH.js";
10
- import "./chunk-24ZKQWG2.js";
11
- import "./chunk-TMMUBSKW.js";
9
+ } from "./chunk-MZW6NOVS.js";
10
+ import "./chunk-47MTN54I.js";
11
+ import "./chunk-DC66L5AM.js";
12
12
  export {
13
13
  httpDelete,
14
14
  httpGet,
@@ -4,14 +4,14 @@ import {
4
4
  getToken,
5
5
  loadToken,
6
6
  validateToken
7
- } from "./chunk-24ZKQWG2.js";
7
+ } from "./chunk-47MTN54I.js";
8
8
  import {
9
9
  addHost,
10
10
  listHosts,
11
11
  removeHost,
12
12
  setActiveHost,
13
13
  updateHostLabel
14
- } from "./chunk-TMMUBSKW.js";
14
+ } from "./chunk-DC66L5AM.js";
15
15
 
16
16
  // src/commands/config.ts
17
17
  import * as readline from "readline";
package/dist/index.js CHANGED
@@ -1,11 +1,12 @@
1
1
  import {
2
2
  printError,
3
3
  printOutput
4
- } from "./chunk-KM57HI5B.js";
4
+ } from "./chunk-JJCRURTR.js";
5
5
  import {
6
6
  getActiveHost,
7
+ logger,
7
8
  safeJsonParse
8
- } from "./chunk-TMMUBSKW.js";
9
+ } from "./chunk-DC66L5AM.js";
9
10
 
10
11
  // src/index.ts
11
12
  import { Command as CommanderCommand } from "commander";
@@ -16,6 +17,8 @@ import * as readline from "readline";
16
17
  async function runCommand(cmd, opts, globalOpts) {
17
18
  try {
18
19
  const ctx = createRuntimeContext(cmd, opts, globalOpts);
20
+ const cmdName = `${cmd.service} ${cmd.command}`;
21
+ logger.command(cmdName, opts);
19
22
  for (const flag of cmd.flags) {
20
23
  if (flag.required) {
21
24
  const val = opts[camelCase(flag.name)];
@@ -48,6 +51,7 @@ async function runCommand(cmd, opts, globalOpts) {
48
51
  ctx.out(result);
49
52
  } catch (err) {
50
53
  const message = err.message || String(err);
54
+ logger.error(`Command failed: ${message}`);
51
55
  if (message.includes("token") || message.includes("auth") || message.includes("401") || message.includes("403")) {
52
56
  printError("auth", message, "Run: ae-cli auth login");
53
57
  } else if (message.includes("AE API error")) {
@@ -62,7 +66,7 @@ function createRuntimeContext(cmd, opts, globalOpts) {
62
66
  let _clientModule = null;
63
67
  async function getClient() {
64
68
  if (!_clientModule) {
65
- _clientModule = await import("./client-7S23DVHH.js");
69
+ _clientModule = await import("./client-TDH6MUWM.js");
66
70
  }
67
71
  return _clientModule;
68
72
  }
@@ -88,12 +92,12 @@ function createRuntimeContext(cmd, opts, globalOpts) {
88
92
  process.exit(1);
89
93
  }
90
94
  },
91
- async api(method, path, params, data) {
95
+ async api(method, path2, params, data) {
92
96
  const client = await getClient();
93
97
  if (method.toUpperCase() === "GET") {
94
- return client.httpGet(path, params, ctx.host());
98
+ return client.httpGet(path2, params, ctx.host());
95
99
  } else {
96
- return client.httpPost(path, params, data, ctx.host());
100
+ return client.httpPost(path2, params, data, ctx.host());
97
101
  }
98
102
  },
99
103
  async querySql(projectId, sql) {
@@ -105,7 +109,7 @@ function createRuntimeContext(cmd, opts, globalOpts) {
105
109
  return client.queryReportData(projectId, reportId, qp, eventModel, options, ctx.host());
106
110
  },
107
111
  async token() {
108
- const { getToken } = await import("./auth-YM7OI23X.js");
112
+ const { getToken } = await import("./auth-4OPL4YWG.js");
109
113
  return getToken(ctx.host());
110
114
  },
111
115
  host() {
@@ -202,50 +206,192 @@ function extractGlobalOptions(program2) {
202
206
  };
203
207
  }
204
208
 
209
+ // src/core/update-check.ts
210
+ import fs from "fs";
211
+ import path from "path";
212
+ var CONFIG_DIR = path.join(process.env.HOME || "", ".ae-cli");
213
+ var CACHE_FILE = path.join(CONFIG_DIR, "update-check.json");
214
+ var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
215
+ var FETCH_TIMEOUT_MS = 3e3;
216
+ var REGISTRY_BY_PACKAGE = {
217
+ "@thinkingai/ae-cli": "https://registry.npmjs.org"
218
+ };
219
+ var UPDATE_CHECK_PACKAGE = "@thinkingai/ae-cli";
220
+ function isUpdateCheckEnabled(packageName) {
221
+ return packageName === UPDATE_CHECK_PACKAGE;
222
+ }
223
+ var DEFAULT_REGISTRY = "https://registry.npmjs.org";
224
+ function parseVersion(version2) {
225
+ const core = version2.trim().split("-")[0];
226
+ return core.split(".").map((part) => {
227
+ const n = Number.parseInt(part, 10);
228
+ return Number.isFinite(n) ? n : 0;
229
+ });
230
+ }
231
+ function isNewer(latest, current) {
232
+ const a = parseVersion(latest);
233
+ const b = parseVersion(current);
234
+ const len = Math.max(a.length, b.length, 3);
235
+ for (let i = 0; i < len; i += 1) {
236
+ const av = a[i] ?? 0;
237
+ const bv = b[i] ?? 0;
238
+ if (av > bv) return true;
239
+ if (av < bv) return false;
240
+ }
241
+ return false;
242
+ }
243
+ function resolveRegistry(packageName) {
244
+ if (REGISTRY_BY_PACKAGE[packageName]) {
245
+ return REGISTRY_BY_PACKAGE[packageName];
246
+ }
247
+ const envRegistry = process.env.npm_config_registry?.replace(/\/$/, "");
248
+ return envRegistry || DEFAULT_REGISTRY;
249
+ }
250
+ function buildInstallCommand(packageName, registry) {
251
+ const base = `npm install -g ${packageName}@latest`;
252
+ if (registry === DEFAULT_REGISTRY) {
253
+ return base;
254
+ }
255
+ return `${base} --registry=${registry}`;
256
+ }
257
+ function shouldSkipUpdateCheck(argv = process.argv) {
258
+ if (process.env.AE_CLI_NO_UPDATE_CHECK === "1") return true;
259
+ if (process.env.CI === "true" || process.env.CI === "1") return true;
260
+ return argv.slice(2).some((arg) => {
261
+ if (arg === "--no-update-check") return true;
262
+ if (arg === "--version" || arg === "-V" || arg === "--help" || arg === "-h") return true;
263
+ return false;
264
+ });
265
+ }
266
+ function ensureConfigDir() {
267
+ if (!fs.existsSync(CONFIG_DIR)) {
268
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
269
+ }
270
+ }
271
+ function readCache() {
272
+ try {
273
+ if (!fs.existsSync(CACHE_FILE)) return null;
274
+ const raw = fs.readFileSync(CACHE_FILE, "utf8");
275
+ const data = JSON.parse(raw);
276
+ if (!data.packageName || !data.latestVersion || !data.lastCheckedAt) return null;
277
+ return data;
278
+ } catch {
279
+ return null;
280
+ }
281
+ }
282
+ function writeCache(cache) {
283
+ try {
284
+ ensureConfigDir();
285
+ fs.writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2));
286
+ } catch {
287
+ }
288
+ }
289
+ function cacheIsStale(cache, packageName) {
290
+ if (!cache || cache.packageName !== packageName) return true;
291
+ const age = Date.now() - new Date(cache.lastCheckedAt).getTime();
292
+ return age >= CHECK_INTERVAL_MS;
293
+ }
294
+ function printUpdateNotice(current, latest, installCommand) {
295
+ process.stderr.write(`[ae-cli] Update available: ${current} \u2192 ${latest}
296
+ `);
297
+ process.stderr.write(` Run: ${installCommand}
298
+ `);
299
+ }
300
+ async function fetchLatestVersion(packageName, registry) {
301
+ const url = `${registry.replace(/\/$/, "")}/${encodeURIComponent(packageName)}`;
302
+ const controller = new AbortController();
303
+ const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
304
+ try {
305
+ const resp = await fetch(url, {
306
+ signal: controller.signal,
307
+ headers: { Accept: "application/json" }
308
+ });
309
+ if (!resp.ok) return null;
310
+ const data = await resp.json();
311
+ const latest = data["dist-tags"]?.latest;
312
+ return typeof latest === "string" && latest.trim() ? latest.trim() : null;
313
+ } catch {
314
+ return null;
315
+ } finally {
316
+ clearTimeout(timer);
317
+ }
318
+ }
319
+ async function refreshCache(pkg2, registry) {
320
+ const latest = await fetchLatestVersion(pkg2.name, registry);
321
+ if (!latest) return readCache();
322
+ const cache = {
323
+ packageName: pkg2.name,
324
+ currentVersion: pkg2.version,
325
+ latestVersion: latest,
326
+ lastCheckedAt: (/* @__PURE__ */ new Date()).toISOString()
327
+ };
328
+ writeCache(cache);
329
+ return cache;
330
+ }
331
+ function maybeNotifyFromCache(pkg2, cache, registry) {
332
+ if (!cache || cache.packageName !== pkg2.name) return;
333
+ if (!isNewer(cache.latestVersion, pkg2.version)) return;
334
+ printUpdateNotice(pkg2.version, cache.latestVersion, buildInstallCommand(pkg2.name, registry));
335
+ }
336
+ function notifyIfUpdateAvailable(pkg2) {
337
+ if (!isUpdateCheckEnabled(pkg2.name)) return;
338
+ if (shouldSkipUpdateCheck()) return;
339
+ const registry = resolveRegistry(pkg2.name);
340
+ const cache = readCache();
341
+ maybeNotifyFromCache(pkg2, cache, registry);
342
+ if (!cacheIsStale(cache, pkg2.name)) return;
343
+ void refreshCache(pkg2, registry).then((fresh) => {
344
+ if (!fresh) return;
345
+ if (cache && fresh.latestVersion === cache.latestVersion) return;
346
+ maybeNotifyFromCache(pkg2, fresh, registry);
347
+ });
348
+ }
349
+
205
350
  // src/index.ts
206
351
  var require2 = createRequire(import.meta.url);
207
- var { version } = require2("../package.json");
352
+ var pkg = require2("../package.json");
353
+ var { version } = pkg;
208
354
  var program = new CommanderCommand();
209
- program.name("ae-cli").version(version).description("CLI tool for ThinkingAI (AE) analytics platform").option("--host <url>", "Override active AE host URL (e.g., https://ta.thinkingdata.cn)").option("--mcp-url <url>", "Override MCP server URL (e.g., http://localhost/mcp/http/example)").option("--format <format>", "Output format: json | table", "json").option("--jq <expr>", "jq filter expression").option("--dry-run", "Show request details without executing", false).option("--yes", "Skip confirmation for write operations", false);
355
+ program.name("ae-cli").version(version).description("CLI tool for ThinkingAI (AE) analytics platform").option("--host <url>", "Override active AE host URL (e.g., https://ta.thinkingdata.cn)").option("--mcp-url <url>", "Override MCP server URL (e.g., http://localhost/mcp/http/example)").option("--format <format>", "Output format: json | table", "json").option("--jq <expr>", "jq filter expression").option("--dry-run", "Show request details without executing", false).option("--yes", "Skip confirmation for write operations", false).option("--no-update-check", "Skip checking for newer ae-cli versions", false);
210
356
  async function loadCommands() {
211
357
  const commands = [];
212
358
  try {
213
- const teAnalysis = await import("./te-analysis-RQST6AT3.js");
359
+ const teAnalysis = await import("./te-analysis-3KFSNUCJ.js");
214
360
  commands.push(...teAnalysis.default);
215
361
  } catch {
216
362
  }
217
363
  try {
218
- const teAudience = await import("./te-audience-TJ74UJI3.js");
364
+ const teAudience = await import("./te-audience-GMYJFCSC.js");
219
365
  commands.push(...teAudience.default);
220
366
  } catch {
221
367
  }
222
368
  try {
223
- const teMeta = await import("./te-meta-25SLZFXJ.js");
369
+ const teMeta = await import("./te-meta-3EFWLM3A.js");
224
370
  commands.push(...teMeta.default);
225
371
  } catch {
226
372
  }
227
373
  try {
228
- const teCommon = await import("./te-common-W5URM2SH.js");
374
+ const teCommon = await import("./te-common-YLERCMQI.js");
229
375
  commands.push(...teCommon.default);
230
376
  } catch {
231
377
  }
232
378
  try {
233
- const engage = await import("./te-engage-57UKFV74.js");
379
+ const engage = await import("./te-engage-ROJVZHDA.js");
234
380
  commands.push(...engage.default);
235
381
  } catch {
236
382
  }
237
383
  try {
238
- const community = await import("./te-community-YRV2VAD4.js");
384
+ const community = await import("./te-community-PYDN5A6B.js");
239
385
  commands.push(...community.default);
240
386
  } catch {
241
387
  }
242
388
  try {
243
- const dataops = await import("./te-dataops-ZPH72BCK.js");
389
+ const dataops = await import("./te-dataops-ETWS5O3X.js");
244
390
  commands.push(...dataops.default);
245
391
  } catch {
246
392
  }
247
393
  try {
248
- const teKb = await import("./te-kb-GD7SJRYB.js");
394
+ const teKb = await import("./te-kb-VUNS2CC7.js");
249
395
  commands.push(...teKb.default);
250
396
  } catch {
251
397
  }
@@ -253,26 +399,27 @@ async function loadCommands() {
253
399
  }
254
400
  async function registerAuthCommands() {
255
401
  try {
256
- const { registerAuth } = await import("./auth-5S7SPPEJ.js");
402
+ const { registerAuth } = await import("./auth-PBPAZQWP.js");
257
403
  registerAuth(program);
258
404
  } catch {
259
405
  }
260
406
  }
261
407
  async function registerConfigCommands() {
262
408
  try {
263
- const { registerConfig } = await import("./config-RVUESJRF.js");
409
+ const { registerConfig } = await import("./config-5ELRYAIH.js");
264
410
  registerConfig(program);
265
411
  } catch {
266
412
  }
267
413
  }
268
414
  async function registerApiCommand() {
269
415
  try {
270
- const { registerApi } = await import("./raw-5SYAUCJQ.js");
416
+ const { registerApi } = await import("./raw-EM7AP3RT.js");
271
417
  registerApi(program);
272
418
  } catch {
273
419
  }
274
420
  }
275
421
  async function main() {
422
+ notifyIfUpdateAvailable({ name: pkg.name, version: pkg.version });
276
423
  const commands = await loadCommands();
277
424
  registerCommands(program, commands);
278
425
  await registerAuthCommands();
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  printError,
3
3
  printOutput
4
- } from "./chunk-KM57HI5B.js";
4
+ } from "./chunk-JJCRURTR.js";
5
5
  import {
6
6
  httpGet,
7
7
  httpPost
8
- } from "./chunk-NBFTPIAH.js";
8
+ } from "./chunk-MZW6NOVS.js";
9
9
  import {
10
10
  resolveHost
11
- } from "./chunk-24ZKQWG2.js";
11
+ } from "./chunk-47MTN54I.js";
12
12
  import {
13
13
  safeJsonParse
14
- } from "./chunk-TMMUBSKW.js";
14
+ } from "./chunk-DC66L5AM.js";
15
15
 
16
16
  // src/api/raw.ts
17
17
  function registerApi(program) {
@@ -2,9 +2,9 @@ import {
2
2
  callMcpTool,
3
3
  parseMcpResult,
4
4
  resolveMcpUrl
5
- } from "./chunk-MPFTXJFG.js";
6
- import "./chunk-24ZKQWG2.js";
7
- import "./chunk-TMMUBSKW.js";
5
+ } from "./chunk-OVHL4JAC.js";
6
+ import "./chunk-47MTN54I.js";
7
+ import "./chunk-DC66L5AM.js";
8
8
 
9
9
  // src/commands/te-analysis/shared.ts
10
10
  function createMcpCommand(config) {
@@ -459,6 +459,24 @@ var updatePublicAccessLink = createMcpCommand({
459
459
  })
460
460
  });
461
461
 
462
+ // src/commands/te-analysis/dashboard/create-space.ts
463
+ var createSpace = createMcpCommand({
464
+ command: "+create_space",
465
+ description: "Create a new space in a project. A space organizes dashboards and assets under a named container.",
466
+ flags: [
467
+ { name: "project_id", type: "number", required: true, desc: "Project ID", alias: "p" },
468
+ { name: "space_name", type: "string", required: true, desc: "Space name (1-64 characters)" },
469
+ { name: "space_desc", type: "string", required: false, desc: "Optional space description (max 200 characters)" }
470
+ ],
471
+ risk: "write",
472
+ mcpService: "te_analysis_extend",
473
+ buildArgs: (ctx) => ({
474
+ projectId: ctx.num("project_id"),
475
+ spaceName: ctx.str("space_name"),
476
+ spaceDesc: optionalString(ctx, "space_desc")
477
+ })
478
+ });
479
+
462
480
  // src/commands/te-analysis/dashboard/index.ts
463
481
  var commands3 = [
464
482
  listDashboards,
@@ -469,7 +487,8 @@ var commands3 = [
469
487
  createOrUpdateDashboardNote,
470
488
  listPublicAccessLinks,
471
489
  createPublicAccessLink,
472
- updatePublicAccessLink
490
+ updatePublicAccessLink,
491
+ createSpace
473
492
  ];
474
493
  var dashboard_default = commands3;
475
494
 
@@ -2,9 +2,9 @@ import {
2
2
  callMcpTool,
3
3
  parseMcpResult,
4
4
  resolveMcpUrl
5
- } from "./chunk-MPFTXJFG.js";
6
- import "./chunk-24ZKQWG2.js";
7
- import "./chunk-TMMUBSKW.js";
5
+ } from "./chunk-OVHL4JAC.js";
6
+ import "./chunk-47MTN54I.js";
7
+ import "./chunk-DC66L5AM.js";
8
8
 
9
9
  // src/commands/te-audience/shared.ts
10
10
  function createMcpCommand(config) {
@@ -2,9 +2,9 @@ import {
2
2
  callMcpTool,
3
3
  parseMcpResult,
4
4
  resolveMcpUrl
5
- } from "./chunk-MPFTXJFG.js";
6
- import "./chunk-24ZKQWG2.js";
7
- import "./chunk-TMMUBSKW.js";
5
+ } from "./chunk-OVHL4JAC.js";
6
+ import "./chunk-47MTN54I.js";
7
+ import "./chunk-DC66L5AM.js";
8
8
 
9
9
  // src/commands/te-common/shared.ts
10
10
  function createMcpCommand(config) {
@@ -3,9 +3,9 @@ import {
3
3
  parseMcpResult,
4
4
  registerMcpMappings,
5
5
  resolveMcpUrl
6
- } from "./chunk-MPFTXJFG.js";
7
- import "./chunk-24ZKQWG2.js";
8
- import "./chunk-TMMUBSKW.js";
6
+ } from "./chunk-OVHL4JAC.js";
7
+ import "./chunk-47MTN54I.js";
8
+ import "./chunk-DC66L5AM.js";
9
9
 
10
10
  // src/commands/te-community/get_channel_info.ts
11
11
  var getChannelInfo = {
@@ -3,9 +3,9 @@ import {
3
3
  parseMcpResult,
4
4
  registerMcpMappings,
5
5
  resolveMcpUrl
6
- } from "./chunk-MPFTXJFG.js";
7
- import "./chunk-24ZKQWG2.js";
8
- import "./chunk-TMMUBSKW.js";
6
+ } from "./chunk-OVHL4JAC.js";
7
+ import "./chunk-47MTN54I.js";
8
+ import "./chunk-DC66L5AM.js";
9
9
 
10
10
  // src/commands/te-dataops/datatable/list-tables-by-page.ts
11
11
  var listTablesByPage = {
@@ -3,9 +3,9 @@ import {
3
3
  parseMcpResult,
4
4
  registerMcpMappings,
5
5
  resolveMcpUrl
6
- } from "./chunk-MPFTXJFG.js";
7
- import "./chunk-24ZKQWG2.js";
8
- import "./chunk-TMMUBSKW.js";
6
+ } from "./chunk-OVHL4JAC.js";
7
+ import "./chunk-47MTN54I.js";
8
+ import "./chunk-DC66L5AM.js";
9
9
 
10
10
  // src/commands/te-engage/utils.ts
11
11
  function buildMcpDryRun(ctx, serviceName44, toolName44, args) {
@@ -1,22 +1,17 @@
1
1
  import {
2
2
  httpDelete,
3
3
  httpUpload
4
- } from "./chunk-NBFTPIAH.js";
5
- import "./chunk-24ZKQWG2.js";
6
- import "./chunk-TMMUBSKW.js";
4
+ } from "./chunk-MZW6NOVS.js";
5
+ import "./chunk-47MTN54I.js";
6
+ import "./chunk-DC66L5AM.js";
7
7
 
8
8
  // src/commands/te-kb/query.ts
9
9
  var API_PATH = "/agent/api/external/knowledge-bases/query";
10
10
  function buildBody(ctx) {
11
- const body = {
11
+ return {
12
12
  query: ctx.str("query"),
13
13
  sources: ctx.json("sources")
14
14
  };
15
- const modelId = ctx.str("model-id");
16
- if (modelId) body.modelId = modelId;
17
- const maxTurnsRaw = ctx.str("max-turns");
18
- if (maxTurnsRaw !== "") body.maxTurns = ctx.num("max-turns");
19
- return body;
20
15
  }
21
16
  var query = {
22
17
  service: "kb",
@@ -24,9 +19,7 @@ var query = {
24
19
  description: "Query knowledge.",
25
20
  flags: [
26
21
  { name: "query", type: "string", required: true, alias: "q", desc: "Natural language question to query against the knowledge bases" },
27
- { name: "sources", type: "json", required: true, desc: 'JSON array of knowledge base refs, e.g. [{"scope":"company","name":"engineering-handbook"}]' },
28
- { name: "model-id", type: "string", required: false, default: "AE-Auto", desc: "Model identifier (default: AE-Auto)" },
29
- { name: "max-turns", type: "number", required: false, default: 6, desc: "Maximum reasoning turns (default: 6)" }
22
+ { name: "sources", type: "json", required: true, desc: 'JSON array of knowledge base refs, e.g. [{"scope":"company","name":"engineering-handbook"}]' }
30
23
  ],
31
24
  risk: "read",
32
25
  dryRun: (ctx) => ({
@@ -2,9 +2,9 @@ import {
2
2
  callMcpTool,
3
3
  parseMcpResult,
4
4
  resolveMcpUrl
5
- } from "./chunk-MPFTXJFG.js";
6
- import "./chunk-24ZKQWG2.js";
7
- import "./chunk-TMMUBSKW.js";
5
+ } from "./chunk-OVHL4JAC.js";
6
+ import "./chunk-47MTN54I.js";
7
+ import "./chunk-DC66L5AM.js";
8
8
 
9
9
  // src/commands/te-meta/shared.ts
10
10
  function createMcpCommand(config) {
@@ -533,8 +533,28 @@ var listEntities = createMcpCommand({
533
533
  })
534
534
  });
535
535
 
536
+ // src/commands/te-meta/entity/create-entity.ts
537
+ var createEntity = createMcpCommand({
538
+ command: "+create_entity",
539
+ description: "Create a new entity that links an event or user property as an analysis dimension.",
540
+ flags: [
541
+ { name: "project_id", type: "number", required: true, desc: "Project ID", alias: "p" },
542
+ { name: "entity_name", type: "string", required: true, desc: "Entity name" },
543
+ { name: "column_name", type: "string", required: true, desc: "Property column name to associate with the entity" },
544
+ { name: "table_type", type: "number", required: true, desc: "Table type: 0 = event property, 1 = user property" }
545
+ ],
546
+ risk: "write",
547
+ mcpService: "te_analysis_extend",
548
+ buildArgs: (ctx) => ({
549
+ projectId: ctx.num("project_id"),
550
+ entityName: ctx.str("entity_name"),
551
+ columnName: ctx.str("column_name"),
552
+ tableType: ctx.num("table_type")
553
+ })
554
+ });
555
+
536
556
  // src/commands/te-meta/entity/index.ts
537
- var commands3 = [listEntities];
557
+ var commands3 = [listEntities, createEntity];
538
558
  var entity_default = commands3;
539
559
 
540
560
  // src/commands/te-meta/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thinkingai/ae-cli",
3
- "version": "1.0.22",
3
+ "version": "1.0.24",
4
4
  "description": "CLI tool for ThinkingAI (AE) analytics platform",
5
5
  "type": "module",
6
6
  "bin": {
@@ -21,6 +21,7 @@
21
21
  "verify:analysis-audience-tools": "node scripts/verify-te-audience-tools.mjs",
22
22
  "verify:analysis-meta-tools": "node scripts/verify-te-meta-tools.mjs",
23
23
  "verify:analysis-common-tools": "node scripts/verify-te-common-tools.mjs",
24
+ "verify:update-check": "npx tsx test/update-check.test.mjs",
24
25
  "prepublishOnly": "npm run build",
25
26
  "start": "node dist/index.js",
26
27
  "test": "npx tsx src/index.ts --help"
@@ -0,0 +1,39 @@
1
+ # analysis_meta +create_entity (Create Entity)
2
+
3
+ > **Prerequisite:** Follow the Global AE CLI Rules in [`../SKILL.md`](../SKILL.md).
4
+
5
+ Domain: **Entity Management**
6
+
7
+ ## Use Cases
8
+ - Create a new entity that links an event property or user property as an analysis dimension.
9
+ - Use `+list_entities` first to confirm the entity does not already exist.
10
+
11
+ ## Commands
12
+ ```bash
13
+ ae-cli analysis_meta +create_entity --project_id <project_id> --entity_name "User Entity" --column_name user_id --table_type 1
14
+ ae-cli analysis_meta +create_entity --project_id <project_id> --entity_name "Event Entity" --column_name event_col --table_type 0
15
+ ae-cli analysis_meta +create_entity --dry-run
16
+ ```
17
+
18
+ ## Parameters
19
+ | Parameter | Required | Description |
20
+ |---|---|---|
21
+ | `--project_id` / `-p` | Yes | Project ID |
22
+ | `--entity_name` | Yes | Entity name |
23
+ | `--column_name` | Yes | Property column name to associate with the entity |
24
+ | `--table_type` | Yes | Table type: `0` = event property, `1` = user property |
25
+
26
+ ## Decision Rules
27
+ - `--table_type` must be `0` (event property) or `1` (user property); no other values are accepted.
28
+ - Before using `--column_name`, call `analysis_meta +list_properties` to verify it exists. If the same column name appears in **both** event properties and user properties, **stop and ask the user which table type to use** — do not guess `--table_type`. Ask every time this condition is met; never carry over the answer from a previous request.
29
+ - For the first run, pass only the required parameters and confirm before adding others.
30
+ - Write operations keep the confirmation prompt by default; use `--yes` only for automation.
31
+
32
+ ## Next Steps After Failure
33
+ - If `PROJECT_ID_REQUIRED` or `ENTITY_NAME_REQUIRED` appears, check that all required parameters are provided.
34
+ - If `INVALID_TABLE_TYPE` appears, verify `--table_type` is exactly `0` or `1`.
35
+ - If the result after writing is not as expected, use `analysis_meta +list_entities` to compare before and after.
36
+
37
+ ## Recommended Chaining
38
+ - `analysis_meta +list_entities` → `+create_entity`
39
+ - `analysis_meta +list_properties` → `+create_entity`
@@ -0,0 +1,34 @@
1
+ # analysis +create_space (Create Space)
2
+
3
+ > **Prerequisite:** Follow the Global AE CLI Rules in [`../SKILL.md`](../SKILL.md).
4
+
5
+ Domain: **Space Management**
6
+
7
+ ## Use Cases
8
+ - Create a new space in a project to organize dashboards and assets under a named container.
9
+ - Returns basic space information (ID, name, avatar settings).
10
+
11
+ ## Commands
12
+ ```bash
13
+ ae-cli analysis +create_space --project_id <project_id> --space_name "My Space"
14
+ ae-cli analysis +create_space --project_id <project_id> --space_name "Team Space" --space_desc "Team dashboards"
15
+ ae-cli analysis +create_space --dry-run
16
+ ```
17
+
18
+ ## Parameters
19
+ | Parameter | Required | Description |
20
+ |---|---|---|
21
+ | `--project_id` / `-p` | Yes | Project ID |
22
+ | `--space_name` | Yes | Space name (1-64 characters) |
23
+ | `--space_desc` | No | Optional space description (max 200 characters) |
24
+
25
+ ## Decision Rules
26
+ - `--space_name` must be between 1 and 64 characters.
27
+ - Write operations keep the confirmation prompt by default; use `--yes` only for automation.
28
+
29
+ ## Next Steps After Failure
30
+ - If `SPACE_NAME_TOO_LONG` appears, shorten `--space_name` to 64 characters or fewer.
31
+ - If the result after writing is not as expected, use `analysis +list_dashboards` to inspect the space context.
32
+
33
+ ## Recommended Chaining
34
+ - `+create_space` → `+create_dashboard` (create dashboard inside the new space using `--space_id`)