@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.
- package/README.md +17 -2
- package/README.zh.md +18 -2
- package/dist/auth-5NSDQFQB.js +454 -0
- package/dist/{auth-4OPL4YWG.js → auth-H2376DEF.js} +2 -2
- package/dist/chunk-AD5V3ZPJ.js +217 -0
- package/dist/chunk-CAQYQA4R.js +399 -0
- package/dist/{chunk-JJCRURTR.js → chunk-CLJF7MQA.js} +1 -1
- package/dist/chunk-GSJGPNKK.js +49 -0
- package/dist/chunk-IMBKVXKY.js +478 -0
- package/dist/chunk-KAEZTSXN.js +99 -0
- package/dist/{chunk-MZW6NOVS.js → chunk-LU7SXK4Q.js} +27 -8
- package/dist/chunk-OJDNO5QY.js +63 -0
- package/dist/{chunk-DC66L5AM.js → chunk-SRJIAOBN.js} +76 -91
- package/dist/{chunk-OVHL4JAC.js → chunk-WCHI7725.js} +44 -47
- package/dist/{client-TDH6MUWM.js → client-FCMK3XEK.js} +5 -3
- package/dist/config-64G36TJL.js +130 -0
- package/dist/index.js +50 -17
- package/dist/model-EZ6K7FXI.js +134 -0
- package/dist/{raw-EM7AP3RT.js → raw-VEF3D6UD.js} +11 -6
- package/dist/sync-NRODEVNH.js +385 -0
- package/dist/te-agent-7U7JLJKU.js +524 -0
- package/dist/{te-analysis-3KFSNUCJ.js → te-analysis-A2OSGHQ4.js} +136 -20
- package/dist/{te-audience-GMYJFCSC.js → te-audience-LI67PPGO.js} +5 -4
- package/dist/{te-common-YLERCMQI.js → te-common-35WVU3C5.js} +19 -8
- package/dist/{te-community-PYDN5A6B.js → te-community-LEAP6PWT.js} +4 -3
- package/dist/{te-dataops-ETWS5O3X.js → te-dataops-N2LGZBOX.js} +4 -3
- package/dist/{te-engage-ROJVZHDA.js → te-engage-H7C72Z46.js} +105 -49
- package/dist/{te-kb-VUNS2CC7.js → te-kb-I2OKTBAI.js} +225 -29
- package/dist/{te-meta-3EFWLM3A.js → te-meta-HBTIEDRO.js} +4 -3
- package/dist/te-team-EM5OWRQL.js +529 -0
- package/package.json +10 -2
- package/skills/ae-agent/SKILL.md +134 -0
- package/skills/ae-analysis/SKILL.md +1 -1
- 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/list_clusters.md +4 -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-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 +26 -2
- 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/build-task-save-guide.md +283 -0
- 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/save-task.md +304 -0
- 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 +284 -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
- package/dist/auth-PBPAZQWP.js +0 -168
- package/dist/chunk-47MTN54I.js +0 -285
- package/dist/config-5ELRYAIH.js +0 -257
package/dist/auth-PBPAZQWP.js
DELETED
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
printError,
|
|
3
|
-
printOutput
|
|
4
|
-
} from "./chunk-JJCRURTR.js";
|
|
5
|
-
import {
|
|
6
|
-
clearMcpToken,
|
|
7
|
-
loadMcpTokenStore,
|
|
8
|
-
setMcpTokenManual,
|
|
9
|
-
validateMcpToken
|
|
10
|
-
} from "./chunk-OVHL4JAC.js";
|
|
11
|
-
import {
|
|
12
|
-
clearToken,
|
|
13
|
-
getAuthStatus,
|
|
14
|
-
getToken,
|
|
15
|
-
resolveHost,
|
|
16
|
-
setTokenManual,
|
|
17
|
-
validateToken
|
|
18
|
-
} from "./chunk-47MTN54I.js";
|
|
19
|
-
import {
|
|
20
|
-
loadConfig,
|
|
21
|
-
logger,
|
|
22
|
-
saveConfig
|
|
23
|
-
} from "./chunk-DC66L5AM.js";
|
|
24
|
-
|
|
25
|
-
// src/commands/auth.ts
|
|
26
|
-
function registerAuth(program) {
|
|
27
|
-
const auth = program.command("auth").description("Authentication management");
|
|
28
|
-
auth.command("login").description("Login to AE (auto-extract token from Chrome on macOS)").action(async () => {
|
|
29
|
-
const host = resolveHost(program.opts().host);
|
|
30
|
-
if (!host) {
|
|
31
|
-
printError("config", "No AE host configured.", "Run: ae-cli config set-host");
|
|
32
|
-
process.exit(1);
|
|
33
|
-
}
|
|
34
|
-
const config = loadConfig();
|
|
35
|
-
if (!config.hosts[host]) {
|
|
36
|
-
config.hosts[host] = { label: host };
|
|
37
|
-
if (!config.activeHost) {
|
|
38
|
-
config.activeHost = host;
|
|
39
|
-
}
|
|
40
|
-
saveConfig(config);
|
|
41
|
-
logger.info(`Host config auto-saved for ${host}`);
|
|
42
|
-
process.stderr.write(`[ae-cli] Config saved for ${host}
|
|
43
|
-
`);
|
|
44
|
-
}
|
|
45
|
-
try {
|
|
46
|
-
const token = await getToken(host);
|
|
47
|
-
logger.info(`Auth login successful for ${host}`);
|
|
48
|
-
process.stderr.write(`[ae-cli] Authenticated to ${host}
|
|
49
|
-
`);
|
|
50
|
-
printOutput({ authenticated: true, host, token: token.slice(0, 8) + "..." }, program.opts().format || "json");
|
|
51
|
-
} catch (err) {
|
|
52
|
-
printError("auth", err.message);
|
|
53
|
-
process.exit(1);
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
auth.command("set-token <token>").description("Manually set authentication token for active host").action(async (token) => {
|
|
57
|
-
const host = resolveHost(program.opts().host);
|
|
58
|
-
if (!host) {
|
|
59
|
-
printError("config", "No AE host configured.", "Run: ae-cli config set-host");
|
|
60
|
-
process.exit(1);
|
|
61
|
-
}
|
|
62
|
-
const config = loadConfig();
|
|
63
|
-
if (!config.hosts[host]) {
|
|
64
|
-
config.hosts[host] = { label: host };
|
|
65
|
-
if (!config.activeHost) {
|
|
66
|
-
config.activeHost = host;
|
|
67
|
-
}
|
|
68
|
-
saveConfig(config);
|
|
69
|
-
process.stderr.write(`[ae-cli] Config saved for ${host}
|
|
70
|
-
`);
|
|
71
|
-
printOutput({ saved: true, config }, program.opts().format || "json");
|
|
72
|
-
}
|
|
73
|
-
process.stderr.write(`[ae-cli] Validating token...
|
|
74
|
-
`);
|
|
75
|
-
const isValid = await validateToken(token, host);
|
|
76
|
-
if (!isValid) {
|
|
77
|
-
logger.warn(`Token validation failed for ${host}`);
|
|
78
|
-
process.stderr.write(`[ae-cli] Token validation failed
|
|
79
|
-
`);
|
|
80
|
-
printError("auth", "Invalid token", "Please check your token and try again");
|
|
81
|
-
process.exit(1);
|
|
82
|
-
}
|
|
83
|
-
setTokenManual(token, host);
|
|
84
|
-
logger.info(`Token manually set and validated for ${host}`);
|
|
85
|
-
process.stderr.write(`[ae-cli] Token verified and saved for ${host}
|
|
86
|
-
`);
|
|
87
|
-
printOutput({ saved: true, host, verified: true }, program.opts().format || "json");
|
|
88
|
-
});
|
|
89
|
-
auth.command("status").description("Show current authentication status").action(async () => {
|
|
90
|
-
const host = resolveHost(program.opts().host);
|
|
91
|
-
if (!host) {
|
|
92
|
-
printOutput({ authenticated: false, host: "(none)", hint: "Run: ae-cli config set-host" }, program.opts().format || "json");
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
const tokenStatus = getAuthStatus(host);
|
|
96
|
-
const hasValidToken = tokenStatus.authenticated;
|
|
97
|
-
const mcpStore = loadMcpTokenStore();
|
|
98
|
-
const cachedMcpToken = mcpStore[host];
|
|
99
|
-
let hasValidMcpToken = false;
|
|
100
|
-
if (cachedMcpToken) {
|
|
101
|
-
hasValidMcpToken = await validateMcpToken(cachedMcpToken, host);
|
|
102
|
-
}
|
|
103
|
-
const authenticated = hasValidToken || hasValidMcpToken;
|
|
104
|
-
printOutput({ authenticated, host }, program.opts().format || "json");
|
|
105
|
-
});
|
|
106
|
-
auth.command("logout").description("Clear stored token for active host").action(() => {
|
|
107
|
-
const host = resolveHost(program.opts().host);
|
|
108
|
-
if (!host) {
|
|
109
|
-
printError("config", "No AE host configured.", "Run: ae-cli config set-host");
|
|
110
|
-
process.exit(1);
|
|
111
|
-
}
|
|
112
|
-
clearToken(host);
|
|
113
|
-
process.stderr.write(`[ae-cli] Token cleared for ${host}
|
|
114
|
-
`);
|
|
115
|
-
printOutput({ cleared: true, host }, program.opts().format || "json");
|
|
116
|
-
});
|
|
117
|
-
auth.command("set-mcp-token <token>").description("Manually set MCP token for a host").option("--host <host>", "Target host URL (defaults to active host)").action(async (token, opts) => {
|
|
118
|
-
const host = opts.host || resolveHost(program.opts().host);
|
|
119
|
-
if (!host) {
|
|
120
|
-
printError("config", "No AE host configured.", "Use --host <url> or run: ae-cli config set-host");
|
|
121
|
-
process.exit(1);
|
|
122
|
-
}
|
|
123
|
-
const config = loadConfig();
|
|
124
|
-
if (!config.hosts[host]) {
|
|
125
|
-
config.hosts[host] = { label: host };
|
|
126
|
-
if (!config.activeHost) {
|
|
127
|
-
config.activeHost = host;
|
|
128
|
-
}
|
|
129
|
-
saveConfig(config);
|
|
130
|
-
process.stderr.write(`[ae-cli] Host config saved for ${host}
|
|
131
|
-
`);
|
|
132
|
-
}
|
|
133
|
-
process.stderr.write(`[ae-cli] Validating MCP token...
|
|
134
|
-
`);
|
|
135
|
-
const isValid = await validateMcpToken(token, host);
|
|
136
|
-
if (!isValid) {
|
|
137
|
-
process.stderr.write(`[ae-cli] MCP token validation failed
|
|
138
|
-
`);
|
|
139
|
-
printError("auth", "Invalid MCP token", "Please check your MCP token and try again");
|
|
140
|
-
process.exit(1);
|
|
141
|
-
}
|
|
142
|
-
setMcpTokenManual(token, host);
|
|
143
|
-
process.stderr.write(`[ae-cli] MCP token verified and saved for ${host}
|
|
144
|
-
`);
|
|
145
|
-
printOutput({ saved: true, host, verified: true, type: "mcp-token" }, program.opts().format || "json");
|
|
146
|
-
});
|
|
147
|
-
auth.command("clear-mcp-token").description("Clear stored MCP token for a host").option("--host <host>", "Target host URL (defaults to active host)").option("--all", "Clear MCP tokens for all hosts").action((opts) => {
|
|
148
|
-
if (opts.all) {
|
|
149
|
-
clearMcpToken();
|
|
150
|
-
process.stderr.write(`[ae-cli] All MCP tokens cleared
|
|
151
|
-
`);
|
|
152
|
-
printOutput({ cleared: true, type: "all-mcp-tokens" }, program.opts().format || "json");
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
const host = opts.host || resolveHost(program.opts().host);
|
|
156
|
-
if (!host) {
|
|
157
|
-
printError("config", "No AE host configured.", "Use --host <url> or run: ae-cli config set-host");
|
|
158
|
-
process.exit(1);
|
|
159
|
-
}
|
|
160
|
-
clearMcpToken(host);
|
|
161
|
-
process.stderr.write(`[ae-cli] MCP token cleared for ${host}
|
|
162
|
-
`);
|
|
163
|
-
printOutput({ cleared: true, host }, program.opts().format || "json");
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
export {
|
|
167
|
-
registerAuth
|
|
168
|
-
};
|
package/dist/chunk-47MTN54I.js
DELETED
|
@@ -1,285 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
extractHostname,
|
|
3
|
-
getActiveHost,
|
|
4
|
-
getConfigDir,
|
|
5
|
-
logger,
|
|
6
|
-
safeJsonParse,
|
|
7
|
-
safeReadJsonFile
|
|
8
|
-
} from "./chunk-DC66L5AM.js";
|
|
9
|
-
|
|
10
|
-
// src/core/auth.ts
|
|
11
|
-
import fs from "fs";
|
|
12
|
-
import path from "path";
|
|
13
|
-
import { execFileSync } from "child_process";
|
|
14
|
-
var TOKENS_FILE = path.join(getConfigDir(), "tokens.json");
|
|
15
|
-
var LEGACY_DIR = path.join(process.env.HOME || "", ".te-mcp");
|
|
16
|
-
var TOKEN_TTL_MS = 20 * 60 * 60 * 1e3;
|
|
17
|
-
var OSASCRIPT_POLL_INTERVAL_MS = 2e3;
|
|
18
|
-
var OSASCRIPT_POLL_TIMEOUT_MS = 6e4;
|
|
19
|
-
function ensureDir() {
|
|
20
|
-
const dir = getConfigDir();
|
|
21
|
-
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
22
|
-
}
|
|
23
|
-
function resolveHost(hostOverride) {
|
|
24
|
-
if (hostOverride) return hostOverride;
|
|
25
|
-
return getActiveHost();
|
|
26
|
-
}
|
|
27
|
-
async function validateToken(token, hostUrl) {
|
|
28
|
-
const url = `${hostUrl}/v1/oauth/checkToken?accessToken=${token}`;
|
|
29
|
-
try {
|
|
30
|
-
const resp = await fetch(url, { method: "POST" });
|
|
31
|
-
const respJson = safeJsonParse(await resp.text());
|
|
32
|
-
return respJson?.return_code === 0;
|
|
33
|
-
} catch {
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
function loadAllTokens() {
|
|
38
|
-
try {
|
|
39
|
-
const legacyTokens = path.join(LEGACY_DIR, "tokens.json");
|
|
40
|
-
if (fs.existsSync(legacyTokens) && !fs.existsSync(TOKENS_FILE)) {
|
|
41
|
-
const data = safeReadJsonFile(legacyTokens);
|
|
42
|
-
const migrated = {};
|
|
43
|
-
for (const [key, val] of Object.entries(data)) {
|
|
44
|
-
const url = key.startsWith("http") ? key : `https://${key}`;
|
|
45
|
-
migrated[url] = val;
|
|
46
|
-
}
|
|
47
|
-
ensureDir();
|
|
48
|
-
fs.writeFileSync(TOKENS_FILE, JSON.stringify(migrated, null, 2));
|
|
49
|
-
return migrated;
|
|
50
|
-
}
|
|
51
|
-
if (fs.existsSync(TOKENS_FILE)) {
|
|
52
|
-
const data = safeReadJsonFile(TOKENS_FILE);
|
|
53
|
-
let needsMigration = false;
|
|
54
|
-
const migrated = {};
|
|
55
|
-
for (const [key, val] of Object.entries(data)) {
|
|
56
|
-
if (!key.startsWith("http")) {
|
|
57
|
-
migrated[`https://${key}`] = val;
|
|
58
|
-
needsMigration = true;
|
|
59
|
-
} else {
|
|
60
|
-
migrated[key] = val;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
if (needsMigration) {
|
|
64
|
-
fs.writeFileSync(TOKENS_FILE, JSON.stringify(migrated, null, 2));
|
|
65
|
-
return migrated;
|
|
66
|
-
}
|
|
67
|
-
return data;
|
|
68
|
-
}
|
|
69
|
-
} catch {
|
|
70
|
-
}
|
|
71
|
-
return {};
|
|
72
|
-
}
|
|
73
|
-
function saveAllTokens(tokens) {
|
|
74
|
-
ensureDir();
|
|
75
|
-
fs.writeFileSync(TOKENS_FILE, JSON.stringify(tokens, null, 2));
|
|
76
|
-
}
|
|
77
|
-
function loadToken(hostUrl) {
|
|
78
|
-
const tokens = loadAllTokens();
|
|
79
|
-
const entry = tokens[hostUrl];
|
|
80
|
-
if (!entry || !entry.token) return null;
|
|
81
|
-
if (entry.updatedAt) {
|
|
82
|
-
const age = Date.now() - new Date(entry.updatedAt).getTime();
|
|
83
|
-
if (age > TOKEN_TTL_MS) {
|
|
84
|
-
clearToken(hostUrl);
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
return { host: hostUrl, token: entry.token, updatedAt: entry.updatedAt };
|
|
89
|
-
}
|
|
90
|
-
function saveToken(token, hostUrl) {
|
|
91
|
-
const tokens = loadAllTokens();
|
|
92
|
-
tokens[hostUrl] = { token, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
93
|
-
saveAllTokens(tokens);
|
|
94
|
-
logger.info(`Token saved for ${hostUrl}`);
|
|
95
|
-
}
|
|
96
|
-
function clearToken(hostUrl) {
|
|
97
|
-
const tokens = loadAllTokens();
|
|
98
|
-
delete tokens[hostUrl];
|
|
99
|
-
saveAllTokens(tokens);
|
|
100
|
-
}
|
|
101
|
-
function setTokenManual(token, hostUrl) {
|
|
102
|
-
saveToken(token, hostUrl);
|
|
103
|
-
}
|
|
104
|
-
function getAuthStatus(hostUrl) {
|
|
105
|
-
if (process.env.TE_TOKEN) {
|
|
106
|
-
return { authenticated: true, host: hostUrl, source: "env:TE_TOKEN" };
|
|
107
|
-
}
|
|
108
|
-
const cached = loadToken(hostUrl);
|
|
109
|
-
if (cached) {
|
|
110
|
-
const ageMs = Date.now() - new Date(cached.updatedAt).getTime();
|
|
111
|
-
const hours = Math.round(ageMs / 36e5);
|
|
112
|
-
return { authenticated: true, host: hostUrl, tokenAge: `${hours}h ago`, source: "cache" };
|
|
113
|
-
}
|
|
114
|
-
return { authenticated: false, host: hostUrl };
|
|
115
|
-
}
|
|
116
|
-
function extractTokenViaOsascript(hostUrl) {
|
|
117
|
-
if (process.platform !== "darwin") return { token: null, error: "not_mac" };
|
|
118
|
-
const hostname = extractHostname(hostUrl);
|
|
119
|
-
const lines = [
|
|
120
|
-
'tell application "Google Chrome"',
|
|
121
|
-
" repeat with w in windows",
|
|
122
|
-
" repeat with t in tabs of w",
|
|
123
|
-
` if URL of t contains "${hostname}" then`,
|
|
124
|
-
` return execute t javascript "localStorage.getItem('ACCESS_TOKEN')"`,
|
|
125
|
-
" end if",
|
|
126
|
-
" end repeat",
|
|
127
|
-
" end repeat",
|
|
128
|
-
' return "NO_TAB_FOUND"',
|
|
129
|
-
"end tell"
|
|
130
|
-
];
|
|
131
|
-
try {
|
|
132
|
-
const args = lines.flatMap((line) => ["-e", line]);
|
|
133
|
-
const result = execFileSync("osascript", args, { encoding: "utf8", timeout: 5e3 }).trim();
|
|
134
|
-
if (result === "NO_TAB_FOUND") return { token: null, error: "no_tab" };
|
|
135
|
-
if (!result || result === "missing value") return { token: null, error: "no_token" };
|
|
136
|
-
return { token: result.replace(/^["']|["']$/g, ""), error: null };
|
|
137
|
-
} catch (e) {
|
|
138
|
-
const msg = e.message || "";
|
|
139
|
-
if (msg.includes("not allowed") || msg.includes("assistive access") || msg.includes("(-1743)")) {
|
|
140
|
-
return { token: null, error: "no_js_permission" };
|
|
141
|
-
}
|
|
142
|
-
return { token: null, error: "no_tab" };
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
function extractTokenFromAllTabs() {
|
|
146
|
-
const lines = [
|
|
147
|
-
'tell application "Google Chrome"',
|
|
148
|
-
" repeat with w in windows",
|
|
149
|
-
" repeat with t in tabs of w",
|
|
150
|
-
" try",
|
|
151
|
-
` set tokenVal to execute t javascript "localStorage.getItem('ACCESS_TOKEN')"`,
|
|
152
|
-
' if tokenVal is not null and tokenVal is not missing value and tokenVal is not "" then',
|
|
153
|
-
" return tokenVal",
|
|
154
|
-
" end if",
|
|
155
|
-
" end try",
|
|
156
|
-
" end repeat",
|
|
157
|
-
" end repeat",
|
|
158
|
-
' return "NO_TOKEN_FOUND"',
|
|
159
|
-
"end tell"
|
|
160
|
-
];
|
|
161
|
-
try {
|
|
162
|
-
const args = lines.flatMap((line) => ["-e", line]);
|
|
163
|
-
const result = execFileSync("osascript", args, { encoding: "utf8", timeout: 1e4 }).trim();
|
|
164
|
-
if (result === "NO_TOKEN_FOUND" || !result || result === "missing value") {
|
|
165
|
-
return { token: null, error: "no_token" };
|
|
166
|
-
}
|
|
167
|
-
return { token: result.replace(/^["']|["']$/g, ""), error: null };
|
|
168
|
-
} catch (e) {
|
|
169
|
-
const msg = e.message || "";
|
|
170
|
-
if (msg.includes("not allowed") || msg.includes("assistive access") || msg.includes("(-1743)")) {
|
|
171
|
-
return { token: null, error: "no_js_permission" };
|
|
172
|
-
}
|
|
173
|
-
return { token: null, error: "no_tab" };
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
async function requestTokenViaOsascript(hostUrl) {
|
|
177
|
-
logger.info(`Opening Chrome to ${hostUrl} for token extraction`);
|
|
178
|
-
process.stderr.write(`[ae-cli] No AE tab found in Chrome. Opening ${hostUrl} ...
|
|
179
|
-
`);
|
|
180
|
-
process.stderr.write(`[ae-cli] Please login, then your token will be captured automatically.
|
|
181
|
-
`);
|
|
182
|
-
try {
|
|
183
|
-
const openScript = [
|
|
184
|
-
'tell application "Google Chrome"',
|
|
185
|
-
" activate",
|
|
186
|
-
" if (count of windows) > 0 then",
|
|
187
|
-
` make new tab at end of tabs of window 1 with properties {URL:"${hostUrl}"}`,
|
|
188
|
-
" else",
|
|
189
|
-
` open location "${hostUrl}"`,
|
|
190
|
-
" end if",
|
|
191
|
-
"end tell"
|
|
192
|
-
];
|
|
193
|
-
execFileSync("osascript", openScript.flatMap((line) => ["-e", line]), { timeout: 5e3 });
|
|
194
|
-
} catch {
|
|
195
|
-
logger.warn(`Failed to open Chrome, user may need to open ${hostUrl} manually`);
|
|
196
|
-
process.stderr.write(`[ae-cli] Could not open browser. Please open ${hostUrl} in Chrome manually.
|
|
197
|
-
`);
|
|
198
|
-
}
|
|
199
|
-
const deadline = Date.now() + OSASCRIPT_POLL_TIMEOUT_MS;
|
|
200
|
-
let pollCount = 0;
|
|
201
|
-
while (Date.now() < deadline) {
|
|
202
|
-
await new Promise((r) => setTimeout(r, OSASCRIPT_POLL_INTERVAL_MS));
|
|
203
|
-
pollCount++;
|
|
204
|
-
let result = extractTokenViaOsascript(hostUrl);
|
|
205
|
-
if (result.error === "no_tab") {
|
|
206
|
-
const fallback = extractTokenFromAllTabs();
|
|
207
|
-
if (fallback.token) {
|
|
208
|
-
logger.info("Token captured via all-tabs fallback search");
|
|
209
|
-
result = fallback;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
if (result.token) {
|
|
213
|
-
saveToken(result.token, hostUrl);
|
|
214
|
-
logger.info(`Token captured after ${pollCount} poll(s)`);
|
|
215
|
-
process.stderr.write(`[ae-cli] Token captured from Chrome for ${hostUrl}.
|
|
216
|
-
`);
|
|
217
|
-
return result.token;
|
|
218
|
-
}
|
|
219
|
-
if (result.error === "no_js_permission") {
|
|
220
|
-
logger.warn("Chrome JS permission denied during polling");
|
|
221
|
-
return null;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
logger.warn(`Token polling timed out after ${OSASCRIPT_POLL_TIMEOUT_MS / 1e3}s (${pollCount} polls)`);
|
|
225
|
-
process.stderr.write(`[ae-cli] Polling timed out after ${OSASCRIPT_POLL_TIMEOUT_MS / 1e3}s.
|
|
226
|
-
`);
|
|
227
|
-
return null;
|
|
228
|
-
}
|
|
229
|
-
async function getToken(hostUrl) {
|
|
230
|
-
if (!hostUrl) {
|
|
231
|
-
throw new Error(
|
|
232
|
-
`No AE host configured.
|
|
233
|
-
Run: ae-cli config set-host`
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
if (process.env.TE_TOKEN) {
|
|
237
|
-
logger.info("Using token from env:TE_TOKEN");
|
|
238
|
-
return process.env.TE_TOKEN;
|
|
239
|
-
}
|
|
240
|
-
const cached = loadToken(hostUrl);
|
|
241
|
-
if (cached && cached.token) {
|
|
242
|
-
logger.info(`Using cached token for ${hostUrl}`);
|
|
243
|
-
return cached.token;
|
|
244
|
-
}
|
|
245
|
-
const { token: osascriptToken, error } = extractTokenViaOsascript(hostUrl);
|
|
246
|
-
if (error === "no_js_permission") {
|
|
247
|
-
logger.warn("Chrome JS Apple Events not enabled");
|
|
248
|
-
throw new Error(
|
|
249
|
-
`Chrome JavaScript from Apple Events is not enabled.
|
|
250
|
-
Enable it: Chrome menu \u2192 View \u2192 Developer \u2192 Allow JavaScript from Apple Events`
|
|
251
|
-
);
|
|
252
|
-
}
|
|
253
|
-
if (error === "not_mac") {
|
|
254
|
-
throw new Error(
|
|
255
|
-
`Auto token extraction is only available on macOS + Chrome.
|
|
256
|
-
Use: ae-cli auth set-token <token>`
|
|
257
|
-
);
|
|
258
|
-
}
|
|
259
|
-
if (osascriptToken) {
|
|
260
|
-
saveToken(osascriptToken, hostUrl);
|
|
261
|
-
return osascriptToken;
|
|
262
|
-
}
|
|
263
|
-
const polledToken = await requestTokenViaOsascript(hostUrl);
|
|
264
|
-
if (polledToken) {
|
|
265
|
-
saveToken(polledToken, hostUrl);
|
|
266
|
-
return polledToken;
|
|
267
|
-
}
|
|
268
|
-
throw new Error(
|
|
269
|
-
`Cannot obtain token for ${hostUrl}.
|
|
270
|
-
Options:
|
|
271
|
-
1. ae-cli auth login (macOS + Chrome)
|
|
272
|
-
2. ae-cli auth set-token <token>`
|
|
273
|
-
);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
export {
|
|
277
|
-
resolveHost,
|
|
278
|
-
validateToken,
|
|
279
|
-
loadToken,
|
|
280
|
-
saveToken,
|
|
281
|
-
clearToken,
|
|
282
|
-
setTokenManual,
|
|
283
|
-
getAuthStatus,
|
|
284
|
-
getToken
|
|
285
|
-
};
|
package/dist/config-5ELRYAIH.js
DELETED
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
clearToken,
|
|
3
|
-
getAuthStatus,
|
|
4
|
-
getToken,
|
|
5
|
-
loadToken,
|
|
6
|
-
validateToken
|
|
7
|
-
} from "./chunk-47MTN54I.js";
|
|
8
|
-
import {
|
|
9
|
-
addHost,
|
|
10
|
-
listHosts,
|
|
11
|
-
removeHost,
|
|
12
|
-
setActiveHost,
|
|
13
|
-
updateHostLabel
|
|
14
|
-
} from "./chunk-DC66L5AM.js";
|
|
15
|
-
|
|
16
|
-
// src/commands/config.ts
|
|
17
|
-
import * as readline from "readline";
|
|
18
|
-
function registerConfig(program) {
|
|
19
|
-
program.command("config").description("Interactive AE host configuration manager").action(async () => {
|
|
20
|
-
await runConfigUI(program);
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
async function runConfigUI(program) {
|
|
24
|
-
const hosts = listHosts();
|
|
25
|
-
if (hosts.length === 0) {
|
|
26
|
-
process.stderr.write("\x1B[1m[ae-cli] No AE hosts configured. Let's add one.\x1B[0m\n\n");
|
|
27
|
-
const newHost = await promptNewHost();
|
|
28
|
-
if (!newHost) {
|
|
29
|
-
process.stderr.write("Cancelled.\n");
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
addHost(newHost.url, newHost.label);
|
|
33
|
-
setActiveHost(newHost.url);
|
|
34
|
-
process.stderr.write(`
|
|
35
|
-
\x1B[32m\u2713\x1B[0m Added and activated: \x1B[1m${newHost.label}\x1B[0m ${newHost.url}
|
|
36
|
-
`);
|
|
37
|
-
await ensureAuth(newHost.url);
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
await hostManagerLoop(program);
|
|
41
|
-
}
|
|
42
|
-
async function hostManagerLoop(program) {
|
|
43
|
-
while (true) {
|
|
44
|
-
const hosts = listHosts();
|
|
45
|
-
const items = hosts.map((h) => ({
|
|
46
|
-
display: formatHostLine(h),
|
|
47
|
-
type: "host",
|
|
48
|
-
url: h.url,
|
|
49
|
-
label: h.label,
|
|
50
|
-
active: h.active
|
|
51
|
-
}));
|
|
52
|
-
items.push({ display: "\x1B[2m + Add new host...\x1B[0m", type: "add", url: "", label: "", active: false });
|
|
53
|
-
const activeIdx = items.findIndex((i) => i.active);
|
|
54
|
-
process.stderr.write("\n\x1B[1mTE Host Manager\x1B[0m (\u2191\u2193 select \xB7 Enter switch \xB7 e edit label \xB7 d delete \xB7 a add \xB7 q quit)\n\n");
|
|
55
|
-
const result = await interactiveHostSelect(items, activeIdx >= 0 ? activeIdx : 0);
|
|
56
|
-
if (result.action === "quit" || result.action === "cancel") {
|
|
57
|
-
break;
|
|
58
|
-
}
|
|
59
|
-
const item = items[result.index];
|
|
60
|
-
if (result.action === "select") {
|
|
61
|
-
if (item.type === "add") {
|
|
62
|
-
await handleAddHost();
|
|
63
|
-
} else {
|
|
64
|
-
setActiveHost(item.url);
|
|
65
|
-
process.stderr.write(`
|
|
66
|
-
\x1B[32m\u2713\x1B[0m Switched to: \x1B[1m${item.label}\x1B[0m ${item.url}
|
|
67
|
-
`);
|
|
68
|
-
await ensureAuth(item.url);
|
|
69
|
-
break;
|
|
70
|
-
}
|
|
71
|
-
} else if (result.action === "edit" && item.type === "host") {
|
|
72
|
-
await handleEditLabel(item.url, item.label);
|
|
73
|
-
} else if (result.action === "delete" && item.type === "host") {
|
|
74
|
-
await handleDeleteHost(item.url, item.label, item.active);
|
|
75
|
-
} else if (result.action === "add") {
|
|
76
|
-
await handleAddHost();
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
function formatHostLine(h) {
|
|
81
|
-
const marker = h.active ? "\x1B[32m\u25CF\x1B[0m" : "\x1B[2m\u25CB\x1B[0m";
|
|
82
|
-
const labelText = h.active ? `\x1B[1;32m${h.label}\x1B[0m` : `\x1B[1m${h.label}\x1B[0m`;
|
|
83
|
-
const authInfo = getAuthStatus(h.url);
|
|
84
|
-
const authBadge = authInfo.authenticated ? `\x1B[32m\u2713\x1B[0m` : `\x1B[31m\u2717\x1B[0m`;
|
|
85
|
-
return `${marker} ${labelText} ${h.url} ${authBadge}`;
|
|
86
|
-
}
|
|
87
|
-
async function handleAddHost() {
|
|
88
|
-
const newHost = await promptNewHost();
|
|
89
|
-
if (!newHost) {
|
|
90
|
-
process.stderr.write("Cancelled.\n");
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
addHost(newHost.url, newHost.label);
|
|
94
|
-
setActiveHost(newHost.url);
|
|
95
|
-
process.stderr.write(`
|
|
96
|
-
\x1B[32m\u2713\x1B[0m Added and activated: \x1B[1m${newHost.label}\x1B[0m ${newHost.url}
|
|
97
|
-
`);
|
|
98
|
-
await ensureAuth(newHost.url);
|
|
99
|
-
}
|
|
100
|
-
async function handleEditLabel(url, currentLabel) {
|
|
101
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
102
|
-
const newLabel = await new Promise(
|
|
103
|
-
(resolve) => rl.question(`
|
|
104
|
-
New label [${currentLabel}]: `, (a) => {
|
|
105
|
-
rl.close();
|
|
106
|
-
resolve(a.trim() || currentLabel);
|
|
107
|
-
})
|
|
108
|
-
);
|
|
109
|
-
if (newLabel !== currentLabel) {
|
|
110
|
-
updateHostLabel(url, newLabel);
|
|
111
|
-
process.stderr.write(`\x1B[32m\u2713\x1B[0m Label updated: ${newLabel}
|
|
112
|
-
`);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
async function handleDeleteHost(url, label, isActive) {
|
|
116
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
117
|
-
const answer = await new Promise(
|
|
118
|
-
(resolve) => rl.question(`
|
|
119
|
-
Delete \x1B[1m${label}\x1B[0m (${url})? [y/N]: `, (a) => {
|
|
120
|
-
rl.close();
|
|
121
|
-
resolve(a.trim().toLowerCase());
|
|
122
|
-
})
|
|
123
|
-
);
|
|
124
|
-
if (answer === "y" || answer === "yes") {
|
|
125
|
-
removeHost(url);
|
|
126
|
-
process.stderr.write(`\x1B[32m\u2713\x1B[0m Deleted: ${label}
|
|
127
|
-
`);
|
|
128
|
-
if (isActive) {
|
|
129
|
-
const remaining = listHosts();
|
|
130
|
-
if (remaining.length > 0) {
|
|
131
|
-
process.stderr.write(` Active host switched to: ${remaining[0].label}
|
|
132
|
-
`);
|
|
133
|
-
} else {
|
|
134
|
-
process.stderr.write(" No hosts remaining. Run ae-cli config to add one.\n");
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
async function ensureAuth(hostUrl) {
|
|
140
|
-
const status = getAuthStatus(hostUrl);
|
|
141
|
-
if (status.authenticated) {
|
|
142
|
-
const cached = loadToken(hostUrl);
|
|
143
|
-
if (cached && cached.token) {
|
|
144
|
-
process.stderr.write(`\x1B[2m[ae-cli] Validating token...\x1B[0m
|
|
145
|
-
`);
|
|
146
|
-
const isValid = await validateToken(cached.token, hostUrl);
|
|
147
|
-
if (isValid) {
|
|
148
|
-
process.stderr.write(`\x1B[32m\u2713\x1B[0m Token valid for ${hostUrl}
|
|
149
|
-
`);
|
|
150
|
-
return;
|
|
151
|
-
} else {
|
|
152
|
-
clearToken(hostUrl);
|
|
153
|
-
process.stderr.write(`\x1B[33m!\x1B[0m Token expired or invalid. Re-authenticating...
|
|
154
|
-
`);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
process.stderr.write(`
|
|
159
|
-
\x1B[33m!\x1B[0m No valid token for ${hostUrl}. Starting login...
|
|
160
|
-
`);
|
|
161
|
-
try {
|
|
162
|
-
await getToken(hostUrl);
|
|
163
|
-
process.stderr.write(`\x1B[32m\u2713\x1B[0m Authenticated to ${hostUrl}
|
|
164
|
-
`);
|
|
165
|
-
} catch (err) {
|
|
166
|
-
process.stderr.write(`\x1B[31m\u2717\x1B[0m Auth failed: ${err.message}
|
|
167
|
-
`);
|
|
168
|
-
process.stderr.write(` You can set token manually: ae-cli auth set-token <token>
|
|
169
|
-
`);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
async function promptNewHost() {
|
|
173
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
174
|
-
const ask = (q) => new Promise((resolve) => rl.question(q, (a) => resolve(a.trim())));
|
|
175
|
-
const url = await ask("AE host URL (e.g., https://ta.thinkingdata.cn): ");
|
|
176
|
-
if (!url) {
|
|
177
|
-
rl.close();
|
|
178
|
-
return null;
|
|
179
|
-
}
|
|
180
|
-
const label = await ask("Label: ");
|
|
181
|
-
rl.close();
|
|
182
|
-
if (!label) return null;
|
|
183
|
-
return { url: normalizeUrl(url), label };
|
|
184
|
-
}
|
|
185
|
-
function normalizeUrl(url) {
|
|
186
|
-
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
|
187
|
-
url = `https://${url}`;
|
|
188
|
-
}
|
|
189
|
-
return url.replace(/\/$/, "");
|
|
190
|
-
}
|
|
191
|
-
function interactiveHostSelect(items, initialIndex) {
|
|
192
|
-
return new Promise((resolve) => {
|
|
193
|
-
let cursor = initialIndex;
|
|
194
|
-
const stdin = process.stdin;
|
|
195
|
-
const stderr = process.stderr;
|
|
196
|
-
let renderCount = 0;
|
|
197
|
-
stderr.write("\x1B[?25l");
|
|
198
|
-
function render() {
|
|
199
|
-
if (renderCount > 0) {
|
|
200
|
-
stderr.write(`\x1B[${items.length}A`);
|
|
201
|
-
}
|
|
202
|
-
for (let i = 0; i < items.length; i++) {
|
|
203
|
-
const pointer = i === cursor ? "\x1B[36m\u276F\x1B[0m" : " ";
|
|
204
|
-
stderr.write(`${pointer} ${items[i].display}\x1B[K
|
|
205
|
-
`);
|
|
206
|
-
}
|
|
207
|
-
renderCount++;
|
|
208
|
-
}
|
|
209
|
-
render();
|
|
210
|
-
if (!stdin.isTTY) {
|
|
211
|
-
stderr.write("\x1B[?25h");
|
|
212
|
-
resolve({ action: "select", index: initialIndex });
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
stdin.setRawMode(true);
|
|
216
|
-
stdin.resume();
|
|
217
|
-
stdin.setEncoding("utf8");
|
|
218
|
-
function cleanup() {
|
|
219
|
-
stdin.setRawMode(false);
|
|
220
|
-
stdin.pause();
|
|
221
|
-
stdin.removeListener("data", onData);
|
|
222
|
-
stderr.write("\x1B[?25h");
|
|
223
|
-
}
|
|
224
|
-
function onData(key) {
|
|
225
|
-
if (key === "\x1B[A" || key === "k") {
|
|
226
|
-
cursor = (cursor - 1 + items.length) % items.length;
|
|
227
|
-
render();
|
|
228
|
-
} else if (key === "\x1B[B" || key === "j") {
|
|
229
|
-
cursor = (cursor + 1) % items.length;
|
|
230
|
-
render();
|
|
231
|
-
} else if (key === "\r" || key === "\n") {
|
|
232
|
-
cleanup();
|
|
233
|
-
resolve({ action: "select", index: cursor });
|
|
234
|
-
} else if (key === "e" || key === "E") {
|
|
235
|
-
if (items[cursor].type === "host") {
|
|
236
|
-
cleanup();
|
|
237
|
-
resolve({ action: "edit", index: cursor });
|
|
238
|
-
}
|
|
239
|
-
} else if (key === "d" || key === "D") {
|
|
240
|
-
if (items[cursor].type === "host") {
|
|
241
|
-
cleanup();
|
|
242
|
-
resolve({ action: "delete", index: cursor });
|
|
243
|
-
}
|
|
244
|
-
} else if (key === "a" || key === "A") {
|
|
245
|
-
cleanup();
|
|
246
|
-
resolve({ action: "add", index: cursor });
|
|
247
|
-
} else if (key === "q" || key === "Q" || key === "\x1B" || key === "") {
|
|
248
|
-
cleanup();
|
|
249
|
-
resolve({ action: "quit", index: cursor });
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
stdin.on("data", onData);
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
export {
|
|
256
|
-
registerConfig
|
|
257
|
-
};
|