@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.
Files changed (54) hide show
  1. package/README.md +17 -2
  2. package/README.zh.md +18 -2
  3. package/dist/{auth-4OPL4YWG.js → auth-TXFJHPXU.js} +3 -2
  4. package/dist/{auth-PBPAZQWP.js → auth-W2JZ3DKA.js} +7 -5
  5. package/dist/{chunk-OVHL4JAC.js → chunk-2NGWYMLB.js} +5 -3
  6. package/dist/chunk-6T7245YE.js +478 -0
  7. package/dist/{chunk-JJCRURTR.js → chunk-B4UL2VIJ.js} +1 -1
  8. package/dist/{chunk-DC66L5AM.js → chunk-BI5ZVD6T.js} +17 -90
  9. package/dist/chunk-MNHE6SSI.js +79 -0
  10. package/dist/chunk-MWWYGZ76.js +130 -0
  11. package/dist/{chunk-47MTN54I.js → chunk-SRRUNQIM.js} +29 -2
  12. package/dist/chunk-TG64AQV4.js +262 -0
  13. package/dist/{chunk-MZW6NOVS.js → chunk-VT6VC4PA.js} +9 -3
  14. package/dist/{client-TDH6MUWM.js → client-IU2E5IOL.js} +6 -3
  15. package/dist/{config-5ELRYAIH.js → config-WH3R3IRY.js} +3 -2
  16. package/dist/index.js +45 -17
  17. package/dist/model-CC7U5XOJ.js +131 -0
  18. package/dist/{raw-EM7AP3RT.js → raw-TUHZQ4UB.js} +5 -4
  19. package/dist/sync-EVEDS7YE.js +383 -0
  20. package/dist/te-agent-FLTDYV3L.js +522 -0
  21. package/dist/{te-analysis-3KFSNUCJ.js → te-analysis-CMXADIJM.js} +81 -11
  22. package/dist/{te-audience-GMYJFCSC.js → te-audience-LPZFFSXM.js} +4 -3
  23. package/dist/{te-common-YLERCMQI.js → te-common-BXQFKTB3.js} +4 -3
  24. package/dist/{te-community-PYDN5A6B.js → te-community-K5S3VOZY.js} +4 -3
  25. package/dist/{te-dataops-ETWS5O3X.js → te-dataops-567IMCQG.js} +4 -3
  26. package/dist/{te-engage-ROJVZHDA.js → te-engage-TBUTGW5F.js} +105 -49
  27. package/dist/{te-kb-VUNS2CC7.js → te-kb-XHVBWP5E.js} +225 -29
  28. package/dist/{te-meta-3EFWLM3A.js → te-meta-NQ4U3VJW.js} +4 -3
  29. package/dist/te-team-556APRIU.js +529 -0
  30. package/package.json +5 -1
  31. package/skills/ae-agent/SKILL.md +133 -0
  32. package/skills/ae-analysis/references/drilldown_user_events.md +1 -1
  33. package/skills/ae-analysis/references/drilldown_users.md +2 -2
  34. package/skills/ae-analysis/references/query_adhoc.md +3 -2
  35. package/skills/ae-analysis/references/query_entity_details.md +1 -1
  36. package/skills/ae-engage/SKILL.md +25 -2
  37. package/skills/ae-engage/references/build-task-save-guide.md +283 -0
  38. package/skills/ae-engage/references/save-task.md +304 -0
  39. package/skills/ae-kb/SKILL.md +283 -0
  40. package/skills/ae-team/SKILL.md +164 -0
  41. package/skills/ae-team/references/ai-generate.md +39 -0
  42. package/skills/ae-team/references/create.md +94 -0
  43. package/skills/ae-team/references/delete.md +39 -0
  44. package/skills/ae-team/references/list-projects.md +45 -0
  45. package/skills/ae-team/references/list-templates.md +38 -0
  46. package/skills/ae-team/references/list.md +39 -0
  47. package/skills/ae-team/references/run-artifacts.md +51 -0
  48. package/skills/ae-team/references/run-cancel.md +38 -0
  49. package/skills/ae-team/references/run-chat.md +57 -0
  50. package/skills/ae-team/references/run-reply.md +41 -0
  51. package/skills/ae-team/references/run-result.md +75 -0
  52. package/skills/ae-team/references/run-start.md +73 -0
  53. package/skills/ae-team/references/run-watch.md +82 -0
  54. package/skills/ae-team/references/update.md +47 -0
@@ -1,81 +1,9 @@
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();
1
+ import {
2
+ logger
3
+ } from "./chunk-MNHE6SSI.js";
76
4
 
77
5
  // src/core/json-utils.ts
78
- import fs2 from "fs";
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 = fs2.readFileSync(filePath, "utf-8");
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 fs3 from "fs";
93
- import path2 from "path";
94
- var CONFIG_DIR = path2.join(process.env.HOME || "", ".ae-cli");
95
- var CONFIG_FILE = path2.join(CONFIG_DIR, "config.json");
96
- var MCP_TOKENS_FILE = path2.join(CONFIG_DIR, "mcp-tokens.json");
97
- var FALLBACK_MCP_TOKEN_FILE = "/data/app/te_agent_ta/.ae-config/mcp-token.json";
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 (!fs3.existsSync(CONFIG_DIR)) fs3.mkdirSync(CONFIG_DIR, { recursive: true });
27
+ if (!fs2.existsSync(CONFIG_DIR)) fs2.mkdirSync(CONFIG_DIR, { recursive: true });
100
28
  }
101
29
  function migrateFromFallback() {
102
- if (!fs3.existsSync(FALLBACK_MCP_TOKEN_FILE) || fs3.existsSync(MCP_TOKENS_FILE)) {
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 (!fs3.existsSync(FALLBACK_MCP_TOKEN_FILE)) {
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
- fs3.writeFileSync(MCP_TOKENS_FILE, JSON.stringify(tokenStore, null, 2));
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 (fs3.existsSync(CONFIG_FILE)) {
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
- fs3.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
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-DC66L5AM.js";
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
+ };