@thinkingai/ae-cli 1.0.27 → 1.0.30
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 +1 -16
- package/README.zh.md +1 -16
- package/dist/auth-H3GJD55A.js +457 -0
- package/dist/{auth-TXFJHPXU.js → auth-U25QJU5D.js} +3 -3
- package/dist/{chunk-B4UL2VIJ.js → chunk-CLJF7MQA.js} +1 -1
- package/dist/{chunk-6T7245YE.js → chunk-EGEIXA2Z.js} +90 -18
- package/dist/{chunk-2NGWYMLB.js → chunk-GLNZSDKO.js} +88 -50
- package/dist/chunk-GSJGPNKK.js +49 -0
- package/dist/{chunk-VT6VC4PA.js → chunk-I42JGQ2J.js} +26 -11
- package/dist/chunk-JM34JPCO.js +239 -0
- package/dist/chunk-NOO24N7W.js +11 -0
- package/dist/chunk-SF3KTPIC.js +98 -0
- package/dist/chunk-SRJIAOBN.js +244 -0
- package/dist/chunk-U6TKB3IV.js +171 -0
- package/dist/{chunk-TG64AQV4.js → chunk-VJFUB3H3.js} +99 -56
- package/dist/{client-IU2E5IOL.js → client-IN42F223.js} +4 -4
- package/dist/config-64G36TJL.js +130 -0
- package/dist/index.js +52 -25
- package/dist/{model-CC7U5XOJ.js → model-VZCYNIOG.js} +11 -10
- package/dist/{raw-TUHZQ4UB.js → raw-WJIWC6YP.js} +13 -7
- package/dist/{sync-EVEDS7YE.js → sync-NVASAISS.js} +146 -47
- package/dist/{te-agent-FLTDYV3L.js → te-agent-YV4DXEXK.js} +24 -23
- package/dist/{te-analysis-CMXADIJM.js → te-analysis-CB3DA5I5.js} +61 -13
- package/dist/{te-audience-LPZFFSXM.js → te-audience-W7SCQFC7.js} +7 -5
- package/dist/{te-common-BXQFKTB3.js → te-common-FBXVWBSI.js} +21 -9
- package/dist/{te-community-K5S3VOZY.js → te-community-LNVDZSGW.js} +6 -4
- package/dist/{te-dataops-567IMCQG.js → te-dataops-IPCSSWYH.js} +29 -27
- package/dist/{te-engage-TBUTGW5F.js → te-engage-KARARMIR.js} +6 -4
- package/dist/{te-kb-XHVBWP5E.js → te-kb-2Z2CARN3.js} +7 -5
- package/dist/{te-meta-NQ4U3VJW.js → te-meta-PDKLMWXN.js} +6 -4
- package/dist/{te-team-556APRIU.js → te-team-SW5B7DHO.js} +17 -12
- package/package.json +6 -2
- package/skills/ae-agent/SKILL.md +2 -1
- package/skills/ae-analysis/SKILL.md +1 -1
- package/skills/ae-analysis/references/list_clusters.md +4 -2
- package/skills/ae-analysis-global/SKILL.md +51 -0
- package/skills/ae-analysis-global/references/list_query_clusters.md +67 -0
- package/skills/ae-community/SKILL.md +2 -3
- package/skills/ae-dataops/SKILL.md +1 -2
- package/skills/ae-engage/SKILL.md +1 -0
- package/skills/ae-engage/references/add-approver.md +0 -1
- package/skills/ae-engage/references/add-channel.md +0 -1
- package/skills/ae-engage/references/approver-list.md +0 -1
- package/skills/ae-engage/references/cancel-query-by-request-id.md +0 -1
- package/skills/ae-engage/references/channel-detail.md +0 -1
- package/skills/ae-engage/references/channel-list.md +0 -1
- package/skills/ae-engage/references/config-channel-detail.md +0 -1
- package/skills/ae-engage/references/config-channel-list.md +0 -1
- package/skills/ae-engage/references/config-item-analysis-report.md +0 -1
- package/skills/ae-engage/references/config-item-detail.md +0 -1
- package/skills/ae-engage/references/config-item-list.md +0 -1
- package/skills/ae-engage/references/config-item-strategy-comparison.md +0 -1
- package/skills/ae-engage/references/config-item-trigger-report.md +0 -1
- package/skills/ae-engage/references/copy-config-template.md +0 -1
- package/skills/ae-engage/references/delete-channel.md +0 -1
- package/skills/ae-engage/references/delete-config-channel.md +0 -1
- package/skills/ae-engage/references/delete-config-item.md +0 -1
- package/skills/ae-engage/references/delete-flow.md +0 -1
- package/skills/ae-engage/references/flow-ab-split-node-report.md +0 -1
- package/skills/ae-engage/references/flow-detail.md +0 -1
- package/skills/ae-engage/references/flow-list.md +0 -1
- package/skills/ae-engage/references/flow-node-config-schema.md +0 -1
- package/skills/ae-engage/references/flow-node-detail-report.md +0 -1
- package/skills/ae-engage/references/flow-node-overview-report.md +0 -1
- package/skills/ae-engage/references/flow-process-report.md +0 -1
- package/skills/ae-engage/references/manage-flow.md +0 -1
- package/skills/ae-engage/references/manage-strategy.md +0 -1
- package/skills/ae-engage/references/manage-task.md +0 -1
- package/skills/ae-engage/references/modify-flow-base-info.md +0 -1
- package/skills/ae-engage/references/strategy-detail.md +0 -1
- package/skills/ae-engage/references/strategy-list.md +0 -1
- package/skills/ae-engage/references/task-data-detail.md +0 -1
- package/skills/ae-engage/references/task-data-overview.md +0 -1
- package/skills/ae-engage/references/task-detail.md +0 -1
- package/skills/ae-engage/references/task-experiment-report.md +0 -1
- package/skills/ae-engage/references/task-list.md +0 -1
- package/skills/ae-engage/references/task-metric-detail.md +0 -1
- package/skills/ae-engage/references/task-stats.md +0 -1
- package/skills/ae-engage/references/update-channel-status.md +0 -1
- package/skills/ae-engage/references/update-config-channel-status.md +0 -1
- package/skills/ae-engage/references/validate-flow-node-config.md +0 -1
- package/skills/ae-engage/references/whitelist-list.md +0 -1
- package/skills/ae-kb/SKILL.md +1 -0
- package/skills/ae-team/SKILL.md +1 -1
- package/dist/auth-W2JZ3DKA.js +0 -170
- package/dist/chunk-BI5ZVD6T.js +0 -186
- package/dist/chunk-MNHE6SSI.js +0 -79
- package/dist/chunk-MWWYGZ76.js +0 -130
- package/dist/chunk-SRRUNQIM.js +0 -312
- package/dist/config-WH3R3IRY.js +0 -258
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getConfigDir,
|
|
3
|
+
logger,
|
|
4
|
+
safeJsonParse
|
|
5
|
+
} from "./chunk-SRJIAOBN.js";
|
|
6
|
+
|
|
7
|
+
// src/core/secure-store.ts
|
|
8
|
+
import crypto from "crypto";
|
|
9
|
+
import fs from "fs";
|
|
10
|
+
import os from "os";
|
|
11
|
+
import path from "path";
|
|
12
|
+
import { execFileSync } from "child_process";
|
|
13
|
+
var STORE_DIR_SUFFIX = "secure-tokens";
|
|
14
|
+
var SCRYPT_SALT = Buffer.from("ae-cli-secure-store-v1");
|
|
15
|
+
var SCRYPT_N = 16384;
|
|
16
|
+
var SCRYPT_R = 8;
|
|
17
|
+
var SCRYPT_P = 1;
|
|
18
|
+
var KEY_LEN = 32;
|
|
19
|
+
var NONCE_LEN = 12;
|
|
20
|
+
var REFRESH_TOKEN_PATH = "/v1/oauth/refreshForToken";
|
|
21
|
+
function getMachineId() {
|
|
22
|
+
if (process.platform === "darwin") {
|
|
23
|
+
try {
|
|
24
|
+
const out = execFileSync(
|
|
25
|
+
"ioreg",
|
|
26
|
+
["-rd1", "-c", "IOPlatformExpertDevice"],
|
|
27
|
+
{ encoding: "utf8", timeout: 3e3 }
|
|
28
|
+
);
|
|
29
|
+
const m = out.match(/IOPlatformUUID.*?=.*?"([0-9A-F-]{36})"/i);
|
|
30
|
+
if (m?.[1]) return `darwin:${m[1]}`;
|
|
31
|
+
} catch {
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (process.platform === "linux") {
|
|
35
|
+
for (const p of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
|
|
36
|
+
try {
|
|
37
|
+
const id = fs.readFileSync(p, "utf8").trim();
|
|
38
|
+
if (id) return `linux:${id}`;
|
|
39
|
+
} catch {
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (process.platform === "win32") {
|
|
44
|
+
try {
|
|
45
|
+
const out = execFileSync(
|
|
46
|
+
"reg",
|
|
47
|
+
["query", "HKLM\\SOFTWARE\\Microsoft\\Cryptography", "/v", "MachineGuid"],
|
|
48
|
+
{ encoding: "utf8", timeout: 3e3 }
|
|
49
|
+
);
|
|
50
|
+
const m = out.match(/MachineGuid\s+REG_SZ\s+([0-9a-f-]+)/i);
|
|
51
|
+
if (m?.[1]) return `win32:${m[1]}`;
|
|
52
|
+
} catch {
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const fallback = `${os.homedir()}|${os.hostname()}|${os.platform()}`;
|
|
56
|
+
logger.warn("secure-store: using fallback machine-id (HOME+hostname+platform)");
|
|
57
|
+
return `fallback:${fallback}`;
|
|
58
|
+
}
|
|
59
|
+
var _cachedMachineId;
|
|
60
|
+
function getCachedMachineId() {
|
|
61
|
+
if (!_cachedMachineId) _cachedMachineId = getMachineId();
|
|
62
|
+
return _cachedMachineId;
|
|
63
|
+
}
|
|
64
|
+
function deriveKey(machineId) {
|
|
65
|
+
return crypto.scryptSync(machineId, SCRYPT_SALT, KEY_LEN, {
|
|
66
|
+
N: SCRYPT_N,
|
|
67
|
+
r: SCRYPT_R,
|
|
68
|
+
p: SCRYPT_P
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
var _cachedKey;
|
|
72
|
+
function getKey() {
|
|
73
|
+
if (!_cachedKey) _cachedKey = deriveKey(getCachedMachineId());
|
|
74
|
+
return _cachedKey;
|
|
75
|
+
}
|
|
76
|
+
function encrypt(plaintext) {
|
|
77
|
+
const key = getKey();
|
|
78
|
+
const nonce = crypto.randomBytes(NONCE_LEN);
|
|
79
|
+
const cipher = crypto.createCipheriv("aes-256-gcm", key, nonce);
|
|
80
|
+
const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
|
|
81
|
+
const tag = cipher.getAuthTag();
|
|
82
|
+
return {
|
|
83
|
+
nonce: nonce.toString("hex"),
|
|
84
|
+
tag: tag.toString("hex"),
|
|
85
|
+
data: encrypted.toString("hex")
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function decrypt(blob) {
|
|
89
|
+
const key = getKey();
|
|
90
|
+
const nonce = Buffer.from(blob.nonce, "hex");
|
|
91
|
+
const tag = Buffer.from(blob.tag, "hex");
|
|
92
|
+
const data = Buffer.from(blob.data, "hex");
|
|
93
|
+
const decipher = crypto.createDecipheriv("aes-256-gcm", key, nonce);
|
|
94
|
+
decipher.setAuthTag(tag);
|
|
95
|
+
const decrypted = Buffer.concat([decipher.update(data), decipher.final()]);
|
|
96
|
+
return decrypted.toString("utf8");
|
|
97
|
+
}
|
|
98
|
+
function storeDir() {
|
|
99
|
+
return path.join(getConfigDir(), STORE_DIR_SUFFIX);
|
|
100
|
+
}
|
|
101
|
+
function tokenFilePath(host) {
|
|
102
|
+
const safe = host.replace(/^https?:\/\//, "").replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
103
|
+
return path.join(storeDir(), `${safe}.enc.json`);
|
|
104
|
+
}
|
|
105
|
+
function ensureStoreDir() {
|
|
106
|
+
const dir = storeDir();
|
|
107
|
+
if (!fs.existsSync(dir)) {
|
|
108
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
109
|
+
try {
|
|
110
|
+
fs.chmodSync(dir, 448);
|
|
111
|
+
} catch {
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function save(host, payload) {
|
|
116
|
+
ensureStoreDir();
|
|
117
|
+
const plaintext = JSON.stringify(payload);
|
|
118
|
+
const blob = encrypt(plaintext);
|
|
119
|
+
const filePath = tokenFilePath(host);
|
|
120
|
+
fs.writeFileSync(filePath, JSON.stringify(blob, null, 2), { encoding: "utf8" });
|
|
121
|
+
try {
|
|
122
|
+
fs.chmodSync(filePath, 384);
|
|
123
|
+
} catch {
|
|
124
|
+
}
|
|
125
|
+
logger.info(`secure-store: token saved for ${host}`);
|
|
126
|
+
}
|
|
127
|
+
function load(host) {
|
|
128
|
+
const filePath = tokenFilePath(host);
|
|
129
|
+
if (!fs.existsSync(filePath)) return null;
|
|
130
|
+
try {
|
|
131
|
+
const raw = fs.readFileSync(filePath, "utf8");
|
|
132
|
+
const blob = safeJsonParse(raw);
|
|
133
|
+
if (!blob?.nonce || !blob?.tag || !blob?.data) {
|
|
134
|
+
logger.warn(`secure-store: malformed blob for ${host}`);
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
const plaintext = decrypt(blob);
|
|
138
|
+
return JSON.parse(plaintext);
|
|
139
|
+
} catch (e) {
|
|
140
|
+
logger.error(`secure-store: decrypt failed for ${host}: ${e.message}`);
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function clear(host) {
|
|
145
|
+
const filePath = tokenFilePath(host);
|
|
146
|
+
if (fs.existsSync(filePath)) {
|
|
147
|
+
fs.rmSync(filePath);
|
|
148
|
+
logger.info(`secure-store: token cleared for ${host}`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async function getValidAccessToken(host) {
|
|
152
|
+
const payload = load(host);
|
|
153
|
+
if (!payload) {
|
|
154
|
+
throw new SecureStoreAuthError(
|
|
155
|
+
`No stored token for ${host}. Please run: ae-cli auth login`
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
const expiresAt = new Date(payload.accessExpiresAt).getTime();
|
|
159
|
+
const now = Date.now();
|
|
160
|
+
const needsRefresh = expiresAt - now < 6e4;
|
|
161
|
+
if (!needsRefresh) {
|
|
162
|
+
return payload.accessToken;
|
|
163
|
+
}
|
|
164
|
+
if (payload.refreshToken) {
|
|
165
|
+
logger.info(`secure-store: access token past local expiry for ${host}, refreshing\u2026`);
|
|
166
|
+
const refreshed = await doRefresh(host, payload.refreshToken);
|
|
167
|
+
save(host, { ...refreshed, mcpToken: payload.mcpToken });
|
|
168
|
+
logger.info(`secure-store: token refreshed for ${host}`);
|
|
169
|
+
return refreshed.accessToken;
|
|
170
|
+
}
|
|
171
|
+
logger.info(`secure-store: ${host} past static local expiry, no refresh token \u2014 returning stored token, server validates (lazy)`);
|
|
172
|
+
return payload.accessToken;
|
|
173
|
+
}
|
|
174
|
+
function loadMcpToken(host) {
|
|
175
|
+
return load(host)?.mcpToken ?? null;
|
|
176
|
+
}
|
|
177
|
+
async function doRefresh(host, refreshToken) {
|
|
178
|
+
const base = host.replace(/\/+$/, "");
|
|
179
|
+
const url = `${base}${REFRESH_TOKEN_PATH}`;
|
|
180
|
+
let resp;
|
|
181
|
+
try {
|
|
182
|
+
resp = await fetch(url, {
|
|
183
|
+
method: "POST",
|
|
184
|
+
headers: { "Content-Type": "application/json" },
|
|
185
|
+
body: JSON.stringify({ refreshToken })
|
|
186
|
+
});
|
|
187
|
+
} catch (e) {
|
|
188
|
+
throw new SecureStoreAuthError(
|
|
189
|
+
`Token refresh network error for ${host}: ${e.message}. Please run: ae-cli auth login`
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
if (!resp.ok) {
|
|
193
|
+
throw new SecureStoreAuthError(
|
|
194
|
+
`Token refresh failed (HTTP ${resp.status}) for ${host}. Please run: ae-cli auth login`
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
let body;
|
|
198
|
+
try {
|
|
199
|
+
body = safeJsonParse(await resp.text());
|
|
200
|
+
} catch {
|
|
201
|
+
throw new SecureStoreAuthError(
|
|
202
|
+
`Token refresh returned invalid JSON for ${host}. Please run: ae-cli auth login`
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
if (body.return_code !== 0) {
|
|
206
|
+
throw new SecureStoreAuthError(
|
|
207
|
+
`Token refresh error (${body.return_code}: ${body.return_message ?? "unknown"}) for ${host}. Please run: ae-cli auth login`
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
const d = body.data;
|
|
211
|
+
const newAccess = d?.accessToken ?? d?.access_token ?? "";
|
|
212
|
+
const newRefresh = d?.refreshToken ?? d?.refresh_token ?? refreshToken;
|
|
213
|
+
const expiresIn = d?.expiresIn ?? d?.expires_in ?? 72e3;
|
|
214
|
+
if (!newAccess) {
|
|
215
|
+
throw new SecureStoreAuthError(
|
|
216
|
+
`Token refresh returned empty accessToken for ${host}. Please run: ae-cli auth login`
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
220
|
+
accessToken: newAccess,
|
|
221
|
+
refreshToken: newRefresh,
|
|
222
|
+
accessExpiresAt: new Date(Date.now() + expiresIn * 1e3).toISOString()
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
var SecureStoreAuthError = class extends Error {
|
|
226
|
+
constructor(message) {
|
|
227
|
+
super(message);
|
|
228
|
+
this.name = "SecureStoreAuthError";
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
export {
|
|
233
|
+
save,
|
|
234
|
+
load,
|
|
235
|
+
clear,
|
|
236
|
+
getValidAccessToken,
|
|
237
|
+
loadMcpToken,
|
|
238
|
+
SecureStoreAuthError
|
|
239
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getMcpToken
|
|
3
|
+
} from "./chunk-GLNZSDKO.js";
|
|
4
|
+
import {
|
|
5
|
+
PermissionError
|
|
6
|
+
} from "./chunk-NOO24N7W.js";
|
|
7
|
+
import {
|
|
8
|
+
safeJsonParse
|
|
9
|
+
} from "./chunk-SRJIAOBN.js";
|
|
10
|
+
|
|
11
|
+
// src/core/mcp-access.ts
|
|
12
|
+
function buildUrl(host, path, params = {}) {
|
|
13
|
+
const base = host.replace(/\/$/, "");
|
|
14
|
+
const p = path.startsWith("/") ? path : `/${path}`;
|
|
15
|
+
const url = new URL(`${base}${p}`);
|
|
16
|
+
for (const [key, value] of Object.entries(params)) {
|
|
17
|
+
if (value !== void 0 && value !== null) {
|
|
18
|
+
url.searchParams.set(key, String(value));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return url.toString();
|
|
22
|
+
}
|
|
23
|
+
function parseKbResponse(resp, text) {
|
|
24
|
+
if (resp.status === 403) {
|
|
25
|
+
let msg = "Permission denied for this resource (HTTP 403)";
|
|
26
|
+
try {
|
|
27
|
+
const d = safeJsonParse(text);
|
|
28
|
+
const m = d && (typeof d.error === "string" ? d.error : d.error?.message || d.message);
|
|
29
|
+
if (m && typeof m === "string") msg = m;
|
|
30
|
+
} catch {
|
|
31
|
+
}
|
|
32
|
+
throw new PermissionError(msg);
|
|
33
|
+
}
|
|
34
|
+
if (resp.status === 401) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
`KB MCP token auth failed: HTTP ${resp.status} ${resp.statusText}`
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
const data = safeJsonParse(text);
|
|
40
|
+
if (data.return_code === -1001) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
`KB MCP token auth failed: ${data.return_message || "unauthorized"} (code: ${data.return_code})`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
if (data.return_code !== 0 && data.return_code !== void 0) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`AE API error: ${data.return_message || "unknown"} (code: ${data.return_code})`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
if (data && typeof data === "object" && typeof data.error === "string" && data.error.trim() && data.return_code === void 0 && data.success === void 0) {
|
|
51
|
+
throw new Error(data.error);
|
|
52
|
+
}
|
|
53
|
+
return data.data !== void 0 ? data.data : data;
|
|
54
|
+
}
|
|
55
|
+
async function fetchWithMcpToken(input, init, mcpToken) {
|
|
56
|
+
const headers = new Headers(init.headers);
|
|
57
|
+
headers.set("mcp-token", mcpToken);
|
|
58
|
+
const resp = await fetch(input, { ...init, headers });
|
|
59
|
+
return parseKbResponse(resp, await resp.text());
|
|
60
|
+
}
|
|
61
|
+
async function getAuthHeaders(ctx) {
|
|
62
|
+
const mcpToken = await getMcpToken(ctx.host());
|
|
63
|
+
return { "mcp-token": mcpToken };
|
|
64
|
+
}
|
|
65
|
+
async function kbApi(ctx, method, path, params = {}, body) {
|
|
66
|
+
const host = ctx.host();
|
|
67
|
+
const mcpToken = await getMcpToken(host);
|
|
68
|
+
const upperMethod = method.toUpperCase();
|
|
69
|
+
return fetchWithMcpToken(
|
|
70
|
+
buildUrl(host, path, params),
|
|
71
|
+
{
|
|
72
|
+
method: upperMethod,
|
|
73
|
+
headers: {
|
|
74
|
+
"Content-Type": "application/json"
|
|
75
|
+
},
|
|
76
|
+
body: upperMethod === "GET" ? void 0 : JSON.stringify(body ?? {})
|
|
77
|
+
},
|
|
78
|
+
mcpToken
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
async function kbUpload(ctx, path, form, params = {}) {
|
|
82
|
+
const host = ctx.host();
|
|
83
|
+
const mcpToken = await getMcpToken(host);
|
|
84
|
+
return fetchWithMcpToken(
|
|
85
|
+
buildUrl(host, path, params),
|
|
86
|
+
{
|
|
87
|
+
method: "POST",
|
|
88
|
+
body: form
|
|
89
|
+
},
|
|
90
|
+
mcpToken
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export {
|
|
95
|
+
getAuthHeaders,
|
|
96
|
+
kbApi,
|
|
97
|
+
kbUpload
|
|
98
|
+
};
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
// src/core/logger.ts
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
var LOG_DIR = path.join(process.env.HOME || "", ".ae-cli", "log");
|
|
5
|
+
var Logger = class {
|
|
6
|
+
logDir;
|
|
7
|
+
date;
|
|
8
|
+
constructor() {
|
|
9
|
+
this.logDir = LOG_DIR;
|
|
10
|
+
this.date = this.today();
|
|
11
|
+
this.ensureDir();
|
|
12
|
+
}
|
|
13
|
+
today() {
|
|
14
|
+
const d = /* @__PURE__ */ new Date();
|
|
15
|
+
return `${d.getFullYear()}${String(d.getMonth() + 1).padStart(2, "0")}${String(d.getDate()).padStart(2, "0")}`;
|
|
16
|
+
}
|
|
17
|
+
timestamp() {
|
|
18
|
+
return (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").split(".")[0];
|
|
19
|
+
}
|
|
20
|
+
ensureDir() {
|
|
21
|
+
if (!fs.existsSync(this.logDir)) {
|
|
22
|
+
fs.mkdirSync(this.logDir, { recursive: true });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
filePath(level) {
|
|
26
|
+
const today = this.today();
|
|
27
|
+
if (today !== this.date) {
|
|
28
|
+
this.date = today;
|
|
29
|
+
}
|
|
30
|
+
return path.join(this.logDir, `${this.date}_${level}.log`);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Write a log entry to file (not printed to the console; console output is handled by existing process.stderr.write calls)
|
|
34
|
+
*/
|
|
35
|
+
write(level, message) {
|
|
36
|
+
const ts = this.timestamp();
|
|
37
|
+
const line = `[${ts}] ${message}
|
|
38
|
+
`;
|
|
39
|
+
try {
|
|
40
|
+
fs.appendFileSync(this.filePath(level), line);
|
|
41
|
+
} catch {
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** Informational log */
|
|
45
|
+
info(message) {
|
|
46
|
+
this.write("info", message);
|
|
47
|
+
}
|
|
48
|
+
/** Warning log */
|
|
49
|
+
warn(message) {
|
|
50
|
+
this.write("warning", message);
|
|
51
|
+
}
|
|
52
|
+
/** Error log */
|
|
53
|
+
error(message) {
|
|
54
|
+
this.write("error", message);
|
|
55
|
+
}
|
|
56
|
+
/** Log HTTP API request details */
|
|
57
|
+
api(method, url, status, reqBody, respBody) {
|
|
58
|
+
const parts = [`API ${method} ${url} \u2192 HTTP ${status}`];
|
|
59
|
+
if (reqBody !== void 0 && reqBody !== null) {
|
|
60
|
+
const s = typeof reqBody === "string" ? reqBody : JSON.stringify(reqBody);
|
|
61
|
+
parts.push(`REQ: ${s.slice(0, 500)}`);
|
|
62
|
+
}
|
|
63
|
+
if (respBody !== void 0 && respBody !== null) {
|
|
64
|
+
const s = typeof respBody === "string" ? respBody : JSON.stringify(respBody);
|
|
65
|
+
parts.push(`RESP: ${s.slice(0, 500)}`);
|
|
66
|
+
}
|
|
67
|
+
this.info(parts.join(" | "));
|
|
68
|
+
}
|
|
69
|
+
/** Log command execution */
|
|
70
|
+
command(name, args) {
|
|
71
|
+
const filtered = Object.entries(args).filter(([, v]) => v !== void 0 && v !== null && v !== "").map(([k, v]) => `--${k}=${JSON.stringify(v)}`).join(" ");
|
|
72
|
+
this.info(`CMD ${name}${filtered ? " " + filtered : ""}`);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
var logger = new Logger();
|
|
76
|
+
|
|
77
|
+
// src/core/json-utils.ts
|
|
78
|
+
import fs2 from "fs";
|
|
79
|
+
import { createRequire } from "module";
|
|
80
|
+
var require2 = createRequire(import.meta.url);
|
|
81
|
+
var JSONbig = require2("json-bigint");
|
|
82
|
+
var jsonParser = JSONbig({ storeAsString: true });
|
|
83
|
+
function safeJsonParse(text) {
|
|
84
|
+
return jsonParser.parse(text);
|
|
85
|
+
}
|
|
86
|
+
function safeReadJsonFile(filePath) {
|
|
87
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
88
|
+
return safeJsonParse(content);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// src/core/config.ts
|
|
92
|
+
import fs3 from "fs";
|
|
93
|
+
import path2 from "path";
|
|
94
|
+
|
|
95
|
+
// src/core/sandbox-runtime.ts
|
|
96
|
+
import { homedir } from "os";
|
|
97
|
+
import { join } from "path";
|
|
98
|
+
var LINUX_SANDBOX_RUNTIME_ROOT = "/home/ta/te_agent_ta";
|
|
99
|
+
function getDefaultSandboxRuntimeRoot() {
|
|
100
|
+
if (process.platform === "linux") {
|
|
101
|
+
return LINUX_SANDBOX_RUNTIME_ROOT;
|
|
102
|
+
}
|
|
103
|
+
return homedir();
|
|
104
|
+
}
|
|
105
|
+
function getSandboxRuntimeRoot() {
|
|
106
|
+
return process.env.SANDBOX_RUNTIME_ROOT || getDefaultSandboxRuntimeRoot();
|
|
107
|
+
}
|
|
108
|
+
function getSandboxAeConfigDir() {
|
|
109
|
+
return join(getSandboxRuntimeRoot(), ".ae-config");
|
|
110
|
+
}
|
|
111
|
+
function getSandboxMcpTokenFilePath() {
|
|
112
|
+
return join(getSandboxAeConfigDir(), "mcp-token.json");
|
|
113
|
+
}
|
|
114
|
+
function getSandboxClusterInfoFilePath() {
|
|
115
|
+
return join(getSandboxAeConfigDir(), "cluster-info.json");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// src/core/config.ts
|
|
119
|
+
var CONFIG_DIR = path2.join(process.env.HOME || "", ".ae-cli");
|
|
120
|
+
var CONFIG_FILE = path2.join(CONFIG_DIR, "config.json");
|
|
121
|
+
function sandboxMcpTokenCandidatePaths() {
|
|
122
|
+
const paths = [getSandboxMcpTokenFilePath()];
|
|
123
|
+
const homeFallback = path2.join(process.env.HOME || "", ".ae-config", "mcp-token.json");
|
|
124
|
+
if (!paths.includes(homeFallback)) {
|
|
125
|
+
paths.push(homeFallback);
|
|
126
|
+
}
|
|
127
|
+
return paths;
|
|
128
|
+
}
|
|
129
|
+
function readSandboxMcpTokenEntry() {
|
|
130
|
+
for (const file of sandboxMcpTokenCandidatePaths()) {
|
|
131
|
+
if (!fs3.existsSync(file)) continue;
|
|
132
|
+
try {
|
|
133
|
+
const data = safeReadJsonFile(file);
|
|
134
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
135
|
+
const url = data.url;
|
|
136
|
+
const token = data["mcp-token"];
|
|
137
|
+
if (url && token) {
|
|
138
|
+
return { url, token };
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
} catch {
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
function ensureDir() {
|
|
147
|
+
if (!fs3.existsSync(CONFIG_DIR)) fs3.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
148
|
+
}
|
|
149
|
+
function forceMigrateFromFallback() {
|
|
150
|
+
const entry = readSandboxMcpTokenEntry();
|
|
151
|
+
if (!entry) return null;
|
|
152
|
+
return { [entry.url]: entry.token };
|
|
153
|
+
}
|
|
154
|
+
function getFallbackMcpToken(hostUrl) {
|
|
155
|
+
const store = forceMigrateFromFallback();
|
|
156
|
+
if (!store) return null;
|
|
157
|
+
if (hostUrl && store[hostUrl]) return store[hostUrl];
|
|
158
|
+
const entries = Object.values(store);
|
|
159
|
+
if (entries.length === 1) return entries[0];
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
function loadConfig() {
|
|
163
|
+
try {
|
|
164
|
+
if (fs3.existsSync(CONFIG_FILE)) {
|
|
165
|
+
const raw = safeReadJsonFile(CONFIG_FILE);
|
|
166
|
+
if (raw.defaultHost && !raw.activeHost) {
|
|
167
|
+
const migrated = migrateConfig(raw);
|
|
168
|
+
saveConfig(migrated);
|
|
169
|
+
return migrated;
|
|
170
|
+
}
|
|
171
|
+
return raw;
|
|
172
|
+
}
|
|
173
|
+
} catch (err) {
|
|
174
|
+
logger.error(`Error loading config: ${err.message}`);
|
|
175
|
+
console.error(`Error loading config: ${err.message}`);
|
|
176
|
+
}
|
|
177
|
+
return { activeHost: "", hosts: {} };
|
|
178
|
+
}
|
|
179
|
+
function migrateConfig(old) {
|
|
180
|
+
const hosts = {};
|
|
181
|
+
const oldHost = old.defaultHost;
|
|
182
|
+
if (old.hosts) {
|
|
183
|
+
for (const [key, val] of Object.entries(old.hosts)) {
|
|
184
|
+
const url = key.startsWith("http") ? key : `https://${key}`;
|
|
185
|
+
hosts[url] = val;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const activeHost = oldHost.startsWith("http") ? oldHost : `https://${oldHost}`;
|
|
189
|
+
if (!hosts[activeHost]) {
|
|
190
|
+
hosts[activeHost] = { label: "default" };
|
|
191
|
+
}
|
|
192
|
+
return { activeHost, hosts };
|
|
193
|
+
}
|
|
194
|
+
function saveConfig(config) {
|
|
195
|
+
ensureDir();
|
|
196
|
+
fs3.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
197
|
+
logger.info(`Config saved: activeHost=${config.activeHost}, hosts=${Object.keys(config.hosts).length}`);
|
|
198
|
+
}
|
|
199
|
+
function getActiveHost() {
|
|
200
|
+
const config = loadConfig();
|
|
201
|
+
if (config.activeHost) return config.activeHost;
|
|
202
|
+
return readSandboxMcpTokenEntry()?.url ?? "";
|
|
203
|
+
}
|
|
204
|
+
function setActiveHost(url) {
|
|
205
|
+
const config = loadConfig();
|
|
206
|
+
if (!config.hosts[url]) {
|
|
207
|
+
throw new Error(`Host ${url} is not in the configured hosts list. Add it first.`);
|
|
208
|
+
}
|
|
209
|
+
config.activeHost = url;
|
|
210
|
+
saveConfig(config);
|
|
211
|
+
}
|
|
212
|
+
function addHost(url, label) {
|
|
213
|
+
const config = loadConfig();
|
|
214
|
+
assertUniqueHostLabel(config, label, url);
|
|
215
|
+
config.hosts[url] = { label };
|
|
216
|
+
if (!config.activeHost) {
|
|
217
|
+
config.activeHost = url;
|
|
218
|
+
}
|
|
219
|
+
saveConfig(config);
|
|
220
|
+
}
|
|
221
|
+
function assertUniqueHostLabel(config, label, excludeUrl) {
|
|
222
|
+
const duplicate = Object.entries(config.hosts).find(([url, entry]) => url !== excludeUrl && entry.label === label);
|
|
223
|
+
if (duplicate) {
|
|
224
|
+
throw new Error(`Label already exists: ${label}. Rename the existing label first: ${duplicate[0]}`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function getConfigDir() {
|
|
228
|
+
return CONFIG_DIR;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export {
|
|
232
|
+
logger,
|
|
233
|
+
safeJsonParse,
|
|
234
|
+
safeReadJsonFile,
|
|
235
|
+
getSandboxRuntimeRoot,
|
|
236
|
+
getSandboxClusterInfoFilePath,
|
|
237
|
+
getFallbackMcpToken,
|
|
238
|
+
loadConfig,
|
|
239
|
+
saveConfig,
|
|
240
|
+
getActiveHost,
|
|
241
|
+
setActiveHost,
|
|
242
|
+
addHost,
|
|
243
|
+
getConfigDir
|
|
244
|
+
};
|