@thinkingai/ae-cli 1.0.20 → 1.0.22
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-A4NV2VHD.js → auth-5S7SPPEJ.js} +12 -2
- package/dist/{auth-T3ILGJKW.js → auth-YM7OI23X.js} +1 -1
- package/dist/{chunk-OVMQFFC2.js → chunk-24ZKQWG2.js} +39 -4
- package/dist/{chunk-5CCFSPAF.js → chunk-MPFTXJFG.js} +1 -1
- package/dist/{chunk-TOXRLDUP.js → chunk-NBFTPIAH.js} +31 -1
- package/dist/{client-FPA76DGY.js → client-7S23DVHH.js} +6 -2
- package/dist/{config-QZIEYXQZ.js → config-RVUESJRF.js} +1 -1
- package/dist/index.js +13 -13
- package/dist/{raw-MZIKHQH4.js → raw-5SYAUCJQ.js} +2 -2
- package/dist/{te-analysis-CFQBXUCO.js → te-analysis-RQST6AT3.js} +2 -2
- package/dist/{te-audience-WPBBWADL.js → te-audience-TJ74UJI3.js} +2 -2
- package/dist/{te-common-GL3KBZPD.js → te-common-W5URM2SH.js} +2 -2
- package/dist/{te-community-3XEJNSQL.js → te-community-YRV2VAD4.js} +2 -2
- package/dist/{te-dataops-H4WSMCG5.js → te-dataops-ZPH72BCK.js} +2 -2
- package/dist/{te-engage-2VFM37ZM.js → te-engage-57UKFV74.js} +2 -2
- package/dist/te-kb-GD7SJRYB.js +407 -0
- package/dist/{te-meta-BO45GW32.js → te-meta-25SLZFXJ.js} +2 -2
- package/package.json +3 -1
- package/dist/te-kb-U6SQUJVC.js +0 -41
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
loadMcpTokenStore,
|
|
8
8
|
setMcpTokenManual,
|
|
9
9
|
validateMcpToken
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-MPFTXJFG.js";
|
|
11
11
|
import {
|
|
12
12
|
clearToken,
|
|
13
13
|
getAuthStatus,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
resolveHost,
|
|
16
16
|
setTokenManual,
|
|
17
17
|
validateToken
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-24ZKQWG2.js";
|
|
19
19
|
import {
|
|
20
20
|
loadConfig,
|
|
21
21
|
saveConfig
|
|
@@ -30,6 +30,16 @@ function registerAuth(program) {
|
|
|
30
30
|
printError("config", "No AE host configured.", "Run: ae-cli config set-host");
|
|
31
31
|
process.exit(1);
|
|
32
32
|
}
|
|
33
|
+
const config = loadConfig();
|
|
34
|
+
if (!config.hosts[host]) {
|
|
35
|
+
config.hosts[host] = { label: host };
|
|
36
|
+
if (!config.activeHost) {
|
|
37
|
+
config.activeHost = host;
|
|
38
|
+
}
|
|
39
|
+
saveConfig(config);
|
|
40
|
+
process.stderr.write(`[ae-cli] Config saved for ${host}
|
|
41
|
+
`);
|
|
42
|
+
}
|
|
33
43
|
try {
|
|
34
44
|
const token = await getToken(host);
|
|
35
45
|
process.stderr.write(`[ae-cli] Authenticated to ${host}
|
|
@@ -140,6 +140,37 @@ function extractTokenViaOsascript(hostUrl) {
|
|
|
140
140
|
return { token: null, error: "no_tab" };
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
|
+
function extractTokenFromAllTabs() {
|
|
144
|
+
const lines = [
|
|
145
|
+
'tell application "Google Chrome"',
|
|
146
|
+
" repeat with w in windows",
|
|
147
|
+
" repeat with t in tabs of w",
|
|
148
|
+
" try",
|
|
149
|
+
` set tokenVal to execute t javascript "localStorage.getItem('ACCESS_TOKEN')"`,
|
|
150
|
+
' if tokenVal is not null and tokenVal is not missing value and tokenVal is not "" then',
|
|
151
|
+
" return tokenVal",
|
|
152
|
+
" end if",
|
|
153
|
+
" end try",
|
|
154
|
+
" end repeat",
|
|
155
|
+
" end repeat",
|
|
156
|
+
' return "NO_TOKEN_FOUND"',
|
|
157
|
+
"end tell"
|
|
158
|
+
];
|
|
159
|
+
try {
|
|
160
|
+
const args = lines.flatMap((line) => ["-e", line]);
|
|
161
|
+
const result = execFileSync("osascript", args, { encoding: "utf8", timeout: 1e4 }).trim();
|
|
162
|
+
if (result === "NO_TOKEN_FOUND" || !result || result === "missing value") {
|
|
163
|
+
return { token: null, error: "no_token" };
|
|
164
|
+
}
|
|
165
|
+
return { token: result.replace(/^["']|["']$/g, ""), error: null };
|
|
166
|
+
} catch (e) {
|
|
167
|
+
const msg = e.message || "";
|
|
168
|
+
if (msg.includes("not allowed") || msg.includes("assistive access") || msg.includes("(-1743)")) {
|
|
169
|
+
return { token: null, error: "no_js_permission" };
|
|
170
|
+
}
|
|
171
|
+
return { token: null, error: "no_tab" };
|
|
172
|
+
}
|
|
173
|
+
}
|
|
143
174
|
async function requestTokenViaOsascript(hostUrl) {
|
|
144
175
|
process.stderr.write(`[ae-cli] No AE tab found in Chrome. Opening ${hostUrl} ...
|
|
145
176
|
`);
|
|
@@ -164,13 +195,17 @@ async function requestTokenViaOsascript(hostUrl) {
|
|
|
164
195
|
const deadline = Date.now() + OSASCRIPT_POLL_TIMEOUT_MS;
|
|
165
196
|
while (Date.now() < deadline) {
|
|
166
197
|
await new Promise((r) => setTimeout(r, OSASCRIPT_POLL_INTERVAL_MS));
|
|
167
|
-
|
|
168
|
-
if (
|
|
198
|
+
let result = extractTokenViaOsascript(hostUrl);
|
|
199
|
+
if (result.error === "no_tab") {
|
|
200
|
+
const fallback = extractTokenFromAllTabs();
|
|
201
|
+
if (fallback.token) result = fallback;
|
|
202
|
+
}
|
|
203
|
+
if (result.token) {
|
|
169
204
|
process.stderr.write(`[ae-cli] Token captured from Chrome for ${hostUrl}.
|
|
170
205
|
`);
|
|
171
|
-
return token;
|
|
206
|
+
return result.token;
|
|
172
207
|
}
|
|
173
|
-
if (error === "no_js_permission") return null;
|
|
208
|
+
if (result.error === "no_js_permission") return null;
|
|
174
209
|
}
|
|
175
210
|
process.stderr.write(`[ae-cli] Polling timed out after ${OSASCRIPT_POLL_TIMEOUT_MS / 1e3}s.
|
|
176
211
|
`);
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
clearToken,
|
|
3
3
|
getToken,
|
|
4
4
|
resolveHost
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-24ZKQWG2.js";
|
|
6
6
|
import {
|
|
7
7
|
safeJsonParse
|
|
8
8
|
} from "./chunk-TMMUBSKW.js";
|
|
@@ -59,6 +59,34 @@ async function httpGet(modulePath, params = {}, hostUrl) {
|
|
|
59
59
|
async function httpPost(modulePath, params = {}, body, hostUrl) {
|
|
60
60
|
return request("POST", modulePath, params, body ?? {}, true, hostUrl);
|
|
61
61
|
}
|
|
62
|
+
async function httpDelete(modulePath, params = {}, body, hostUrl) {
|
|
63
|
+
return request("DELETE", modulePath, params, body ?? null, true, hostUrl);
|
|
64
|
+
}
|
|
65
|
+
async function uploadRequest(modulePath, form, params = {}, retry = true, hostUrl) {
|
|
66
|
+
const resolvedHost = resolveHost(hostUrl);
|
|
67
|
+
const token = await getToken(resolvedHost);
|
|
68
|
+
const url = buildUrl(resolvedHost, modulePath, params);
|
|
69
|
+
const headers = {
|
|
70
|
+
"Authorization": `bearer ${token}`
|
|
71
|
+
};
|
|
72
|
+
const resp = await fetch(url, { method: "POST", headers, body: form });
|
|
73
|
+
if ((resp.status === 401 || resp.status === 403) && retry) {
|
|
74
|
+
clearToken(resolvedHost);
|
|
75
|
+
return uploadRequest(modulePath, form, params, false, resolvedHost);
|
|
76
|
+
}
|
|
77
|
+
const data = safeJsonParse(await resp.text());
|
|
78
|
+
if (data.return_code === -1001 && retry) {
|
|
79
|
+
clearToken(resolvedHost);
|
|
80
|
+
return uploadRequest(modulePath, form, params, false, resolvedHost);
|
|
81
|
+
}
|
|
82
|
+
if (data.return_code !== 0 && data.return_code !== void 0) {
|
|
83
|
+
throw new Error(`AE API error: ${data.return_message || "unknown"} (code: ${data.return_code})`);
|
|
84
|
+
}
|
|
85
|
+
return data.data !== void 0 ? data.data : data;
|
|
86
|
+
}
|
|
87
|
+
async function httpUpload(modulePath, form, params = {}, hostUrl) {
|
|
88
|
+
return uploadRequest(modulePath, form, params, true, hostUrl);
|
|
89
|
+
}
|
|
62
90
|
async function wsQueryOnce(projectId, requestId, qp, eventModel, options = {}, token, hostUrl, timeoutMs = 3e4) {
|
|
63
91
|
const wsUrl = buildWsUrl(hostUrl, token);
|
|
64
92
|
return new Promise((resolve, reject) => {
|
|
@@ -142,6 +170,8 @@ async function queryReportData(projectId, reportId, qp, eventModel, options = {}
|
|
|
142
170
|
export {
|
|
143
171
|
httpGet,
|
|
144
172
|
httpPost,
|
|
173
|
+
httpDelete,
|
|
174
|
+
httpUpload,
|
|
145
175
|
wsQuery,
|
|
146
176
|
querySql,
|
|
147
177
|
queryReportData
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
|
+
httpDelete,
|
|
2
3
|
httpGet,
|
|
3
4
|
httpPost,
|
|
5
|
+
httpUpload,
|
|
4
6
|
queryReportData,
|
|
5
7
|
querySql,
|
|
6
8
|
wsQuery
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
9
|
+
} from "./chunk-NBFTPIAH.js";
|
|
10
|
+
import "./chunk-24ZKQWG2.js";
|
|
9
11
|
import "./chunk-TMMUBSKW.js";
|
|
10
12
|
export {
|
|
13
|
+
httpDelete,
|
|
11
14
|
httpGet,
|
|
12
15
|
httpPost,
|
|
16
|
+
httpUpload,
|
|
13
17
|
queryReportData,
|
|
14
18
|
querySql,
|
|
15
19
|
wsQuery
|
package/dist/index.js
CHANGED
|
@@ -62,7 +62,7 @@ function createRuntimeContext(cmd, opts, globalOpts) {
|
|
|
62
62
|
let _clientModule = null;
|
|
63
63
|
async function getClient() {
|
|
64
64
|
if (!_clientModule) {
|
|
65
|
-
_clientModule = await import("./client-
|
|
65
|
+
_clientModule = await import("./client-7S23DVHH.js");
|
|
66
66
|
}
|
|
67
67
|
return _clientModule;
|
|
68
68
|
}
|
|
@@ -105,7 +105,7 @@ function createRuntimeContext(cmd, opts, globalOpts) {
|
|
|
105
105
|
return client.queryReportData(projectId, reportId, qp, eventModel, options, ctx.host());
|
|
106
106
|
},
|
|
107
107
|
async token() {
|
|
108
|
-
const { getToken } = await import("./auth-
|
|
108
|
+
const { getToken } = await import("./auth-YM7OI23X.js");
|
|
109
109
|
return getToken(ctx.host());
|
|
110
110
|
},
|
|
111
111
|
host() {
|
|
@@ -210,42 +210,42 @@ program.name("ae-cli").version(version).description("CLI tool for ThinkingAI (AE
|
|
|
210
210
|
async function loadCommands() {
|
|
211
211
|
const commands = [];
|
|
212
212
|
try {
|
|
213
|
-
const teAnalysis = await import("./te-analysis-
|
|
213
|
+
const teAnalysis = await import("./te-analysis-RQST6AT3.js");
|
|
214
214
|
commands.push(...teAnalysis.default);
|
|
215
215
|
} catch {
|
|
216
216
|
}
|
|
217
217
|
try {
|
|
218
|
-
const teAudience = await import("./te-audience-
|
|
218
|
+
const teAudience = await import("./te-audience-TJ74UJI3.js");
|
|
219
219
|
commands.push(...teAudience.default);
|
|
220
220
|
} catch {
|
|
221
221
|
}
|
|
222
222
|
try {
|
|
223
|
-
const teMeta = await import("./te-meta-
|
|
223
|
+
const teMeta = await import("./te-meta-25SLZFXJ.js");
|
|
224
224
|
commands.push(...teMeta.default);
|
|
225
225
|
} catch {
|
|
226
226
|
}
|
|
227
227
|
try {
|
|
228
|
-
const teCommon = await import("./te-common-
|
|
228
|
+
const teCommon = await import("./te-common-W5URM2SH.js");
|
|
229
229
|
commands.push(...teCommon.default);
|
|
230
230
|
} catch {
|
|
231
231
|
}
|
|
232
232
|
try {
|
|
233
|
-
const engage = await import("./te-engage-
|
|
233
|
+
const engage = await import("./te-engage-57UKFV74.js");
|
|
234
234
|
commands.push(...engage.default);
|
|
235
235
|
} catch {
|
|
236
236
|
}
|
|
237
237
|
try {
|
|
238
|
-
const community = await import("./te-community-
|
|
238
|
+
const community = await import("./te-community-YRV2VAD4.js");
|
|
239
239
|
commands.push(...community.default);
|
|
240
240
|
} catch {
|
|
241
241
|
}
|
|
242
242
|
try {
|
|
243
|
-
const dataops = await import("./te-dataops-
|
|
243
|
+
const dataops = await import("./te-dataops-ZPH72BCK.js");
|
|
244
244
|
commands.push(...dataops.default);
|
|
245
245
|
} catch {
|
|
246
246
|
}
|
|
247
247
|
try {
|
|
248
|
-
const teKb = await import("./te-kb-
|
|
248
|
+
const teKb = await import("./te-kb-GD7SJRYB.js");
|
|
249
249
|
commands.push(...teKb.default);
|
|
250
250
|
} catch {
|
|
251
251
|
}
|
|
@@ -253,21 +253,21 @@ async function loadCommands() {
|
|
|
253
253
|
}
|
|
254
254
|
async function registerAuthCommands() {
|
|
255
255
|
try {
|
|
256
|
-
const { registerAuth } = await import("./auth-
|
|
256
|
+
const { registerAuth } = await import("./auth-5S7SPPEJ.js");
|
|
257
257
|
registerAuth(program);
|
|
258
258
|
} catch {
|
|
259
259
|
}
|
|
260
260
|
}
|
|
261
261
|
async function registerConfigCommands() {
|
|
262
262
|
try {
|
|
263
|
-
const { registerConfig } = await import("./config-
|
|
263
|
+
const { registerConfig } = await import("./config-RVUESJRF.js");
|
|
264
264
|
registerConfig(program);
|
|
265
265
|
} catch {
|
|
266
266
|
}
|
|
267
267
|
}
|
|
268
268
|
async function registerApiCommand() {
|
|
269
269
|
try {
|
|
270
|
-
const { registerApi } = await import("./raw-
|
|
270
|
+
const { registerApi } = await import("./raw-5SYAUCJQ.js");
|
|
271
271
|
registerApi(program);
|
|
272
272
|
} catch {
|
|
273
273
|
}
|
|
@@ -5,10 +5,10 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
httpGet,
|
|
7
7
|
httpPost
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-NBFTPIAH.js";
|
|
9
9
|
import {
|
|
10
10
|
resolveHost
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-24ZKQWG2.js";
|
|
12
12
|
import {
|
|
13
13
|
safeJsonParse
|
|
14
14
|
} from "./chunk-TMMUBSKW.js";
|
|
@@ -3,8 +3,8 @@ import {
|
|
|
3
3
|
parseMcpResult,
|
|
4
4
|
registerMcpMappings,
|
|
5
5
|
resolveMcpUrl
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-MPFTXJFG.js";
|
|
7
|
+
import "./chunk-24ZKQWG2.js";
|
|
8
8
|
import "./chunk-TMMUBSKW.js";
|
|
9
9
|
|
|
10
10
|
// src/commands/te-community/get_channel_info.ts
|
|
@@ -3,8 +3,8 @@ import {
|
|
|
3
3
|
parseMcpResult,
|
|
4
4
|
registerMcpMappings,
|
|
5
5
|
resolveMcpUrl
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-MPFTXJFG.js";
|
|
7
|
+
import "./chunk-24ZKQWG2.js";
|
|
8
8
|
import "./chunk-TMMUBSKW.js";
|
|
9
9
|
|
|
10
10
|
// src/commands/te-dataops/datatable/list-tables-by-page.ts
|
|
@@ -3,8 +3,8 @@ import {
|
|
|
3
3
|
parseMcpResult,
|
|
4
4
|
registerMcpMappings,
|
|
5
5
|
resolveMcpUrl
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-MPFTXJFG.js";
|
|
7
|
+
import "./chunk-24ZKQWG2.js";
|
|
8
8
|
import "./chunk-TMMUBSKW.js";
|
|
9
9
|
|
|
10
10
|
// src/commands/te-engage/utils.ts
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
import {
|
|
2
|
+
httpDelete,
|
|
3
|
+
httpUpload
|
|
4
|
+
} from "./chunk-NBFTPIAH.js";
|
|
5
|
+
import "./chunk-24ZKQWG2.js";
|
|
6
|
+
import "./chunk-TMMUBSKW.js";
|
|
7
|
+
|
|
8
|
+
// src/commands/te-kb/query.ts
|
|
9
|
+
var API_PATH = "/agent/api/external/knowledge-bases/query";
|
|
10
|
+
function buildBody(ctx) {
|
|
11
|
+
const body = {
|
|
12
|
+
query: ctx.str("query"),
|
|
13
|
+
sources: ctx.json("sources")
|
|
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
|
+
}
|
|
21
|
+
var query = {
|
|
22
|
+
service: "kb",
|
|
23
|
+
command: "+query",
|
|
24
|
+
description: "Query knowledge.",
|
|
25
|
+
flags: [
|
|
26
|
+
{ 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)" }
|
|
30
|
+
],
|
|
31
|
+
risk: "read",
|
|
32
|
+
dryRun: (ctx) => ({
|
|
33
|
+
method: "POST",
|
|
34
|
+
url: `${ctx.host().replace(/\/$/, "")}${API_PATH}`,
|
|
35
|
+
body: buildBody(ctx)
|
|
36
|
+
}),
|
|
37
|
+
execute: async (ctx) => ctx.api("POST", API_PATH, {}, buildBody(ctx))
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// src/commands/te-kb/add.ts
|
|
41
|
+
import { promises as fs } from "fs";
|
|
42
|
+
import * as path from "path";
|
|
43
|
+
import TurndownService from "turndown";
|
|
44
|
+
var API_PATH2 = "/agent/api/external/knowledge-bases/sources/upload";
|
|
45
|
+
var MD_EXT = /* @__PURE__ */ new Set([".md", ".markdown"]);
|
|
46
|
+
function normalizeFilesInput(raw) {
|
|
47
|
+
if (!Array.isArray(raw)) {
|
|
48
|
+
throw new Error(`--files must be a JSON array of strings, e.g. '["./a.md","./docs","https://example.com/page"]'`);
|
|
49
|
+
}
|
|
50
|
+
const items = [];
|
|
51
|
+
for (const v of raw) {
|
|
52
|
+
if (typeof v !== "string") {
|
|
53
|
+
throw new Error(`--files entries must be strings (got: ${JSON.stringify(v)})`);
|
|
54
|
+
}
|
|
55
|
+
const trimmed = v.trim();
|
|
56
|
+
if (trimmed) items.push(trimmed);
|
|
57
|
+
}
|
|
58
|
+
return items;
|
|
59
|
+
}
|
|
60
|
+
function isUrl(s) {
|
|
61
|
+
return /^https?:\/\//i.test(s);
|
|
62
|
+
}
|
|
63
|
+
function classifyInput(item) {
|
|
64
|
+
return isUrl(item) ? "url" : "path";
|
|
65
|
+
}
|
|
66
|
+
function sanitizeFilename(name) {
|
|
67
|
+
const cleaned = name.replace(/[\\/:*?"<>|\s]+/g, "-").replace(/^-+|-+$/g, "");
|
|
68
|
+
return cleaned || "document";
|
|
69
|
+
}
|
|
70
|
+
async function readLocalFile(filePath) {
|
|
71
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
72
|
+
if (!MD_EXT.has(ext)) {
|
|
73
|
+
throw new Error(`Not a markdown file (only .md / .markdown allowed): ${filePath}`);
|
|
74
|
+
}
|
|
75
|
+
const content = await fs.readFile(filePath, "utf8");
|
|
76
|
+
return { filename: path.basename(filePath), content, origin: "file", source: filePath };
|
|
77
|
+
}
|
|
78
|
+
async function readDirectory(dir) {
|
|
79
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
80
|
+
const files = [];
|
|
81
|
+
for (const entry of entries) {
|
|
82
|
+
if (!entry.isFile()) continue;
|
|
83
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
84
|
+
if (!MD_EXT.has(ext)) continue;
|
|
85
|
+
const full = path.join(dir, entry.name);
|
|
86
|
+
const content = await fs.readFile(full, "utf8");
|
|
87
|
+
files.push({ filename: entry.name, content, origin: "dir", source: full });
|
|
88
|
+
}
|
|
89
|
+
if (files.length === 0) {
|
|
90
|
+
throw new Error(`No .md / .markdown files found in directory: ${dir}`);
|
|
91
|
+
}
|
|
92
|
+
return files;
|
|
93
|
+
}
|
|
94
|
+
function deriveFilenameFromUrl(rawUrl) {
|
|
95
|
+
try {
|
|
96
|
+
const u = new URL(rawUrl);
|
|
97
|
+
const lastSeg = u.pathname.split("/").filter(Boolean).pop();
|
|
98
|
+
const base = lastSeg ? lastSeg.replace(/\.[^.]+$/, "") : u.hostname;
|
|
99
|
+
return `${sanitizeFilename(base)}.md`;
|
|
100
|
+
} catch {
|
|
101
|
+
return `${sanitizeFilename(rawUrl)}.md`;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async function fetchAsMarkdown(rawUrl) {
|
|
105
|
+
const resp = await fetch(rawUrl);
|
|
106
|
+
if (!resp.ok) {
|
|
107
|
+
throw new Error(`Failed to fetch ${rawUrl}: HTTP ${resp.status}`);
|
|
108
|
+
}
|
|
109
|
+
const html = await resp.text();
|
|
110
|
+
const turndown = new TurndownService({ headingStyle: "atx", codeBlockStyle: "fenced" });
|
|
111
|
+
const markdown = turndown.turndown(html);
|
|
112
|
+
return {
|
|
113
|
+
filename: deriveFilenameFromUrl(rawUrl),
|
|
114
|
+
content: markdown,
|
|
115
|
+
origin: "url",
|
|
116
|
+
source: rawUrl
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
async function resolveLocalPath(p) {
|
|
120
|
+
const abs = path.resolve(p);
|
|
121
|
+
let stat;
|
|
122
|
+
try {
|
|
123
|
+
stat = await fs.stat(abs);
|
|
124
|
+
} catch {
|
|
125
|
+
throw new Error(`Path not found: ${p}`);
|
|
126
|
+
}
|
|
127
|
+
if (stat.isDirectory()) {
|
|
128
|
+
return readDirectory(abs);
|
|
129
|
+
}
|
|
130
|
+
if (stat.isFile()) {
|
|
131
|
+
return [await readLocalFile(abs)];
|
|
132
|
+
}
|
|
133
|
+
throw new Error(`Unsupported path (not file or directory): ${p}`);
|
|
134
|
+
}
|
|
135
|
+
async function collectFiles(ctx) {
|
|
136
|
+
const items = normalizeFilesInput(ctx.json("files"));
|
|
137
|
+
if (items.length === 0) {
|
|
138
|
+
throw new Error("--files must contain at least one entry");
|
|
139
|
+
}
|
|
140
|
+
const collected = [];
|
|
141
|
+
for (const item of items) {
|
|
142
|
+
if (classifyInput(item) === "url") {
|
|
143
|
+
collected.push(await fetchAsMarkdown(item));
|
|
144
|
+
} else {
|
|
145
|
+
collected.push(...await resolveLocalPath(item));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (collected.length === 0) {
|
|
149
|
+
throw new Error("No markdown files collected from the given inputs");
|
|
150
|
+
}
|
|
151
|
+
return dedupeByFilename(collected);
|
|
152
|
+
}
|
|
153
|
+
function dedupeByFilename(files) {
|
|
154
|
+
const used = /* @__PURE__ */ new Map();
|
|
155
|
+
return files.map((f) => {
|
|
156
|
+
const count = used.get(f.filename) ?? 0;
|
|
157
|
+
used.set(f.filename, count + 1);
|
|
158
|
+
if (count === 0) return f;
|
|
159
|
+
const ext = path.extname(f.filename);
|
|
160
|
+
const stem = f.filename.slice(0, f.filename.length - ext.length);
|
|
161
|
+
return { ...f, filename: `${stem}-${count}${ext}` };
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
function buildForm(name, files) {
|
|
165
|
+
const form = new FormData();
|
|
166
|
+
form.append("name", name);
|
|
167
|
+
for (const f of files) {
|
|
168
|
+
form.append("files", new Blob([f.content], { type: "text/markdown" }), f.filename);
|
|
169
|
+
}
|
|
170
|
+
return form;
|
|
171
|
+
}
|
|
172
|
+
var add = {
|
|
173
|
+
service: "kb",
|
|
174
|
+
command: "+add",
|
|
175
|
+
description: "Upload markdown sources to a knowledge. --files accepts a JSON array; each entry can be a .md/.markdown file path, a directory path (all .md/.markdown inside, non-recursive), or an http(s) URL (HTML auto-converted to markdown).",
|
|
176
|
+
flags: [
|
|
177
|
+
{ name: "name", type: "string", required: true, desc: "Knowledge base name" },
|
|
178
|
+
{
|
|
179
|
+
name: "files",
|
|
180
|
+
type: "json",
|
|
181
|
+
required: true,
|
|
182
|
+
desc: `JSON array of strings. Each entry can be a .md/.markdown file path, a directory path, or an http(s) URL. Example: '["./a.md","./docs","https://example.com/page"]'`
|
|
183
|
+
}
|
|
184
|
+
],
|
|
185
|
+
risk: "write",
|
|
186
|
+
validate: (ctx) => {
|
|
187
|
+
normalizeFilesInput(ctx.json("files"));
|
|
188
|
+
},
|
|
189
|
+
dryRun: (ctx) => {
|
|
190
|
+
const items = normalizeFilesInput(ctx.json("files"));
|
|
191
|
+
return {
|
|
192
|
+
method: "POST",
|
|
193
|
+
url: `${ctx.host().replace(/\/$/, "")}${API_PATH2}`,
|
|
194
|
+
body: {
|
|
195
|
+
name: ctx.str("name"),
|
|
196
|
+
files: items.map((item) => ({ value: item, type: classifyInput(item) })),
|
|
197
|
+
contentType: "multipart/form-data"
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
},
|
|
201
|
+
execute: async (ctx) => {
|
|
202
|
+
const name = ctx.str("name");
|
|
203
|
+
const files = await collectFiles(ctx);
|
|
204
|
+
const form = buildForm(name, files);
|
|
205
|
+
const result = await httpUpload(API_PATH2, form, {}, ctx.host());
|
|
206
|
+
return {
|
|
207
|
+
uploaded: files.map((f) => ({ filename: f.filename, origin: f.origin, source: f.source })),
|
|
208
|
+
result
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
// src/commands/te-kb/compile.ts
|
|
214
|
+
var API_PATH3 = "/agent/api/external/knowledge-bases/compile";
|
|
215
|
+
var VALID_MODES = /* @__PURE__ */ new Set(["incremental", "full"]);
|
|
216
|
+
function getMode(ctx) {
|
|
217
|
+
const mode = ctx.str("mode") || "incremental";
|
|
218
|
+
if (!VALID_MODES.has(mode)) {
|
|
219
|
+
throw new Error(`Invalid --mode: ${mode}. Must be one of: incremental | full`);
|
|
220
|
+
}
|
|
221
|
+
return mode;
|
|
222
|
+
}
|
|
223
|
+
function buildBody2(ctx) {
|
|
224
|
+
return {
|
|
225
|
+
name: ctx.str("name"),
|
|
226
|
+
mode: getMode(ctx)
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
var compile = {
|
|
230
|
+
service: "kb",
|
|
231
|
+
command: "+compile",
|
|
232
|
+
description: "Compile a knowledge.",
|
|
233
|
+
flags: [
|
|
234
|
+
{ name: "name", type: "string", required: true, desc: "Knowledge base name" },
|
|
235
|
+
{ name: "mode", type: "string", required: false, default: "incremental", desc: "Compile mode: incremental | full (default: incremental)" }
|
|
236
|
+
],
|
|
237
|
+
risk: "write",
|
|
238
|
+
validate: (ctx) => {
|
|
239
|
+
getMode(ctx);
|
|
240
|
+
},
|
|
241
|
+
dryRun: (ctx) => ({
|
|
242
|
+
method: "POST",
|
|
243
|
+
url: `${ctx.host().replace(/\/$/, "")}${API_PATH3}`,
|
|
244
|
+
body: buildBody2(ctx)
|
|
245
|
+
}),
|
|
246
|
+
execute: async (ctx) => ctx.api("POST", API_PATH3, {}, buildBody2(ctx))
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
// src/commands/te-kb/remove.ts
|
|
250
|
+
var API_PATH4 = "/agent/api/external/knowledge-bases";
|
|
251
|
+
function buildBody3(ctx) {
|
|
252
|
+
return { name: ctx.str("name") };
|
|
253
|
+
}
|
|
254
|
+
var remove = {
|
|
255
|
+
service: "kb",
|
|
256
|
+
command: "+remove",
|
|
257
|
+
description: "Delete an entire knowledge.",
|
|
258
|
+
flags: [
|
|
259
|
+
{ name: "name", type: "string", required: true, desc: "Knowledge base name to delete" }
|
|
260
|
+
],
|
|
261
|
+
risk: "write",
|
|
262
|
+
dryRun: (ctx) => ({
|
|
263
|
+
method: "DELETE",
|
|
264
|
+
url: `${ctx.host().replace(/\/$/, "")}${API_PATH4}`,
|
|
265
|
+
body: buildBody3(ctx)
|
|
266
|
+
}),
|
|
267
|
+
execute: async (ctx) => httpDelete(API_PATH4, {}, buildBody3(ctx), ctx.host())
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
// src/commands/te-kb/create.ts
|
|
271
|
+
var API_PATH5 = "/agent/api/external/knowledge-bases/create";
|
|
272
|
+
var VALID_SCOPES = /* @__PURE__ */ new Set(["personal", "company"]);
|
|
273
|
+
function validateScope(scope) {
|
|
274
|
+
if (!VALID_SCOPES.has(scope)) {
|
|
275
|
+
throw new Error(`Invalid --scope: ${scope}. Must be one of: personal | company`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
function normalizeTags(raw) {
|
|
279
|
+
if (raw === void 0 || raw === null) return void 0;
|
|
280
|
+
if (!Array.isArray(raw)) {
|
|
281
|
+
throw new Error(`--tags must be a JSON array of strings, e.g. '["t1","t2"]'`);
|
|
282
|
+
}
|
|
283
|
+
const tags = [];
|
|
284
|
+
for (const v of raw) {
|
|
285
|
+
if (typeof v !== "string") {
|
|
286
|
+
throw new Error(`--tags entries must be strings (got: ${JSON.stringify(v)})`);
|
|
287
|
+
}
|
|
288
|
+
const t = v.trim();
|
|
289
|
+
if (t) tags.push(t);
|
|
290
|
+
}
|
|
291
|
+
return tags;
|
|
292
|
+
}
|
|
293
|
+
function buildBody4(ctx) {
|
|
294
|
+
const body = {
|
|
295
|
+
scope: ctx.str("scope"),
|
|
296
|
+
name: ctx.str("name")
|
|
297
|
+
};
|
|
298
|
+
const description = ctx.str("description");
|
|
299
|
+
if (description) body.description = description;
|
|
300
|
+
const tags = normalizeTags(ctx.json("tags"));
|
|
301
|
+
if (tags && tags.length > 0) body.tags = tags;
|
|
302
|
+
const projectId = ctx.str("project-id");
|
|
303
|
+
if (projectId) body.projectId = projectId;
|
|
304
|
+
const projectName = ctx.str("project-name");
|
|
305
|
+
if (projectName) body.projectName = projectName;
|
|
306
|
+
return body;
|
|
307
|
+
}
|
|
308
|
+
var create = {
|
|
309
|
+
service: "kb",
|
|
310
|
+
command: "+new",
|
|
311
|
+
description: "Create a new knowledge.",
|
|
312
|
+
flags: [
|
|
313
|
+
{ name: "scope", type: "string", required: true, desc: "Knowledge base scope: personal | company" },
|
|
314
|
+
{ name: "name", type: "string", required: true, desc: "Knowledge base name (\u226430 chars, unique per scope)" },
|
|
315
|
+
{ name: "description", type: "string", required: false, desc: "Optional description (\u2264200 chars)" },
|
|
316
|
+
{ name: "tags", type: "json", required: false, desc: `Optional JSON array of tags (max 2, each \u226415 chars). Example: '["t1","t2"]'` },
|
|
317
|
+
{ name: "project-id", type: "string", required: false, desc: "Optional project ID to bind" },
|
|
318
|
+
{ name: "project-name", type: "string", required: false, desc: "Optional project display name" }
|
|
319
|
+
],
|
|
320
|
+
risk: "write",
|
|
321
|
+
validate: (ctx) => {
|
|
322
|
+
validateScope(ctx.str("scope"));
|
|
323
|
+
normalizeTags(ctx.json("tags"));
|
|
324
|
+
},
|
|
325
|
+
dryRun: (ctx) => ({
|
|
326
|
+
method: "POST",
|
|
327
|
+
url: `${ctx.host().replace(/\/$/, "")}${API_PATH5}`,
|
|
328
|
+
body: buildBody4(ctx)
|
|
329
|
+
}),
|
|
330
|
+
execute: async (ctx) => ctx.api("POST", API_PATH5, {}, buildBody4(ctx))
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
// src/commands/te-kb/rm-source.ts
|
|
334
|
+
var API_PATH6 = "/agent/api/external/knowledge-bases/sources";
|
|
335
|
+
function buildBody5(ctx) {
|
|
336
|
+
return {
|
|
337
|
+
name: ctx.str("name"),
|
|
338
|
+
displayName: ctx.str("display-name")
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
var rmSource = {
|
|
342
|
+
service: "kb",
|
|
343
|
+
command: "+rm-source",
|
|
344
|
+
description: "Delete a single source file inside a knowledge.",
|
|
345
|
+
flags: [
|
|
346
|
+
{ name: "name", type: "string", required: true, desc: "Knowledge base name (looked up personal \u2192 company)" },
|
|
347
|
+
{ name: "display-name", type: "string", required: true, desc: "Source file display name as uploaded (e.g. kb-1780046712-foo.md)" }
|
|
348
|
+
],
|
|
349
|
+
risk: "write",
|
|
350
|
+
dryRun: (ctx) => ({
|
|
351
|
+
method: "DELETE",
|
|
352
|
+
url: `${ctx.host().replace(/\/$/, "")}${API_PATH6}`,
|
|
353
|
+
body: buildBody5(ctx)
|
|
354
|
+
}),
|
|
355
|
+
execute: async (ctx) => httpDelete(API_PATH6, {}, buildBody5(ctx), ctx.host())
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
// src/commands/te-kb/schema.ts
|
|
359
|
+
var API_PATH7 = "/agent/api/external/knowledge-bases/schema";
|
|
360
|
+
function buildBody6(ctx) {
|
|
361
|
+
const body = {
|
|
362
|
+
name: ctx.str("name")
|
|
363
|
+
};
|
|
364
|
+
if (ctx.bool("force")) body.force = true;
|
|
365
|
+
const model = ctx.str("model");
|
|
366
|
+
if (model) body.model = model;
|
|
367
|
+
return body;
|
|
368
|
+
}
|
|
369
|
+
var schema = {
|
|
370
|
+
service: "kb",
|
|
371
|
+
command: "+schema",
|
|
372
|
+
description: "Generate the compile schema for a knowledge base via POST /agent/api/external/knowledge-bases/schema.",
|
|
373
|
+
flags: [
|
|
374
|
+
{ name: "name", type: "string", required: true, desc: "Knowledge base name (looked up personal \u2192 company)" },
|
|
375
|
+
{ name: "force", type: "boolean", required: false, desc: "Preempt generation even when status is `generating` (use only for stuck recovery)" },
|
|
376
|
+
{ name: "model", type: "string", required: false, desc: "Optional model displayName to use for schema generation" }
|
|
377
|
+
],
|
|
378
|
+
risk: "write",
|
|
379
|
+
dryRun: (ctx) => ({
|
|
380
|
+
method: "POST",
|
|
381
|
+
url: `${ctx.host().replace(/\/$/, "")}${API_PATH7}`,
|
|
382
|
+
body: buildBody6(ctx)
|
|
383
|
+
}),
|
|
384
|
+
execute: async (ctx) => ctx.api("POST", API_PATH7, {}, buildBody6(ctx))
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
// src/commands/te-kb/index.ts
|
|
388
|
+
var commands = [
|
|
389
|
+
query,
|
|
390
|
+
add,
|
|
391
|
+
compile,
|
|
392
|
+
remove,
|
|
393
|
+
create,
|
|
394
|
+
rmSource,
|
|
395
|
+
schema
|
|
396
|
+
];
|
|
397
|
+
var te_kb_default = commands;
|
|
398
|
+
export {
|
|
399
|
+
add,
|
|
400
|
+
compile,
|
|
401
|
+
create,
|
|
402
|
+
te_kb_default as default,
|
|
403
|
+
query,
|
|
404
|
+
remove,
|
|
405
|
+
rmSource,
|
|
406
|
+
schema
|
|
407
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thinkingai/ae-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.22",
|
|
4
4
|
"description": "CLI tool for ThinkingAI (AE) analytics platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -44,10 +44,12 @@
|
|
|
44
44
|
"cli-table3": "^0.6.5",
|
|
45
45
|
"commander": "^12.1.0",
|
|
46
46
|
"json-bigint": "^1.0.0",
|
|
47
|
+
"turndown": "^7.2.4",
|
|
47
48
|
"ws": "^8.18.0"
|
|
48
49
|
},
|
|
49
50
|
"devDependencies": {
|
|
50
51
|
"@types/node": "^22.0.0",
|
|
52
|
+
"@types/turndown": "^5.0.6",
|
|
51
53
|
"@types/ws": "^8.5.0",
|
|
52
54
|
"tsup": "^8.0.0",
|
|
53
55
|
"tsx": "^4.0.0",
|
package/dist/te-kb-U6SQUJVC.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
// src/commands/te-kb/query.ts
|
|
2
|
-
var API_PATH = "/api/external/knowledge-bases/query";
|
|
3
|
-
function buildBody(ctx) {
|
|
4
|
-
const body = {
|
|
5
|
-
query: ctx.str("query"),
|
|
6
|
-
sources: ctx.json("sources")
|
|
7
|
-
};
|
|
8
|
-
const modelId = ctx.str("model-id");
|
|
9
|
-
if (modelId) body.modelId = modelId;
|
|
10
|
-
const maxTurnsRaw = ctx.str("max-turns");
|
|
11
|
-
if (maxTurnsRaw !== "") body.maxTurns = ctx.num("max-turns");
|
|
12
|
-
return body;
|
|
13
|
-
}
|
|
14
|
-
var query = {
|
|
15
|
-
service: "kb",
|
|
16
|
-
command: "+query",
|
|
17
|
-
description: "Query knowledge bases via /api/external/knowledge-bases/query.",
|
|
18
|
-
flags: [
|
|
19
|
-
{ name: "query", type: "string", required: true, alias: "q", desc: "Natural language question to query against the knowledge bases" },
|
|
20
|
-
{ name: "sources", type: "json", required: true, desc: 'JSON array of knowledge base refs, e.g. [{"scope":"company","name":"engineering-handbook"}]' },
|
|
21
|
-
{ name: "model-id", type: "string", required: false, default: "AE-Auto", desc: "Model identifier (default: AE-Auto)" },
|
|
22
|
-
{ name: "max-turns", type: "number", required: false, default: 6, desc: "Maximum reasoning turns (default: 6)" }
|
|
23
|
-
],
|
|
24
|
-
risk: "read",
|
|
25
|
-
dryRun: (ctx) => ({
|
|
26
|
-
method: "POST",
|
|
27
|
-
url: `${ctx.host().replace(/\/$/, "")}${API_PATH}`,
|
|
28
|
-
body: buildBody(ctx)
|
|
29
|
-
}),
|
|
30
|
-
execute: async (ctx) => ctx.api("POST", API_PATH, {}, buildBody(ctx))
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
// src/commands/te-kb/index.ts
|
|
34
|
-
var commands = [
|
|
35
|
-
query
|
|
36
|
-
];
|
|
37
|
-
var te_kb_default = commands;
|
|
38
|
-
export {
|
|
39
|
-
te_kb_default as default,
|
|
40
|
-
query
|
|
41
|
-
};
|