@thinkingai/ae-cli 1.0.24 → 1.0.27
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 +17 -2
- package/README.zh.md +18 -2
- package/dist/{auth-4OPL4YWG.js → auth-TXFJHPXU.js} +3 -2
- package/dist/{auth-PBPAZQWP.js → auth-W2JZ3DKA.js} +7 -5
- package/dist/{chunk-OVHL4JAC.js → chunk-2NGWYMLB.js} +5 -3
- package/dist/chunk-6T7245YE.js +478 -0
- package/dist/{chunk-JJCRURTR.js → chunk-B4UL2VIJ.js} +1 -1
- package/dist/{chunk-DC66L5AM.js → chunk-BI5ZVD6T.js} +17 -90
- package/dist/chunk-MNHE6SSI.js +79 -0
- package/dist/chunk-MWWYGZ76.js +130 -0
- package/dist/{chunk-47MTN54I.js → chunk-SRRUNQIM.js} +29 -2
- package/dist/chunk-TG64AQV4.js +262 -0
- package/dist/{chunk-MZW6NOVS.js → chunk-VT6VC4PA.js} +9 -3
- package/dist/{client-TDH6MUWM.js → client-IU2E5IOL.js} +6 -3
- package/dist/{config-5ELRYAIH.js → config-WH3R3IRY.js} +3 -2
- package/dist/index.js +45 -17
- package/dist/model-CC7U5XOJ.js +131 -0
- package/dist/{raw-EM7AP3RT.js → raw-TUHZQ4UB.js} +5 -4
- package/dist/sync-EVEDS7YE.js +383 -0
- package/dist/te-agent-FLTDYV3L.js +522 -0
- package/dist/{te-analysis-3KFSNUCJ.js → te-analysis-CMXADIJM.js} +81 -11
- package/dist/{te-audience-GMYJFCSC.js → te-audience-LPZFFSXM.js} +4 -3
- package/dist/{te-common-YLERCMQI.js → te-common-BXQFKTB3.js} +4 -3
- package/dist/{te-community-PYDN5A6B.js → te-community-K5S3VOZY.js} +4 -3
- package/dist/{te-dataops-ETWS5O3X.js → te-dataops-567IMCQG.js} +4 -3
- package/dist/{te-engage-ROJVZHDA.js → te-engage-TBUTGW5F.js} +105 -49
- package/dist/{te-kb-VUNS2CC7.js → te-kb-XHVBWP5E.js} +225 -29
- package/dist/{te-meta-3EFWLM3A.js → te-meta-NQ4U3VJW.js} +4 -3
- package/dist/te-team-556APRIU.js +529 -0
- package/package.json +5 -1
- package/skills/ae-agent/SKILL.md +133 -0
- package/skills/ae-analysis/references/drilldown_user_events.md +1 -1
- package/skills/ae-analysis/references/drilldown_users.md +2 -2
- package/skills/ae-analysis/references/query_adhoc.md +3 -2
- package/skills/ae-analysis/references/query_entity_details.md +1 -1
- package/skills/ae-engage/SKILL.md +25 -2
- package/skills/ae-engage/references/build-task-save-guide.md +283 -0
- package/skills/ae-engage/references/save-task.md +304 -0
- package/skills/ae-kb/SKILL.md +283 -0
- package/skills/ae-team/SKILL.md +164 -0
- package/skills/ae-team/references/ai-generate.md +39 -0
- package/skills/ae-team/references/create.md +94 -0
- package/skills/ae-team/references/delete.md +39 -0
- package/skills/ae-team/references/list-projects.md +45 -0
- package/skills/ae-team/references/list-templates.md +38 -0
- package/skills/ae-team/references/list.md +39 -0
- package/skills/ae-team/references/run-artifacts.md +51 -0
- package/skills/ae-team/references/run-cancel.md +38 -0
- package/skills/ae-team/references/run-chat.md +57 -0
- package/skills/ae-team/references/run-reply.md +41 -0
- package/skills/ae-team/references/run-result.md +75 -0
- package/skills/ae-team/references/run-start.md +73 -0
- package/skills/ae-team/references/run-watch.md +82 -0
- package/skills/ae-team/references/update.md +47 -0
|
@@ -1,81 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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();
|
|
1
|
+
import {
|
|
2
|
+
logger
|
|
3
|
+
} from "./chunk-MNHE6SSI.js";
|
|
76
4
|
|
|
77
5
|
// src/core/json-utils.ts
|
|
78
|
-
import
|
|
6
|
+
import fs from "fs";
|
|
79
7
|
import { createRequire } from "module";
|
|
80
8
|
var require2 = createRequire(import.meta.url);
|
|
81
9
|
var JSONbig = require2("json-bigint");
|
|
@@ -84,28 +12,28 @@ function safeJsonParse(text) {
|
|
|
84
12
|
return jsonParser.parse(text);
|
|
85
13
|
}
|
|
86
14
|
function safeReadJsonFile(filePath) {
|
|
87
|
-
const content =
|
|
15
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
88
16
|
return safeJsonParse(content);
|
|
89
17
|
}
|
|
90
18
|
|
|
91
19
|
// src/core/config.ts
|
|
92
|
-
import
|
|
93
|
-
import
|
|
94
|
-
var CONFIG_DIR =
|
|
95
|
-
var CONFIG_FILE =
|
|
96
|
-
var MCP_TOKENS_FILE =
|
|
97
|
-
var FALLBACK_MCP_TOKEN_FILE = "/
|
|
20
|
+
import fs2 from "fs";
|
|
21
|
+
import path from "path";
|
|
22
|
+
var CONFIG_DIR = path.join(process.env.HOME || "", ".ae-cli");
|
|
23
|
+
var CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
|
|
24
|
+
var MCP_TOKENS_FILE = path.join(CONFIG_DIR, "mcp-tokens.json");
|
|
25
|
+
var FALLBACK_MCP_TOKEN_FILE = "/home/ta/te_agent_ta/.ae-config/mcp-token.json";
|
|
98
26
|
function ensureDir() {
|
|
99
|
-
if (!
|
|
27
|
+
if (!fs2.existsSync(CONFIG_DIR)) fs2.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
100
28
|
}
|
|
101
29
|
function migrateFromFallback() {
|
|
102
|
-
if (!
|
|
30
|
+
if (!fs2.existsSync(FALLBACK_MCP_TOKEN_FILE) || fs2.existsSync(MCP_TOKENS_FILE)) {
|
|
103
31
|
return null;
|
|
104
32
|
}
|
|
105
33
|
return doMigrateFromFallback();
|
|
106
34
|
}
|
|
107
35
|
function forceMigrateFromFallback() {
|
|
108
|
-
if (!
|
|
36
|
+
if (!fs2.existsSync(FALLBACK_MCP_TOKEN_FILE)) {
|
|
109
37
|
return null;
|
|
110
38
|
}
|
|
111
39
|
return doMigrateFromFallback();
|
|
@@ -123,7 +51,7 @@ function doMigrateFromFallback() {
|
|
|
123
51
|
}
|
|
124
52
|
if (Object.keys(tokenStore).length > 0) {
|
|
125
53
|
ensureDir();
|
|
126
|
-
|
|
54
|
+
fs2.writeFileSync(MCP_TOKENS_FILE, JSON.stringify(tokenStore, null, 2));
|
|
127
55
|
return tokenStore;
|
|
128
56
|
}
|
|
129
57
|
} catch {
|
|
@@ -133,7 +61,7 @@ function doMigrateFromFallback() {
|
|
|
133
61
|
function loadConfig() {
|
|
134
62
|
try {
|
|
135
63
|
const migratedTokens = migrateFromFallback();
|
|
136
|
-
if (
|
|
64
|
+
if (fs2.existsSync(CONFIG_FILE)) {
|
|
137
65
|
const raw = safeReadJsonFile(CONFIG_FILE);
|
|
138
66
|
if (raw.defaultHost && !raw.activeHost) {
|
|
139
67
|
const migrated = migrateConfig(raw);
|
|
@@ -182,7 +110,7 @@ function migrateConfig(old) {
|
|
|
182
110
|
}
|
|
183
111
|
function saveConfig(config) {
|
|
184
112
|
ensureDir();
|
|
185
|
-
|
|
113
|
+
fs2.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
186
114
|
logger.info(`Config saved: activeHost=${config.activeHost}, hosts=${Object.keys(config.hosts).length}`);
|
|
187
115
|
}
|
|
188
116
|
function getActiveHost() {
|
|
@@ -242,7 +170,6 @@ function extractHostname(fullUrl) {
|
|
|
242
170
|
}
|
|
243
171
|
|
|
244
172
|
export {
|
|
245
|
-
logger,
|
|
246
173
|
safeJsonParse,
|
|
247
174
|
safeReadJsonFile,
|
|
248
175
|
forceMigrateFromFallback,
|
|
@@ -0,0 +1,79 @@
|
|
|
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
|
+
* 写入日志到文件(不输出到控制台,控制台由现有 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
|
+
export {
|
|
78
|
+
logger
|
|
79
|
+
};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import {
|
|
2
|
+
loadMcpTokenStore
|
|
3
|
+
} from "./chunk-2NGWYMLB.js";
|
|
4
|
+
import {
|
|
5
|
+
forceMigrateFromFallback,
|
|
6
|
+
safeJsonParse
|
|
7
|
+
} from "./chunk-BI5ZVD6T.js";
|
|
8
|
+
import {
|
|
9
|
+
logger
|
|
10
|
+
} from "./chunk-MNHE6SSI.js";
|
|
11
|
+
|
|
12
|
+
// src/core/mcp-access.ts
|
|
13
|
+
function buildUrl(host, path, params = {}) {
|
|
14
|
+
const base = host.replace(/\/$/, "");
|
|
15
|
+
const p = path.startsWith("/") ? path : `/${path}`;
|
|
16
|
+
const url = new URL(`${base}${p}`);
|
|
17
|
+
for (const [key, value] of Object.entries(params)) {
|
|
18
|
+
if (value !== void 0 && value !== null) {
|
|
19
|
+
url.searchParams.set(key, String(value));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return url.toString();
|
|
23
|
+
}
|
|
24
|
+
function getMcpTokenForHost(host) {
|
|
25
|
+
const store = loadMcpTokenStore();
|
|
26
|
+
const normalizedHost = host.replace(/\/+$/, "");
|
|
27
|
+
if (store[host]) return store[host];
|
|
28
|
+
if (store[normalizedHost]) return store[normalizedHost];
|
|
29
|
+
for (const [key, token] of Object.entries(store)) {
|
|
30
|
+
if (key.replace(/\/+$/, "") === normalizedHost) {
|
|
31
|
+
return token;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return void 0;
|
|
35
|
+
}
|
|
36
|
+
function getTokenFromStore(store, host) {
|
|
37
|
+
const normalizedHost = host.replace(/\/+$/, "");
|
|
38
|
+
if (store[host]) return store[host];
|
|
39
|
+
if (store[normalizedHost]) return store[normalizedHost];
|
|
40
|
+
for (const [key, token] of Object.entries(store)) {
|
|
41
|
+
if (key.replace(/\/+$/, "") === normalizedHost) {
|
|
42
|
+
return token;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return void 0;
|
|
46
|
+
}
|
|
47
|
+
function refreshMcpTokenFromFallback(host) {
|
|
48
|
+
logger.warn(`KB MCP request failed for ${host}, trying fallback`);
|
|
49
|
+
const migrated = forceMigrateFromFallback();
|
|
50
|
+
const token = migrated ? getTokenFromStore(migrated, host) : void 0;
|
|
51
|
+
if (token) {
|
|
52
|
+
logger.info(`KB MCP token refreshed from fallback for ${host}`);
|
|
53
|
+
process.stderr.write(`[ae-cli] MCP token refreshed from fallback for ${host}
|
|
54
|
+
`);
|
|
55
|
+
}
|
|
56
|
+
return token;
|
|
57
|
+
}
|
|
58
|
+
function isMcpAuthFailure(resp, data) {
|
|
59
|
+
return !resp.ok || data.return_code === -1001;
|
|
60
|
+
}
|
|
61
|
+
function parseKbResponse(resp, text) {
|
|
62
|
+
if (resp.status === 401 || resp.status === 403) {
|
|
63
|
+
throw new Error(`KB MCP token auth failed: HTTP ${resp.status} ${resp.statusText}`);
|
|
64
|
+
}
|
|
65
|
+
const data = safeJsonParse(text);
|
|
66
|
+
if (data.return_code === -1001) {
|
|
67
|
+
throw new Error(`KB MCP token auth failed: ${data.return_message || "unauthorized"} (code: ${data.return_code})`);
|
|
68
|
+
}
|
|
69
|
+
if (data.return_code !== 0 && data.return_code !== void 0) {
|
|
70
|
+
throw new Error(`AE API error: ${data.return_message || "unknown"} (code: ${data.return_code})`);
|
|
71
|
+
}
|
|
72
|
+
return data.data !== void 0 ? data.data : data;
|
|
73
|
+
}
|
|
74
|
+
async function fetchWithMcpToken(host, input, init, mcpToken) {
|
|
75
|
+
const headers = new Headers(init.headers);
|
|
76
|
+
headers.set("mcp-token", mcpToken);
|
|
77
|
+
const resp = await fetch(input, { ...init, headers });
|
|
78
|
+
const text = await resp.text();
|
|
79
|
+
if (!resp.ok) {
|
|
80
|
+
const refreshedToken = refreshMcpTokenFromFallback(host);
|
|
81
|
+
if (refreshedToken) {
|
|
82
|
+
headers.set("mcp-token", refreshedToken);
|
|
83
|
+
const retryResp = await fetch(input, { ...init, headers });
|
|
84
|
+
return parseKbResponse(retryResp, await retryResp.text());
|
|
85
|
+
}
|
|
86
|
+
return parseKbResponse(resp, text);
|
|
87
|
+
}
|
|
88
|
+
const data = safeJsonParse(text);
|
|
89
|
+
if (isMcpAuthFailure(resp, data)) {
|
|
90
|
+
const refreshedToken = refreshMcpTokenFromFallback(host);
|
|
91
|
+
if (refreshedToken) {
|
|
92
|
+
headers.set("mcp-token", refreshedToken);
|
|
93
|
+
const retryResp = await fetch(input, { ...init, headers });
|
|
94
|
+
return parseKbResponse(retryResp, await retryResp.text());
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return parseKbResponse(resp, text);
|
|
98
|
+
}
|
|
99
|
+
async function kbApi(ctx, method, path, params = {}, body) {
|
|
100
|
+
const host = ctx.host();
|
|
101
|
+
const mcpToken = getMcpTokenForHost(host);
|
|
102
|
+
if (!mcpToken) {
|
|
103
|
+
return ctx.api(method, path, params, body);
|
|
104
|
+
}
|
|
105
|
+
const upperMethod = method.toUpperCase();
|
|
106
|
+
return fetchWithMcpToken(host, buildUrl(host, path, params), {
|
|
107
|
+
method: upperMethod,
|
|
108
|
+
headers: {
|
|
109
|
+
"Content-Type": "application/json"
|
|
110
|
+
},
|
|
111
|
+
body: upperMethod === "GET" ? void 0 : JSON.stringify(body ?? {})
|
|
112
|
+
}, mcpToken);
|
|
113
|
+
}
|
|
114
|
+
async function kbUpload(ctx, path, form, params = {}) {
|
|
115
|
+
const host = ctx.host();
|
|
116
|
+
const mcpToken = getMcpTokenForHost(host);
|
|
117
|
+
if (!mcpToken) {
|
|
118
|
+
const { httpUpload } = await import("./client-IU2E5IOL.js");
|
|
119
|
+
return httpUpload(path, form, params, host);
|
|
120
|
+
}
|
|
121
|
+
return fetchWithMcpToken(host, buildUrl(host, path, params), {
|
|
122
|
+
method: "POST",
|
|
123
|
+
body: form
|
|
124
|
+
}, mcpToken);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export {
|
|
128
|
+
kbApi,
|
|
129
|
+
kbUpload
|
|
130
|
+
};
|
|
@@ -2,16 +2,19 @@ import {
|
|
|
2
2
|
extractHostname,
|
|
3
3
|
getActiveHost,
|
|
4
4
|
getConfigDir,
|
|
5
|
-
logger,
|
|
6
5
|
safeJsonParse,
|
|
7
6
|
safeReadJsonFile
|
|
8
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-BI5ZVD6T.js";
|
|
8
|
+
import {
|
|
9
|
+
logger
|
|
10
|
+
} from "./chunk-MNHE6SSI.js";
|
|
9
11
|
|
|
10
12
|
// src/core/auth.ts
|
|
11
13
|
import fs from "fs";
|
|
12
14
|
import path from "path";
|
|
13
15
|
import { execFileSync } from "child_process";
|
|
14
16
|
var TOKENS_FILE = path.join(getConfigDir(), "tokens.json");
|
|
17
|
+
var MCP_TOKENS_FILE = path.join(getConfigDir(), "mcp-tokens.json");
|
|
15
18
|
var LEGACY_DIR = path.join(process.env.HOME || "", ".te-mcp");
|
|
16
19
|
var TOKEN_TTL_MS = 20 * 60 * 60 * 1e3;
|
|
17
20
|
var OSASCRIPT_POLL_INTERVAL_MS = 2e3;
|
|
@@ -113,6 +116,23 @@ function getAuthStatus(hostUrl) {
|
|
|
113
116
|
}
|
|
114
117
|
return { authenticated: false, host: hostUrl };
|
|
115
118
|
}
|
|
119
|
+
function getCachedMcpToken(hostUrl) {
|
|
120
|
+
try {
|
|
121
|
+
if (!fs.existsSync(MCP_TOKENS_FILE)) return void 0;
|
|
122
|
+
const store = safeReadJsonFile(MCP_TOKENS_FILE);
|
|
123
|
+
const normalizedHost = hostUrl.replace(/\/+$/, "");
|
|
124
|
+
if (store[hostUrl]) return store[hostUrl];
|
|
125
|
+
if (store[normalizedHost]) return store[normalizedHost];
|
|
126
|
+
for (const [key, token] of Object.entries(store)) {
|
|
127
|
+
if (key.replace(/\/+$/, "") === normalizedHost) {
|
|
128
|
+
return token;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return void 0;
|
|
132
|
+
} catch {
|
|
133
|
+
return void 0;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
116
136
|
function extractTokenViaOsascript(hostUrl) {
|
|
117
137
|
if (process.platform !== "darwin") return { token: null, error: "not_mac" };
|
|
118
138
|
const hostname = extractHostname(hostUrl);
|
|
@@ -242,6 +262,13 @@ Run: ae-cli config set-host`
|
|
|
242
262
|
logger.info(`Using cached token for ${hostUrl}`);
|
|
243
263
|
return cached.token;
|
|
244
264
|
}
|
|
265
|
+
if (getCachedMcpToken(hostUrl)) {
|
|
266
|
+
logger.info(`MCP token exists for ${hostUrl}; skipping browser token extraction`);
|
|
267
|
+
throw new Error(
|
|
268
|
+
`MCP token already exists for ${hostUrl}; skipped browser login.
|
|
269
|
+
Use an MCP-token aware command, or run: ae-cli auth set-token <token>`
|
|
270
|
+
);
|
|
271
|
+
}
|
|
245
272
|
const { token: osascriptToken, error } = extractTokenViaOsascript(hostUrl);
|
|
246
273
|
if (error === "no_js_permission") {
|
|
247
274
|
logger.warn("Chrome JS Apple Events not enabled");
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
// src/core/te-agent-credentials.ts
|
|
2
|
+
import { readFileSync } from "fs";
|
|
3
|
+
import { homedir } from "os";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
function getCredentialsPath() {
|
|
6
|
+
return join(process.env.HOME || homedir(), ".te-agent", "credentials.json");
|
|
7
|
+
}
|
|
8
|
+
function getPersistentEnvPath() {
|
|
9
|
+
const runtimeRoot = process.env.SANDBOX_RUNTIME_ROOT ?? "/home/ta/te_agent_ta";
|
|
10
|
+
return join(runtimeRoot, ".env");
|
|
11
|
+
}
|
|
12
|
+
function parseDotEnv(raw) {
|
|
13
|
+
const result = {};
|
|
14
|
+
for (const line of raw.split("\n")) {
|
|
15
|
+
if (/^\s*#/.test(line) || line.trim() === "") continue;
|
|
16
|
+
const match = /^\s*([^=]+)=(.*)$/.exec(line);
|
|
17
|
+
if (!match) continue;
|
|
18
|
+
const key = match[1].trim();
|
|
19
|
+
let value = match[2].trim();
|
|
20
|
+
if (value.startsWith('"') && value.endsWith('"')) {
|
|
21
|
+
value = value.slice(1, -1).replace(/\\"/g, '"');
|
|
22
|
+
}
|
|
23
|
+
if (value !== "") result[key] = value;
|
|
24
|
+
}
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
function readPersistentEnv() {
|
|
28
|
+
const envPath = getPersistentEnvPath();
|
|
29
|
+
try {
|
|
30
|
+
const raw = readFileSync(envPath, "utf8");
|
|
31
|
+
return parseDotEnv(raw);
|
|
32
|
+
} catch (err) {
|
|
33
|
+
if (err?.code === "ENOENT") return null;
|
|
34
|
+
throw new TeAgentCredentialsError(
|
|
35
|
+
`${envPath} \u8BFB\u53D6\u5931\u8D25\uFF1A${err?.message ?? err}`,
|
|
36
|
+
"\u8BF7\u68C0\u67E5\u6587\u4EF6\u6743\u9650\uFF0C\u6216\u901A\u8FC7 TE_CLAUDE_BASE_URL / SANDBOX_ID / SECRET_KEY \u6CE8\u5165\u8FD0\u884C\u65F6\u51ED\u8BC1"
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
var TeAgentCredentialsError = class extends Error {
|
|
41
|
+
constructor(message, hint) {
|
|
42
|
+
super(message);
|
|
43
|
+
this.hint = hint;
|
|
44
|
+
this.name = "TeAgentCredentialsError";
|
|
45
|
+
}
|
|
46
|
+
hint;
|
|
47
|
+
};
|
|
48
|
+
function readLegacyCredentials() {
|
|
49
|
+
const credPath = getCredentialsPath();
|
|
50
|
+
try {
|
|
51
|
+
const raw = readFileSync(credPath, "utf8");
|
|
52
|
+
const parsed = JSON.parse(raw);
|
|
53
|
+
const main = parsed?.mainApp;
|
|
54
|
+
return main && typeof main === "object" ? main : null;
|
|
55
|
+
} catch (err) {
|
|
56
|
+
if (err?.code === "ENOENT") return null;
|
|
57
|
+
if (err instanceof SyntaxError) {
|
|
58
|
+
throw new TeAgentCredentialsError(
|
|
59
|
+
`${credPath} \u89E3\u6790\u5931\u8D25\uFF1A${err.message}`,
|
|
60
|
+
"\u51ED\u8BC1\u6587\u4EF6\u53EF\u80FD\u5DF2\u635F\u574F\uFF1B\u4E5F\u53EF\u4EE5\u901A\u8FC7 TE_CLAUDE_BASE_URL / SANDBOX_ID / SECRET_KEY \u6CE8\u5165\u8FD0\u884C\u65F6\u51ED\u8BC1"
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
throw new TeAgentCredentialsError(
|
|
64
|
+
`${credPath} \u8BFB\u53D6\u5931\u8D25\uFF1A${err?.message ?? err}`,
|
|
65
|
+
"\u8BF7\u68C0\u67E5\u6587\u4EF6\u6743\u9650\uFF0C\u6216\u901A\u8FC7 TE_CLAUDE_BASE_URL / SANDBOX_ID / SECRET_KEY \u6CE8\u5165\u8FD0\u884C\u65F6\u51ED\u8BC1"
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function loadTeAgentCredentials() {
|
|
70
|
+
const legacy = readLegacyCredentials();
|
|
71
|
+
const credPath = getCredentialsPath();
|
|
72
|
+
const envPath = getPersistentEnvPath();
|
|
73
|
+
const persistent = readPersistentEnv();
|
|
74
|
+
const url = process.env.TE_CLAUDE_BASE_URL || persistent?.TE_CLAUDE_BASE_URL || legacy?.url;
|
|
75
|
+
const sandboxId = process.env.SANDBOX_ID || persistent?.SANDBOX_ID || legacy?.sandboxId;
|
|
76
|
+
const sandboxSecretKey = process.env.SECRET_KEY || process.env.SANDBOX_SECRET_KEY || persistent?.SECRET_KEY || persistent?.SANDBOX_SECRET_KEY || legacy?.sandboxSecretKey || legacy?.secretKey;
|
|
77
|
+
if (typeof url !== "string" || !url) {
|
|
78
|
+
throw new TeAgentCredentialsError(
|
|
79
|
+
"TE_CLAUDE_BASE_URL \u7F3A\u5931",
|
|
80
|
+
`\u8BF7\u5728 te-agent \u6C99\u7BB1\u5185\u6267\u884C\uFF0C\u6216\u914D\u7F6E ${envPath} / ${credPath}`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
if (typeof sandboxId !== "string" || !sandboxId) {
|
|
84
|
+
throw new TeAgentCredentialsError(
|
|
85
|
+
"SANDBOX_ID \u7F3A\u5931",
|
|
86
|
+
`\u8BF7\u5728 te-agent \u6C99\u7BB1\u5185\u6267\u884C\uFF0C\u6216\u914D\u7F6E ${envPath} / ${credPath}`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
if (typeof sandboxSecretKey !== "string" || !sandboxSecretKey) {
|
|
90
|
+
throw new TeAgentCredentialsError(
|
|
91
|
+
"SECRET_KEY \u7F3A\u5931",
|
|
92
|
+
`\u8BF7\u5728 te-agent \u6C99\u7BB1\u5185\u6267\u884C\uFF0C\u6216\u914D\u7F6E ${envPath} / ${credPath}`
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
return { mainApp: { url, sandboxId, sandboxSecretKey } };
|
|
96
|
+
}
|
|
97
|
+
function getClaudeConfigDir() {
|
|
98
|
+
const fromEnv = process.env.CLAUDE_CONFIG_DIR;
|
|
99
|
+
if (typeof fromEnv === "string" && fromEnv.length > 0) {
|
|
100
|
+
return fromEnv;
|
|
101
|
+
}
|
|
102
|
+
return join(homedir(), ".claude");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// src/core/te-agent-client.ts
|
|
106
|
+
var TeAgentApiError = class extends Error {
|
|
107
|
+
constructor(message, status, code, body) {
|
|
108
|
+
super(message);
|
|
109
|
+
this.status = status;
|
|
110
|
+
this.code = code;
|
|
111
|
+
this.body = body;
|
|
112
|
+
this.name = "TeAgentApiError";
|
|
113
|
+
}
|
|
114
|
+
status;
|
|
115
|
+
code;
|
|
116
|
+
body;
|
|
117
|
+
};
|
|
118
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
119
|
+
var UPLOAD_TIMEOUT_MS = 12e4;
|
|
120
|
+
async function fetchWithTimeout(url, init, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
121
|
+
const controller = new AbortController();
|
|
122
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
123
|
+
try {
|
|
124
|
+
return await fetch(url, { ...init, signal: controller.signal });
|
|
125
|
+
} catch (err) {
|
|
126
|
+
if (err?.name === "AbortError") {
|
|
127
|
+
throw new TeAgentApiError(`\u8BF7\u6C42\u8D85\u65F6\uFF08${timeoutMs}ms\uFF09\uFF1A${url}`, 0, "TIMEOUT");
|
|
128
|
+
}
|
|
129
|
+
throw new TeAgentApiError(`\u7F51\u7EDC\u8BF7\u6C42\u5931\u8D25\uFF1A${err?.message ?? err}`, 0, "NETWORK_ERROR");
|
|
130
|
+
} finally {
|
|
131
|
+
clearTimeout(timer);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function signRequest(method, path, rawBody) {
|
|
135
|
+
const cred = loadTeAgentCredentials();
|
|
136
|
+
const { url: baseUrl, sandboxId, sandboxSecretKey } = cred.mainApp;
|
|
137
|
+
const headers = {
|
|
138
|
+
"X-Sandbox-Id": sandboxId,
|
|
139
|
+
"X-Sandbox-Secret-Key": sandboxSecretKey
|
|
140
|
+
};
|
|
141
|
+
if (method === "POST" || method === "PATCH") {
|
|
142
|
+
headers["Content-Type"] = "application/json";
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
url: `${baseUrl.replace(/\/$/, "")}${path}`,
|
|
146
|
+
headers,
|
|
147
|
+
rawBody
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
async function parseResponse(response, defaultErrorPrefix) {
|
|
151
|
+
const text = await response.text();
|
|
152
|
+
let parsed = void 0;
|
|
153
|
+
if (text) {
|
|
154
|
+
try {
|
|
155
|
+
parsed = JSON.parse(text);
|
|
156
|
+
} catch {
|
|
157
|
+
parsed = text;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (!response.ok) {
|
|
161
|
+
const message = typeof parsed === "object" && parsed && typeof parsed.error === "string" ? parsed.error : `${defaultErrorPrefix} ${response.status}`;
|
|
162
|
+
const code = typeof parsed === "object" && parsed && typeof parsed.code === "string" ? parsed.code : void 0;
|
|
163
|
+
throw new TeAgentApiError(message, response.status, code, parsed);
|
|
164
|
+
}
|
|
165
|
+
return parsed;
|
|
166
|
+
}
|
|
167
|
+
async function postToMainApp(path, body) {
|
|
168
|
+
const rawBody = JSON.stringify(body);
|
|
169
|
+
const signed = signRequest("POST", path, rawBody);
|
|
170
|
+
const response = await fetchWithTimeout(signed.url, {
|
|
171
|
+
method: "POST",
|
|
172
|
+
headers: signed.headers,
|
|
173
|
+
body: signed.rawBody
|
|
174
|
+
});
|
|
175
|
+
return parseResponse(response, "\u4E3B\u5E94\u7528\u8FD4\u56DE");
|
|
176
|
+
}
|
|
177
|
+
async function getFromMainApp(path) {
|
|
178
|
+
const signed = signRequest("GET", path, "");
|
|
179
|
+
const response = await fetchWithTimeout(signed.url, {
|
|
180
|
+
method: "GET",
|
|
181
|
+
headers: signed.headers
|
|
182
|
+
});
|
|
183
|
+
return parseResponse(response, "\u4E3B\u5E94\u7528\u8FD4\u56DE");
|
|
184
|
+
}
|
|
185
|
+
async function getSandboxModels(currentModelId) {
|
|
186
|
+
const qs = currentModelId ? `?current=${encodeURIComponent(currentModelId)}` : "";
|
|
187
|
+
const data = await getFromMainApp(`/api/sandbox/models${qs}`);
|
|
188
|
+
return Array.isArray(data?.models) ? data.models : [];
|
|
189
|
+
}
|
|
190
|
+
async function postSandboxModelSelection(args) {
|
|
191
|
+
return postToMainApp("/api/sandbox/models/select", args);
|
|
192
|
+
}
|
|
193
|
+
async function getSandboxSyncPullCandidates(args) {
|
|
194
|
+
const qs = new URLSearchParams({
|
|
195
|
+
workspacePath: args.workspacePath,
|
|
196
|
+
kind: args.kind
|
|
197
|
+
});
|
|
198
|
+
return getFromMainApp(
|
|
199
|
+
`/api/sandbox/sync/pull/candidates?${qs.toString()}`
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
async function postSandboxSyncPull(args) {
|
|
203
|
+
return postToMainApp("/api/sandbox/sync/pull", {
|
|
204
|
+
workspacePath: args.workspacePath,
|
|
205
|
+
kind: args.kind,
|
|
206
|
+
skills: args.skills,
|
|
207
|
+
mcp: args.mcp,
|
|
208
|
+
mode: "merge",
|
|
209
|
+
ifUnmodifiedSince: args.ifUnmodifiedSince
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
async function deleteFromMainApp(path) {
|
|
213
|
+
const signed = signRequest("DELETE", path, "");
|
|
214
|
+
const response = await fetchWithTimeout(signed.url, {
|
|
215
|
+
method: "DELETE",
|
|
216
|
+
headers: signed.headers
|
|
217
|
+
});
|
|
218
|
+
return parseResponse(response, "\u4E3B\u5E94\u7528\u8FD4\u56DE");
|
|
219
|
+
}
|
|
220
|
+
async function patchToMainApp(path, body) {
|
|
221
|
+
const rawBody = JSON.stringify(body);
|
|
222
|
+
const signed = signRequest("PATCH", path, rawBody);
|
|
223
|
+
const response = await fetchWithTimeout(signed.url, {
|
|
224
|
+
method: "PATCH",
|
|
225
|
+
headers: signed.headers,
|
|
226
|
+
body: signed.rawBody
|
|
227
|
+
});
|
|
228
|
+
return parseResponse(response, "\u4E3B\u5E94\u7528\u8FD4\u56DE");
|
|
229
|
+
}
|
|
230
|
+
async function uploadToMainApp(path, formData) {
|
|
231
|
+
const cred = loadTeAgentCredentials();
|
|
232
|
+
const baseUrl = cred.mainApp.url.replace(/\/$/, "");
|
|
233
|
+
const url = `${baseUrl}${path}`;
|
|
234
|
+
const response = await fetchWithTimeout(
|
|
235
|
+
url,
|
|
236
|
+
{
|
|
237
|
+
method: "POST",
|
|
238
|
+
headers: {
|
|
239
|
+
"X-Sandbox-Id": cred.mainApp.sandboxId,
|
|
240
|
+
"X-Sandbox-Secret-Key": cred.mainApp.sandboxSecretKey
|
|
241
|
+
},
|
|
242
|
+
body: formData
|
|
243
|
+
},
|
|
244
|
+
UPLOAD_TIMEOUT_MS
|
|
245
|
+
);
|
|
246
|
+
return parseResponse(response, "\u4E0A\u4F20\u5931\u8D25");
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export {
|
|
250
|
+
TeAgentCredentialsError,
|
|
251
|
+
getClaudeConfigDir,
|
|
252
|
+
TeAgentApiError,
|
|
253
|
+
postToMainApp,
|
|
254
|
+
getFromMainApp,
|
|
255
|
+
getSandboxModels,
|
|
256
|
+
postSandboxModelSelection,
|
|
257
|
+
getSandboxSyncPullCandidates,
|
|
258
|
+
postSandboxSyncPull,
|
|
259
|
+
deleteFromMainApp,
|
|
260
|
+
patchToMainApp,
|
|
261
|
+
uploadToMainApp
|
|
262
|
+
};
|