@thinkingai/ae-cli 1.0.24 → 1.0.28

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 (106) hide show
  1. package/README.md +17 -2
  2. package/README.zh.md +18 -2
  3. package/dist/auth-5NSDQFQB.js +454 -0
  4. package/dist/{auth-4OPL4YWG.js → auth-H2376DEF.js} +2 -2
  5. package/dist/chunk-AD5V3ZPJ.js +217 -0
  6. package/dist/chunk-CAQYQA4R.js +399 -0
  7. package/dist/{chunk-JJCRURTR.js → chunk-CLJF7MQA.js} +1 -1
  8. package/dist/chunk-GSJGPNKK.js +49 -0
  9. package/dist/chunk-IMBKVXKY.js +478 -0
  10. package/dist/chunk-KAEZTSXN.js +99 -0
  11. package/dist/{chunk-MZW6NOVS.js → chunk-LU7SXK4Q.js} +27 -8
  12. package/dist/chunk-OJDNO5QY.js +63 -0
  13. package/dist/{chunk-DC66L5AM.js → chunk-SRJIAOBN.js} +76 -91
  14. package/dist/{chunk-OVHL4JAC.js → chunk-WCHI7725.js} +44 -47
  15. package/dist/{client-TDH6MUWM.js → client-FCMK3XEK.js} +5 -3
  16. package/dist/config-64G36TJL.js +130 -0
  17. package/dist/index.js +50 -17
  18. package/dist/model-EZ6K7FXI.js +134 -0
  19. package/dist/{raw-EM7AP3RT.js → raw-VEF3D6UD.js} +11 -6
  20. package/dist/sync-NRODEVNH.js +385 -0
  21. package/dist/te-agent-7U7JLJKU.js +524 -0
  22. package/dist/{te-analysis-3KFSNUCJ.js → te-analysis-A2OSGHQ4.js} +136 -20
  23. package/dist/{te-audience-GMYJFCSC.js → te-audience-LI67PPGO.js} +5 -4
  24. package/dist/{te-common-YLERCMQI.js → te-common-35WVU3C5.js} +19 -8
  25. package/dist/{te-community-PYDN5A6B.js → te-community-LEAP6PWT.js} +4 -3
  26. package/dist/{te-dataops-ETWS5O3X.js → te-dataops-N2LGZBOX.js} +4 -3
  27. package/dist/{te-engage-ROJVZHDA.js → te-engage-H7C72Z46.js} +105 -49
  28. package/dist/{te-kb-VUNS2CC7.js → te-kb-I2OKTBAI.js} +225 -29
  29. package/dist/{te-meta-3EFWLM3A.js → te-meta-HBTIEDRO.js} +4 -3
  30. package/dist/te-team-EM5OWRQL.js +529 -0
  31. package/package.json +10 -2
  32. package/skills/ae-agent/SKILL.md +134 -0
  33. package/skills/ae-analysis/SKILL.md +1 -1
  34. package/skills/ae-analysis/references/drilldown_user_events.md +1 -1
  35. package/skills/ae-analysis/references/drilldown_users.md +2 -2
  36. package/skills/ae-analysis/references/list_clusters.md +4 -2
  37. package/skills/ae-analysis/references/query_adhoc.md +3 -2
  38. package/skills/ae-analysis/references/query_entity_details.md +1 -1
  39. package/skills/ae-analysis-global/SKILL.md +51 -0
  40. package/skills/ae-analysis-global/references/list_query_clusters.md +67 -0
  41. package/skills/ae-community/SKILL.md +2 -3
  42. package/skills/ae-dataops/SKILL.md +1 -2
  43. package/skills/ae-engage/SKILL.md +26 -2
  44. package/skills/ae-engage/references/add-approver.md +0 -1
  45. package/skills/ae-engage/references/add-channel.md +0 -1
  46. package/skills/ae-engage/references/approver-list.md +0 -1
  47. package/skills/ae-engage/references/build-task-save-guide.md +283 -0
  48. package/skills/ae-engage/references/cancel-query-by-request-id.md +0 -1
  49. package/skills/ae-engage/references/channel-detail.md +0 -1
  50. package/skills/ae-engage/references/channel-list.md +0 -1
  51. package/skills/ae-engage/references/config-channel-detail.md +0 -1
  52. package/skills/ae-engage/references/config-channel-list.md +0 -1
  53. package/skills/ae-engage/references/config-item-analysis-report.md +0 -1
  54. package/skills/ae-engage/references/config-item-detail.md +0 -1
  55. package/skills/ae-engage/references/config-item-list.md +0 -1
  56. package/skills/ae-engage/references/config-item-strategy-comparison.md +0 -1
  57. package/skills/ae-engage/references/config-item-trigger-report.md +0 -1
  58. package/skills/ae-engage/references/copy-config-template.md +0 -1
  59. package/skills/ae-engage/references/delete-channel.md +0 -1
  60. package/skills/ae-engage/references/delete-config-channel.md +0 -1
  61. package/skills/ae-engage/references/delete-config-item.md +0 -1
  62. package/skills/ae-engage/references/delete-flow.md +0 -1
  63. package/skills/ae-engage/references/flow-ab-split-node-report.md +0 -1
  64. package/skills/ae-engage/references/flow-detail.md +0 -1
  65. package/skills/ae-engage/references/flow-list.md +0 -1
  66. package/skills/ae-engage/references/flow-node-config-schema.md +0 -1
  67. package/skills/ae-engage/references/flow-node-detail-report.md +0 -1
  68. package/skills/ae-engage/references/flow-node-overview-report.md +0 -1
  69. package/skills/ae-engage/references/flow-process-report.md +0 -1
  70. package/skills/ae-engage/references/manage-flow.md +0 -1
  71. package/skills/ae-engage/references/manage-strategy.md +0 -1
  72. package/skills/ae-engage/references/manage-task.md +0 -1
  73. package/skills/ae-engage/references/modify-flow-base-info.md +0 -1
  74. package/skills/ae-engage/references/save-task.md +304 -0
  75. package/skills/ae-engage/references/strategy-detail.md +0 -1
  76. package/skills/ae-engage/references/strategy-list.md +0 -1
  77. package/skills/ae-engage/references/task-data-detail.md +0 -1
  78. package/skills/ae-engage/references/task-data-overview.md +0 -1
  79. package/skills/ae-engage/references/task-detail.md +0 -1
  80. package/skills/ae-engage/references/task-experiment-report.md +0 -1
  81. package/skills/ae-engage/references/task-list.md +0 -1
  82. package/skills/ae-engage/references/task-metric-detail.md +0 -1
  83. package/skills/ae-engage/references/task-stats.md +0 -1
  84. package/skills/ae-engage/references/update-channel-status.md +0 -1
  85. package/skills/ae-engage/references/update-config-channel-status.md +0 -1
  86. package/skills/ae-engage/references/validate-flow-node-config.md +0 -1
  87. package/skills/ae-engage/references/whitelist-list.md +0 -1
  88. package/skills/ae-kb/SKILL.md +284 -0
  89. package/skills/ae-team/SKILL.md +164 -0
  90. package/skills/ae-team/references/ai-generate.md +39 -0
  91. package/skills/ae-team/references/create.md +94 -0
  92. package/skills/ae-team/references/delete.md +39 -0
  93. package/skills/ae-team/references/list-projects.md +45 -0
  94. package/skills/ae-team/references/list-templates.md +38 -0
  95. package/skills/ae-team/references/list.md +39 -0
  96. package/skills/ae-team/references/run-artifacts.md +51 -0
  97. package/skills/ae-team/references/run-cancel.md +38 -0
  98. package/skills/ae-team/references/run-chat.md +57 -0
  99. package/skills/ae-team/references/run-reply.md +41 -0
  100. package/skills/ae-team/references/run-result.md +75 -0
  101. package/skills/ae-team/references/run-start.md +73 -0
  102. package/skills/ae-team/references/run-watch.md +82 -0
  103. package/skills/ae-team/references/update.md +47 -0
  104. package/dist/auth-PBPAZQWP.js +0 -168
  105. package/dist/chunk-47MTN54I.js +0 -285
  106. package/dist/config-5ELRYAIH.js +0 -257
@@ -0,0 +1,217 @@
1
+ import {
2
+ TeAgentCredentialsError,
3
+ tryLoadTeAgentSandboxCredentials
4
+ } from "./chunk-KAEZTSXN.js";
5
+ import {
6
+ getActiveHost
7
+ } from "./chunk-SRJIAOBN.js";
8
+
9
+ // src/core/te-agent-client.ts
10
+ var TeAgentApiError = class extends Error {
11
+ constructor(message, status, code, body) {
12
+ super(message);
13
+ this.status = status;
14
+ this.code = code;
15
+ this.body = body;
16
+ this.name = "TeAgentApiError";
17
+ }
18
+ status;
19
+ code;
20
+ body;
21
+ };
22
+ var DEFAULT_TIMEOUT_MS = 3e4;
23
+ var UPLOAD_TIMEOUT_MS = 12e4;
24
+ async function fetchWithTimeout(url, init, timeoutMs = DEFAULT_TIMEOUT_MS) {
25
+ const controller = new AbortController();
26
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
27
+ try {
28
+ return await fetch(url, { ...init, signal: controller.signal });
29
+ } catch (err) {
30
+ if (err?.name === "AbortError") {
31
+ throw new TeAgentApiError(`Request timed out (${timeoutMs}ms): ${url}`, 0, "TIMEOUT");
32
+ }
33
+ throw new TeAgentApiError(`Network request failed: ${err?.message ?? err}`, 0, "NETWORK_ERROR");
34
+ } finally {
35
+ clearTimeout(timer);
36
+ }
37
+ }
38
+ function teClaudeBaseFromActiveHost() {
39
+ const override = process.env.TE_CLAUDE_BASE_URL;
40
+ if (override) return override.replace(/\/+$/, "");
41
+ const h = getActiveHost();
42
+ if (!h) return void 0;
43
+ const base = h.replace(/\/+$/, "");
44
+ const bp = process.env.TE_CLAUDE_BASE_PATH || "/agent";
45
+ return base.endsWith(bp) ? base : base + bp;
46
+ }
47
+ async function signRequest(method, path, rawBody) {
48
+ const sandboxCred = tryLoadTeAgentSandboxCredentials();
49
+ if (sandboxCred && sandboxCred.sandboxId && sandboxCred.sandboxSecretKey) {
50
+ const headers2 = {
51
+ "X-Sandbox-Id": sandboxCred.sandboxId,
52
+ "X-Sandbox-Secret-Key": sandboxCred.sandboxSecretKey
53
+ };
54
+ if (method === "POST" || method === "PATCH") {
55
+ headers2["Content-Type"] = "application/json";
56
+ }
57
+ return {
58
+ url: `${sandboxCred.url.replace(/\/$/, "")}${path}`,
59
+ headers: headers2,
60
+ rawBody
61
+ };
62
+ }
63
+ const { getToken } = await import("./auth-H2376DEF.js");
64
+ const baseUrl = sandboxCred?.url || teClaudeBaseFromActiveHost();
65
+ if (!baseUrl) {
66
+ throw new TeAgentCredentialsError(
67
+ "Cannot determine the te-claude service URL",
68
+ "Run ae-cli config set-host <url> to configure the service URL, or execute inside a te-agent sandbox"
69
+ );
70
+ }
71
+ const accessToken = await getToken(getActiveHost() || baseUrl);
72
+ const headers = {
73
+ "Authorization": `bearer ${accessToken}`
74
+ };
75
+ if (method === "POST" || method === "PATCH") {
76
+ headers["Content-Type"] = "application/json";
77
+ }
78
+ return {
79
+ url: `${baseUrl.replace(/\/$/, "")}${path}`,
80
+ headers,
81
+ rawBody
82
+ };
83
+ }
84
+ async function parseResponse(response, defaultErrorPrefix) {
85
+ const text = await response.text();
86
+ let parsed = void 0;
87
+ if (text) {
88
+ try {
89
+ parsed = JSON.parse(text);
90
+ } catch {
91
+ parsed = text;
92
+ }
93
+ }
94
+ if (!response.ok) {
95
+ if (response.status === 401) {
96
+ throw new TeAgentApiError(
97
+ `Session expired or unauthorized. Please run: ae-cli auth login`,
98
+ response.status,
99
+ "auth_expired",
100
+ parsed
101
+ );
102
+ }
103
+ const message = typeof parsed === "object" && parsed && typeof parsed.error === "string" ? parsed.error : `${defaultErrorPrefix} ${response.status}`;
104
+ const code = typeof parsed === "object" && parsed && typeof parsed.code === "string" ? parsed.code : void 0;
105
+ throw new TeAgentApiError(message, response.status, code, parsed);
106
+ }
107
+ return parsed;
108
+ }
109
+ async function postToMainApp(path, body) {
110
+ const rawBody = JSON.stringify(body);
111
+ const signed = await signRequest("POST", path, rawBody);
112
+ const response = await fetchWithTimeout(signed.url, {
113
+ method: "POST",
114
+ headers: signed.headers,
115
+ body: signed.rawBody
116
+ });
117
+ return parseResponse(response, "Main app returned");
118
+ }
119
+ async function getFromMainApp(path) {
120
+ const signed = await signRequest("GET", path, "");
121
+ const response = await fetchWithTimeout(signed.url, {
122
+ method: "GET",
123
+ headers: signed.headers
124
+ });
125
+ return parseResponse(response, "Main app returned");
126
+ }
127
+ async function getSandboxModels(currentModelId) {
128
+ const qs = currentModelId ? `?current=${encodeURIComponent(currentModelId)}` : "";
129
+ const data = await getFromMainApp(`/api/sandbox/models${qs}`);
130
+ return Array.isArray(data?.models) ? data.models : [];
131
+ }
132
+ async function postSandboxModelSelection(args) {
133
+ return postToMainApp("/api/sandbox/models/select", args);
134
+ }
135
+ async function getSandboxSyncPullCandidates(args) {
136
+ const qs = new URLSearchParams({
137
+ workspacePath: args.workspacePath,
138
+ kind: args.kind
139
+ });
140
+ return getFromMainApp(
141
+ `/api/sandbox/sync/pull/candidates?${qs.toString()}`
142
+ );
143
+ }
144
+ async function postSandboxSyncPull(args) {
145
+ return postToMainApp("/api/sandbox/sync/pull", {
146
+ workspacePath: args.workspacePath,
147
+ kind: args.kind,
148
+ skills: args.skills,
149
+ mcp: args.mcp,
150
+ mode: "merge",
151
+ ifUnmodifiedSince: args.ifUnmodifiedSince
152
+ });
153
+ }
154
+ async function deleteFromMainApp(path) {
155
+ const signed = await signRequest("DELETE", path, "");
156
+ const response = await fetchWithTimeout(signed.url, {
157
+ method: "DELETE",
158
+ headers: signed.headers
159
+ });
160
+ return parseResponse(response, "Main app returned");
161
+ }
162
+ async function patchToMainApp(path, body) {
163
+ const rawBody = JSON.stringify(body);
164
+ const signed = await signRequest("PATCH", path, rawBody);
165
+ const response = await fetchWithTimeout(signed.url, {
166
+ method: "PATCH",
167
+ headers: signed.headers,
168
+ body: signed.rawBody
169
+ });
170
+ return parseResponse(response, "Main app returned");
171
+ }
172
+ async function uploadToMainApp(path, formData) {
173
+ const sandboxCred = tryLoadTeAgentSandboxCredentials();
174
+ let baseUrl;
175
+ let authHeaders;
176
+ if (sandboxCred && sandboxCred.sandboxId && sandboxCred.sandboxSecretKey) {
177
+ baseUrl = sandboxCred.url.replace(/\/$/, "");
178
+ authHeaders = {
179
+ "X-Sandbox-Id": sandboxCred.sandboxId,
180
+ "X-Sandbox-Secret-Key": sandboxCred.sandboxSecretKey
181
+ };
182
+ } else {
183
+ const { getToken } = await import("./auth-H2376DEF.js");
184
+ baseUrl = (sandboxCred?.url || teClaudeBaseFromActiveHost() || "").replace(/\/$/, "");
185
+ if (!baseUrl) {
186
+ throw new TeAgentCredentialsError(
187
+ "Cannot determine the te-claude service URL",
188
+ "Run ae-cli config set-host <url> to configure the service URL, or execute inside a te-agent sandbox"
189
+ );
190
+ }
191
+ const accessToken = await getToken(getActiveHost() || baseUrl);
192
+ authHeaders = { "Authorization": `bearer ${accessToken}` };
193
+ }
194
+ const response = await fetchWithTimeout(
195
+ `${baseUrl}${path}`,
196
+ {
197
+ method: "POST",
198
+ headers: authHeaders,
199
+ body: formData
200
+ },
201
+ UPLOAD_TIMEOUT_MS
202
+ );
203
+ return parseResponse(response, "Upload failed");
204
+ }
205
+
206
+ export {
207
+ TeAgentApiError,
208
+ postToMainApp,
209
+ getFromMainApp,
210
+ getSandboxModels,
211
+ postSandboxModelSelection,
212
+ getSandboxSyncPullCandidates,
213
+ postSandboxSyncPull,
214
+ deleteFromMainApp,
215
+ patchToMainApp,
216
+ uploadToMainApp
217
+ };
@@ -0,0 +1,399 @@
1
+ import {
2
+ getActiveHost,
3
+ getConfigDir,
4
+ logger,
5
+ safeJsonParse,
6
+ safeReadJsonFile
7
+ } from "./chunk-SRJIAOBN.js";
8
+
9
+ // src/core/auth.ts
10
+ import fs2 from "fs";
11
+ import path2 from "path";
12
+
13
+ // src/core/secure-store.ts
14
+ import crypto from "crypto";
15
+ import fs from "fs";
16
+ import os from "os";
17
+ import path from "path";
18
+ import { execFileSync } from "child_process";
19
+ var STORE_DIR_SUFFIX = "secure-tokens";
20
+ var SCRYPT_SALT = Buffer.from("ae-cli-secure-store-v1");
21
+ var SCRYPT_N = 16384;
22
+ var SCRYPT_R = 8;
23
+ var SCRYPT_P = 1;
24
+ var KEY_LEN = 32;
25
+ var NONCE_LEN = 12;
26
+ var REFRESH_TOKEN_PATH = "/v1/oauth/refreshForToken";
27
+ function getMachineId() {
28
+ if (process.platform === "darwin") {
29
+ try {
30
+ const out = execFileSync(
31
+ "ioreg",
32
+ ["-rd1", "-c", "IOPlatformExpertDevice"],
33
+ { encoding: "utf8", timeout: 3e3 }
34
+ );
35
+ const m = out.match(/IOPlatformUUID.*?=.*?"([0-9A-F-]{36})"/i);
36
+ if (m?.[1]) return `darwin:${m[1]}`;
37
+ } catch {
38
+ }
39
+ }
40
+ if (process.platform === "linux") {
41
+ for (const p of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
42
+ try {
43
+ const id = fs.readFileSync(p, "utf8").trim();
44
+ if (id) return `linux:${id}`;
45
+ } catch {
46
+ }
47
+ }
48
+ }
49
+ if (process.platform === "win32") {
50
+ try {
51
+ const out = execFileSync(
52
+ "reg",
53
+ ["query", "HKLM\\SOFTWARE\\Microsoft\\Cryptography", "/v", "MachineGuid"],
54
+ { encoding: "utf8", timeout: 3e3 }
55
+ );
56
+ const m = out.match(/MachineGuid\s+REG_SZ\s+([0-9a-f-]+)/i);
57
+ if (m?.[1]) return `win32:${m[1]}`;
58
+ } catch {
59
+ }
60
+ }
61
+ const fallback = `${os.homedir()}|${os.hostname()}|${os.platform()}`;
62
+ logger.warn("secure-store: using fallback machine-id (HOME+hostname+platform)");
63
+ return `fallback:${fallback}`;
64
+ }
65
+ var _cachedMachineId;
66
+ function getCachedMachineId() {
67
+ if (!_cachedMachineId) _cachedMachineId = getMachineId();
68
+ return _cachedMachineId;
69
+ }
70
+ function deriveKey(machineId) {
71
+ return crypto.scryptSync(machineId, SCRYPT_SALT, KEY_LEN, {
72
+ N: SCRYPT_N,
73
+ r: SCRYPT_R,
74
+ p: SCRYPT_P
75
+ });
76
+ }
77
+ var _cachedKey;
78
+ function getKey() {
79
+ if (!_cachedKey) _cachedKey = deriveKey(getCachedMachineId());
80
+ return _cachedKey;
81
+ }
82
+ function encrypt(plaintext) {
83
+ const key = getKey();
84
+ const nonce = crypto.randomBytes(NONCE_LEN);
85
+ const cipher = crypto.createCipheriv("aes-256-gcm", key, nonce);
86
+ const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
87
+ const tag = cipher.getAuthTag();
88
+ return {
89
+ nonce: nonce.toString("hex"),
90
+ tag: tag.toString("hex"),
91
+ data: encrypted.toString("hex")
92
+ };
93
+ }
94
+ function decrypt(blob) {
95
+ const key = getKey();
96
+ const nonce = Buffer.from(blob.nonce, "hex");
97
+ const tag = Buffer.from(blob.tag, "hex");
98
+ const data = Buffer.from(blob.data, "hex");
99
+ const decipher = crypto.createDecipheriv("aes-256-gcm", key, nonce);
100
+ decipher.setAuthTag(tag);
101
+ const decrypted = Buffer.concat([decipher.update(data), decipher.final()]);
102
+ return decrypted.toString("utf8");
103
+ }
104
+ function storeDir() {
105
+ return path.join(getConfigDir(), STORE_DIR_SUFFIX);
106
+ }
107
+ function tokenFilePath(host) {
108
+ const safe = host.replace(/^https?:\/\//, "").replace(/[^a-zA-Z0-9._-]/g, "_");
109
+ return path.join(storeDir(), `${safe}.enc.json`);
110
+ }
111
+ function ensureStoreDir() {
112
+ const dir = storeDir();
113
+ if (!fs.existsSync(dir)) {
114
+ fs.mkdirSync(dir, { recursive: true });
115
+ try {
116
+ fs.chmodSync(dir, 448);
117
+ } catch {
118
+ }
119
+ }
120
+ }
121
+ function save(host, payload) {
122
+ ensureStoreDir();
123
+ const plaintext = JSON.stringify(payload);
124
+ const blob = encrypt(plaintext);
125
+ const filePath = tokenFilePath(host);
126
+ fs.writeFileSync(filePath, JSON.stringify(blob, null, 2), { encoding: "utf8" });
127
+ try {
128
+ fs.chmodSync(filePath, 384);
129
+ } catch {
130
+ }
131
+ logger.info(`secure-store: token saved for ${host}`);
132
+ }
133
+ function load(host) {
134
+ const filePath = tokenFilePath(host);
135
+ if (!fs.existsSync(filePath)) return null;
136
+ try {
137
+ const raw = fs.readFileSync(filePath, "utf8");
138
+ const blob = safeJsonParse(raw);
139
+ if (!blob?.nonce || !blob?.tag || !blob?.data) {
140
+ logger.warn(`secure-store: malformed blob for ${host}`);
141
+ return null;
142
+ }
143
+ const plaintext = decrypt(blob);
144
+ return JSON.parse(plaintext);
145
+ } catch (e) {
146
+ logger.error(`secure-store: decrypt failed for ${host}: ${e.message}`);
147
+ return null;
148
+ }
149
+ }
150
+ function clear(host) {
151
+ const filePath = tokenFilePath(host);
152
+ if (fs.existsSync(filePath)) {
153
+ fs.rmSync(filePath);
154
+ logger.info(`secure-store: token cleared for ${host}`);
155
+ }
156
+ }
157
+ async function getValidAccessToken(host) {
158
+ const payload = load(host);
159
+ if (!payload) {
160
+ throw new SecureStoreAuthError(
161
+ `No stored token for ${host}. Please run: ae-cli auth login`
162
+ );
163
+ }
164
+ const expiresAt = new Date(payload.accessExpiresAt).getTime();
165
+ const now = Date.now();
166
+ const needsRefresh = expiresAt - now < 6e4;
167
+ if (!needsRefresh) {
168
+ return payload.accessToken;
169
+ }
170
+ if (payload.refreshToken) {
171
+ logger.info(`secure-store: access token past local expiry for ${host}, refreshing\u2026`);
172
+ const refreshed = await doRefresh(host, payload.refreshToken);
173
+ save(host, { ...refreshed, mcpToken: payload.mcpToken });
174
+ logger.info(`secure-store: token refreshed for ${host}`);
175
+ return refreshed.accessToken;
176
+ }
177
+ logger.info(`secure-store: ${host} past static local expiry, no refresh token \u2014 returning stored token, server validates (lazy)`);
178
+ return payload.accessToken;
179
+ }
180
+ function loadMcpToken(host) {
181
+ return load(host)?.mcpToken ?? null;
182
+ }
183
+ async function doRefresh(host, refreshToken) {
184
+ const base = host.replace(/\/+$/, "");
185
+ const url = `${base}${REFRESH_TOKEN_PATH}`;
186
+ let resp;
187
+ try {
188
+ resp = await fetch(url, {
189
+ method: "POST",
190
+ headers: { "Content-Type": "application/json" },
191
+ body: JSON.stringify({ refreshToken })
192
+ });
193
+ } catch (e) {
194
+ throw new SecureStoreAuthError(
195
+ `Token refresh network error for ${host}: ${e.message}. Please run: ae-cli auth login`
196
+ );
197
+ }
198
+ if (!resp.ok) {
199
+ throw new SecureStoreAuthError(
200
+ `Token refresh failed (HTTP ${resp.status}) for ${host}. Please run: ae-cli auth login`
201
+ );
202
+ }
203
+ let body;
204
+ try {
205
+ body = safeJsonParse(await resp.text());
206
+ } catch {
207
+ throw new SecureStoreAuthError(
208
+ `Token refresh returned invalid JSON for ${host}. Please run: ae-cli auth login`
209
+ );
210
+ }
211
+ if (body.return_code !== 0) {
212
+ throw new SecureStoreAuthError(
213
+ `Token refresh error (${body.return_code}: ${body.return_message ?? "unknown"}) for ${host}. Please run: ae-cli auth login`
214
+ );
215
+ }
216
+ const d = body.data;
217
+ const newAccess = d?.accessToken ?? d?.access_token ?? "";
218
+ const newRefresh = d?.refreshToken ?? d?.refresh_token ?? refreshToken;
219
+ const expiresIn = d?.expiresIn ?? d?.expires_in ?? 72e3;
220
+ if (!newAccess) {
221
+ throw new SecureStoreAuthError(
222
+ `Token refresh returned empty accessToken for ${host}. Please run: ae-cli auth login`
223
+ );
224
+ }
225
+ return {
226
+ accessToken: newAccess,
227
+ refreshToken: newRefresh,
228
+ accessExpiresAt: new Date(Date.now() + expiresIn * 1e3).toISOString()
229
+ };
230
+ }
231
+ var SecureStoreAuthError = class extends Error {
232
+ constructor(message) {
233
+ super(message);
234
+ this.name = "SecureStoreAuthError";
235
+ }
236
+ };
237
+
238
+ // src/core/auth.ts
239
+ var TOKENS_FILE = path2.join(getConfigDir(), "tokens.json");
240
+ var LEGACY_MCP_TOKENS_FILE = path2.join(getConfigDir(), "mcp-tokens.json");
241
+ var LEGACY_DIR = path2.join(process.env.HOME || "", ".te-mcp");
242
+ (function removeLegacyMcpTokens() {
243
+ try {
244
+ if (fs2.existsSync(LEGACY_MCP_TOKENS_FILE)) {
245
+ fs2.rmSync(LEGACY_MCP_TOKENS_FILE);
246
+ logger.info("auth: removed legacy mcp-tokens.json (plaintext MCP token file)");
247
+ }
248
+ } catch {
249
+ }
250
+ })();
251
+ function ensureDir() {
252
+ const dir = getConfigDir();
253
+ if (!fs2.existsSync(dir)) fs2.mkdirSync(dir, { recursive: true });
254
+ }
255
+ function resolveHost(hostOverride) {
256
+ if (hostOverride) return hostOverride;
257
+ return getActiveHost();
258
+ }
259
+ async function validateToken(token, hostUrl) {
260
+ const url = `${hostUrl}/v1/oauth/checkToken`;
261
+ try {
262
+ const resp = await fetch(url, {
263
+ method: "POST",
264
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
265
+ body: new URLSearchParams({ accessToken: token }).toString()
266
+ });
267
+ const respJson = safeJsonParse(await resp.text());
268
+ return respJson?.return_code === 0;
269
+ } catch {
270
+ return false;
271
+ }
272
+ }
273
+ function loadAllTokens() {
274
+ try {
275
+ const legacyTokens = path2.join(LEGACY_DIR, "tokens.json");
276
+ if (fs2.existsSync(legacyTokens) && !fs2.existsSync(TOKENS_FILE)) {
277
+ const data = safeReadJsonFile(legacyTokens);
278
+ const migrated = {};
279
+ for (const [key, val] of Object.entries(data)) {
280
+ const url = key.startsWith("http") ? key : `https://${key}`;
281
+ migrated[url] = val;
282
+ }
283
+ ensureDir();
284
+ fs2.writeFileSync(TOKENS_FILE, JSON.stringify(migrated, null, 2));
285
+ return migrated;
286
+ }
287
+ if (fs2.existsSync(TOKENS_FILE)) {
288
+ const data = safeReadJsonFile(TOKENS_FILE);
289
+ let needsMigration = false;
290
+ const migrated = {};
291
+ for (const [key, val] of Object.entries(data)) {
292
+ if (!key.startsWith("http")) {
293
+ migrated[`https://${key}`] = val;
294
+ needsMigration = true;
295
+ } else {
296
+ migrated[key] = val;
297
+ }
298
+ }
299
+ if (needsMigration) {
300
+ fs2.writeFileSync(TOKENS_FILE, JSON.stringify(migrated, null, 2));
301
+ return migrated;
302
+ }
303
+ return data;
304
+ }
305
+ } catch {
306
+ }
307
+ return {};
308
+ }
309
+ function saveAllTokens(tokens) {
310
+ ensureDir();
311
+ fs2.writeFileSync(TOKENS_FILE, JSON.stringify(tokens, null, 2));
312
+ try {
313
+ fs2.chmodSync(TOKENS_FILE, 384);
314
+ } catch {
315
+ }
316
+ }
317
+ function loadToken(hostUrl) {
318
+ const tokens = loadAllTokens();
319
+ const entry = tokens[hostUrl];
320
+ if (!entry || !entry.token) return null;
321
+ return { host: hostUrl, token: entry.token, updatedAt: entry.updatedAt };
322
+ }
323
+ function saveToken(token, hostUrl) {
324
+ const tokens = loadAllTokens();
325
+ tokens[hostUrl] = { token, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
326
+ saveAllTokens(tokens);
327
+ logger.info(`Token saved for ${hostUrl}`);
328
+ }
329
+ function clearToken(hostUrl) {
330
+ const tokens = loadAllTokens();
331
+ delete tokens[hostUrl];
332
+ saveAllTokens(tokens);
333
+ }
334
+ function setTokenManual(token, hostUrl) {
335
+ saveToken(token, hostUrl);
336
+ }
337
+ function getAuthStatus(hostUrl) {
338
+ if (process.env.TE_TOKEN) {
339
+ return { authenticated: true, host: hostUrl, source: "env:TE_TOKEN" };
340
+ }
341
+ const cached = loadToken(hostUrl);
342
+ if (cached) {
343
+ const ageMs = Date.now() - new Date(cached.updatedAt).getTime();
344
+ const hours = Math.round(ageMs / 36e5);
345
+ return { authenticated: true, host: hostUrl, tokenAge: `${hours}h ago`, source: "cache" };
346
+ }
347
+ return { authenticated: false, host: hostUrl };
348
+ }
349
+ async function getToken(hostUrl) {
350
+ if (!hostUrl) {
351
+ throw new Error(
352
+ `No AE host configured.
353
+ Run: ae-cli config set-host <url>`
354
+ );
355
+ }
356
+ if (process.env.TE_TOKEN) {
357
+ logger.info("Using token from env:TE_TOKEN");
358
+ return process.env.TE_TOKEN;
359
+ }
360
+ const cached = loadToken(hostUrl);
361
+ if (cached && cached.token) {
362
+ logger.info(`Using cached token for ${hostUrl}`);
363
+ return cached.token;
364
+ }
365
+ try {
366
+ const secureToken = await getValidAccessToken(hostUrl);
367
+ logger.info(`Using secure-store token for ${hostUrl}`);
368
+ return secureToken;
369
+ } catch (e) {
370
+ if (e instanceof SecureStoreAuthError) {
371
+ logger.info(`secure-store: ${e.message}`);
372
+ } else {
373
+ logger.warn(`secure-store unexpected error: ${e.message}`);
374
+ }
375
+ }
376
+ throw new Error(
377
+ `Cannot obtain token for ${hostUrl}.
378
+ Options:
379
+ 1. ae-cli auth login (device code flow, cross-platform)
380
+ 2. ae-cli auth set-token <token>
381
+ 3. export TE_TOKEN=<token> (CI/headless)`
382
+ );
383
+ }
384
+
385
+ export {
386
+ save,
387
+ load,
388
+ clear,
389
+ loadMcpToken,
390
+ SecureStoreAuthError,
391
+ resolveHost,
392
+ validateToken,
393
+ loadToken,
394
+ saveToken,
395
+ clearToken,
396
+ setTokenManual,
397
+ getAuthStatus,
398
+ getToken
399
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  logger
3
- } from "./chunk-DC66L5AM.js";
3
+ } from "./chunk-SRJIAOBN.js";
4
4
 
5
5
  // src/framework/output.ts
6
6
  import Table from "cli-table3";
@@ -0,0 +1,49 @@
1
+ import {
2
+ getSandboxClusterInfoFilePath,
3
+ logger,
4
+ safeReadJsonFile
5
+ } from "./chunk-SRJIAOBN.js";
6
+
7
+ // src/core/cluster-info.ts
8
+ import fs from "fs";
9
+ import path from "path";
10
+ var GLOBAL_QUERY_CONFIG_KEY = "sw_cfg_enable_global_query";
11
+ var CLUSTER_INFO_FILE_ENV = "AE_CLUSTER_INFO_FILE";
12
+ function getClusterInfoFilePath() {
13
+ return process.env[CLUSTER_INFO_FILE_ENV] || getSandboxClusterInfoFilePath();
14
+ }
15
+ function loadClusterInfo() {
16
+ const file = getClusterInfoFilePath();
17
+ try {
18
+ if (!fs.existsSync(file)) {
19
+ return {};
20
+ }
21
+ const raw = safeReadJsonFile(file);
22
+ return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
23
+ } catch (err) {
24
+ logger.warn(`Failed to load cluster info from ${file}: ${err.message}`);
25
+ return {};
26
+ }
27
+ }
28
+ function isGlobalQueryModeEnabled() {
29
+ return loadClusterInfo()[GLOBAL_QUERY_CONFIG_KEY] === true;
30
+ }
31
+ function setGlobalQueryModeEnabled(enabled) {
32
+ const file = getClusterInfoFilePath();
33
+ const dir = path.dirname(file);
34
+ fs.mkdirSync(dir, { recursive: true });
35
+ const next = { [GLOBAL_QUERY_CONFIG_KEY]: enabled };
36
+ fs.writeFileSync(file, JSON.stringify(next, null, 2));
37
+ return next;
38
+ }
39
+ function getAnalysisMappingPathForClusterMode(enabled = isGlobalQueryModeEnabled()) {
40
+ return enabled ? "analysis-global" : "analysis";
41
+ }
42
+
43
+ export {
44
+ GLOBAL_QUERY_CONFIG_KEY,
45
+ getClusterInfoFilePath,
46
+ isGlobalQueryModeEnabled,
47
+ setGlobalQueryModeEnabled,
48
+ getAnalysisMappingPathForClusterMode
49
+ };