@thinkingai/ae-cli 1.0.1 → 1.0.3
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 +16 -13
- package/dist/auth-BQC5DBE7.js +19 -0
- package/dist/auth-DXKYLMO7.js +81 -0
- package/dist/chunk-3CDLNXTD.js +218 -0
- package/dist/chunk-53FNNCZW.js +174 -0
- package/dist/chunk-EBFUVWIZ.js +127 -0
- package/dist/chunk-ZYXIGPG7.js +148 -0
- package/dist/client-T4VODLA6.js +16 -0
- package/dist/community-FVDAWGS4.js +684 -0
- package/dist/config-V7AXKDMB.js +242 -0
- package/dist/index.js +16 -15
- package/dist/raw-G3GKHAPZ.js +61 -0
- package/dist/te-analysis-CHFTDN55.js +871 -0
- package/dist/te-audience-M6LQY6M7.js +350 -0
- package/dist/te-common-BLQ7DVGC.js +144 -0
- package/dist/te-dataops-V4OTKY35.js +1853 -0
- package/dist/te-engage-DXE4R5EW.js +1470 -0
- package/dist/te-meta-FMM7KCVI.js +479 -0
- package/package.json +7 -5
- package/skills/te-analysis/SKILL.md +10 -10
- package/skills/te-analysis/references/build-entity-details-sql.md +10 -10
- package/skills/te-analysis/references/build-event-details-sql.md +10 -10
- package/skills/te-analysis/references/create-alert.md +4 -4
- package/skills/te-analysis/references/create-dashboard.md +4 -4
- package/skills/te-analysis/references/create-or-update-dashboard-note.md +4 -4
- package/skills/te-analysis/references/create-public-access-link.md +4 -4
- package/skills/te-analysis/references/create-report.md +10 -10
- package/skills/te-analysis/references/create-result-cluster.md +4 -4
- package/skills/te-analysis/references/drilldown-user-events.md +6 -6
- package/skills/te-analysis/references/drilldown-users.md +4 -4
- package/skills/te-analysis/references/get-alert-definition-schema.md +3 -3
- package/skills/te-analysis/references/get-alert.md +3 -3
- package/skills/te-analysis/references/get-analysis-query-schema.md +4 -4
- package/skills/te-analysis/references/get-filter-schema.md +3 -3
- package/skills/te-analysis/references/get-groupby-schema.md +3 -3
- package/skills/te-analysis/references/get-report-definition.md +3 -3
- package/skills/te-analysis/references/get-table-columns.md +3 -3
- package/skills/te-analysis/references/list-alerts.md +4 -4
- package/skills/te-analysis/references/list-dashboards.md +4 -4
- package/skills/te-analysis/references/list-public-access-links.md +3 -3
- package/skills/te-analysis/references/list-reports.md +4 -4
- package/skills/te-analysis/references/load-filters.md +4 -4
- package/skills/te-analysis/references/query-adhoc.md +10 -10
- package/skills/te-analysis/references/query-dashboard-detail.md +3 -3
- package/skills/te-analysis/references/query-dashboard-report-data.md +9 -9
- package/skills/te-analysis/references/query-entity-details.md +10 -10
- package/skills/te-analysis/references/query-event-details.md +10 -10
- package/skills/te-analysis/references/query-report-data.md +11 -11
- package/skills/te-analysis/references/update-alert.md +4 -4
- package/skills/te-analysis/references/update-dashboard.md +4 -4
- package/skills/te-analysis/references/update-public-access-link.md +4 -4
- package/skills/te-audience/SKILL.md +5 -5
- package/skills/te-audience/references/create-cluster.md +10 -10
- package/skills/te-audience/references/create-tag.md +10 -10
- package/skills/te-audience/references/get-cluster-definition-schema.md +9 -9
- package/skills/te-audience/references/get-clusters-by-name.md +3 -3
- package/skills/te-audience/references/get-tag-definition-schema.md +3 -3
- package/skills/te-audience/references/get-tags-by-name.md +3 -3
- package/skills/te-audience/references/list-cluster-members.md +4 -4
- package/skills/te-audience/references/list-clusters.md +4 -4
- package/skills/te-audience/references/list-tag-members.md +4 -4
- package/skills/te-audience/references/list-tags.md +4 -4
- package/skills/te-audience/references/refresh-cluster.md +3 -3
- package/skills/te-audience/references/refresh-tag.md +3 -3
- package/skills/te-audience/references/update-cluster.md +9 -9
- package/skills/te-audience/references/update-tag.md +10 -10
- package/skills/te-common/SKILL.md +2 -2
- package/skills/te-common/references/get-resource-url.md +5 -5
- package/skills/te-common/references/list-projects.md +3 -3
- package/skills/te-community/SKILL.md +61 -69
- package/skills/te-community/references/community-activity-analysis.md +43 -43
- package/skills/te-community/references/community-analyzing-official-content.md +47 -47
- package/skills/te-community/references/community-analyzing-theme-comment.md +87 -87
- package/skills/te-community/references/community-character-analysis.md +44 -44
- package/skills/te-community/references/community-daily-report.md +56 -53
- package/skills/te-community/references/community-hottopic-insight.md +81 -87
- package/skills/te-community/references/community-weekly-report.md +54 -53
- package/skills/te-community/references/get_channel_info.md +9 -9
- package/skills/te-community/references/get_comment_tag_analysis.md +15 -15
- package/skills/te-community/references/get_comments_summary.md +15 -15
- package/skills/te-community/references/get_corpus_tags.md +9 -9
- package/skills/te-community/references/get_daily_summary.md +10 -10
- package/skills/te-community/references/get_hot_topics.md +12 -12
- package/skills/te-community/references/get_livestream_analysis.md +9 -9
- package/skills/te-community/references/get_livestream_detail.md +19 -19
- package/skills/te-community/references/get_livestream_list.md +13 -13
- package/skills/te-community/references/get_livestream_overview.md +11 -11
- package/skills/te-community/references/get_livestream_room_metrics.md +12 -12
- package/skills/te-community/references/get_livestream_rooms.md +12 -12
- package/skills/te-community/references/get_overview_metrics.md +12 -12
- package/skills/te-community/references/get_post_detail.md +21 -21
- package/skills/te-community/references/get_risk_content.md +23 -23
- package/skills/te-community/references/get_sentiment_overview.md +16 -16
- package/skills/te-community/references/get_tag_trends.md +13 -13
- package/skills/te-community/references/get_topic_detail.md +12 -12
- package/skills/te-community/references/search_posts.md +26 -26
- package/skills/te-dataops/SKILL.md +390 -392
- package/skills/te-dataops/references/dataops-flow-create.md +87 -87
- package/skills/te-dataops/references/dataops-flow-monitor.md +65 -65
- package/skills/te-dataops/references/dataops-integration.md +90 -85
- package/skills/te-dataops/references/dataops-query.md +58 -58
- package/skills/te-dataops/references/dataops-table.md +63 -63
- package/skills/te-engage/SKILL.md +103 -103
- package/skills/te-engage/references/add-approver.md +11 -11
- package/skills/te-engage/references/add-channel.md +34 -34
- package/skills/te-engage/references/approver-list.md +8 -8
- package/skills/te-engage/references/cancel-query-by-request-id.md +10 -10
- package/skills/te-engage/references/channel-detail.md +13 -13
- package/skills/te-engage/references/channel-list.md +15 -15
- package/skills/te-engage/references/config-channel-detail.md +10 -10
- package/skills/te-engage/references/config-channel-list.md +11 -11
- package/skills/te-engage/references/config-item-analysis-report.md +21 -21
- package/skills/te-engage/references/config-item-detail.md +10 -10
- package/skills/te-engage/references/config-item-list.md +8 -8
- package/skills/te-engage/references/config-item-strategy-comparison.md +14 -14
- package/skills/te-engage/references/config-item-trigger-report.md +26 -26
- package/skills/te-engage/references/copy-config-template.md +16 -16
- package/skills/te-engage/references/delete-channel.md +11 -11
- package/skills/te-engage/references/delete-config-channel.md +11 -11
- package/skills/te-engage/references/delete-config-item.md +12 -12
- package/skills/te-engage/references/delete-flow.md +11 -11
- package/skills/te-engage/references/flow-ab-split-node-report.md +29 -29
- package/skills/te-engage/references/flow-detail.md +19 -19
- package/skills/te-engage/references/flow-list.md +8 -8
- package/skills/te-engage/references/flow-node-config-schema.md +11 -11
- package/skills/te-engage/references/flow-node-detail-report.md +30 -30
- package/skills/te-engage/references/flow-node-overview-report.md +27 -27
- package/skills/te-engage/references/flow-process-report.md +32 -32
- package/skills/te-engage/references/manage-flow.md +44 -44
- package/skills/te-engage/references/manage-strategy.md +37 -37
- package/skills/te-engage/references/manage-task.md +20 -20
- package/skills/te-engage/references/modify-flow-base-info.md +14 -14
- package/skills/te-engage/references/save-flow.md +264 -263
- package/skills/te-engage/references/strategy-detail.md +16 -16
- package/skills/te-engage/references/strategy-list.md +10 -10
- package/skills/te-engage/references/task-data-detail.md +47 -47
- package/skills/te-engage/references/task-data-overview.md +23 -23
- package/skills/te-engage/references/task-detail.md +18 -18
- package/skills/te-engage/references/task-experiment-report.md +26 -26
- package/skills/te-engage/references/task-list.md +35 -35
- package/skills/te-engage/references/task-metric-detail.md +31 -31
- package/skills/te-engage/references/task-stats.md +31 -31
- package/skills/te-engage/references/update-channel-status.md +13 -13
- package/skills/te-engage/references/update-config-channel-status.md +14 -14
- package/skills/te-engage/references/validate-flow-node-config.md +15 -15
- package/skills/te-engage/references/whitelist-list.md +8 -8
- package/skills/te-meta/SKILL.md +5 -5
- package/skills/te-meta/references/batch-create-metadata.md +4 -4
- package/skills/te-meta/references/batch-edit-metadata.md +3 -3
- package/skills/te-meta/references/create-metric.md +12 -12
- package/skills/te-meta/references/create-project-mark-time.md +4 -4
- package/skills/te-meta/references/create-virtual-event.md +10 -10
- package/skills/te-meta/references/create-virtual-property.md +8 -8
- package/skills/te-meta/references/delete-project-mark-times.md +3 -3
- package/skills/te-meta/references/delete-track-items.md +3 -3
- package/skills/te-meta/references/get-metric.md +3 -3
- package/skills/te-meta/references/get-project-config.md +3 -3
- package/skills/te-meta/references/get-track-program.md +3 -3
- package/skills/te-meta/references/list-entities.md +4 -4
- package/skills/te-meta/references/list-events.md +4 -4
- package/skills/te-meta/references/list-metrics.md +4 -4
- package/skills/te-meta/references/list-project-mark-times.md +4 -4
- package/skills/te-meta/references/list-project-users.md +3 -3
- package/skills/te-meta/references/list-properties.md +4 -4
- package/skills/te-meta/references/save-track-items.md +3 -3
- package/skills/te-meta/references/update-metric.md +10 -10
- package/skills/te-meta/references/update-project-mark-time.md +4 -4
- package/skills/te-shared/SKILL.md +8 -8
- package/skills/te-community/references/community-release-analysis.md +0 -79
- package/skills/te-dataops/references/dataops-operations.md +0 -107
package/README.md
CHANGED
|
@@ -50,10 +50,13 @@ After selecting a host, ae-cli automatically checks if the token is valid. If no
|
|
|
50
50
|
|
|
51
51
|
```bash
|
|
52
52
|
# TE meta domain (metadata and governance)
|
|
53
|
-
ae-cli
|
|
53
|
+
ae-cli analysis_meta +list_events --project_id 1
|
|
54
54
|
|
|
55
55
|
# Table output
|
|
56
|
-
ae-cli
|
|
56
|
+
ae-cli analysis_meta +list_events --project_id 1 --format table
|
|
57
|
+
|
|
58
|
+
# Raw API call
|
|
59
|
+
ae-cli api GET /v1/ta/event/catalog/listEvent --params '{"projectId": 1}'
|
|
57
60
|
```
|
|
58
61
|
|
|
59
62
|
## Authentication
|
|
@@ -80,13 +83,13 @@ ae-cli auth logout
|
|
|
80
83
|
|
|
81
84
|
| Domain | Commands | Description |
|
|
82
85
|
|--------|----------|-------------|
|
|
83
|
-
| `
|
|
84
|
-
| `
|
|
85
|
-
| `
|
|
86
|
-
| `
|
|
86
|
+
| `analysis` | 31 | Analysis workflows: alerts, reports, dashboards, ad-hoc/drilldown, clusters/tags, metadata/metrics, project tools, entity details, analysis schema |
|
|
87
|
+
| `analysis_audience` | 14 | Audience operations: clusters, tags, and cluster/tag definition schema |
|
|
88
|
+
| `analysis_meta` | 20 | Metadata governance: events/properties, metrics, virtual metadata, project config, tracking plan, mark times, resource links |
|
|
89
|
+
| `engage` | 40+ | Hermes Engage MCP: channels, tasks, configs, flows, strategies |
|
|
87
90
|
| `te_dataops` | 50+ | Data warehouse management: repos, datatables, flows, IDE queries, integration, operations |
|
|
88
91
|
| `te_community` | 30+ | Community analysis: posts search, sentiment analysis, topic trends, livestream data |
|
|
89
|
-
| `
|
|
92
|
+
| `analysis_common` | 2 | Cross-module common constraints: resource link completion, project ID gate |
|
|
90
93
|
| `operation` | 11 | Tasks, flows, channels, space navigation |
|
|
91
94
|
|
|
92
95
|
### Global Options
|
|
@@ -117,7 +120,7 @@ ae-cli auth logout
|
|
|
117
120
|
Install them with:
|
|
118
121
|
|
|
119
122
|
```bash
|
|
120
|
-
npx skills add
|
|
123
|
+
npx skills add http://10.27.249.150:8888/te-ai/te-cli.git#release/6.0 te-ai/ae-cli -g -y
|
|
121
124
|
```
|
|
122
125
|
|
|
123
126
|
## Skill Details
|
|
@@ -147,7 +150,7 @@ Metadata and tracking-plan governance:
|
|
|
147
150
|
- **Mark Times**: date markers management
|
|
148
151
|
- **Entity Catalog**: entity listing
|
|
149
152
|
|
|
150
|
-
###
|
|
153
|
+
### engage (40+ tools)
|
|
151
154
|
|
|
152
155
|
Hermes Engage MCP capabilities:
|
|
153
156
|
- **Channels**: channel management, config channels, approval management
|
|
@@ -218,10 +221,10 @@ src/
|
|
|
218
221
|
## Verification Scripts
|
|
219
222
|
|
|
220
223
|
```bash
|
|
221
|
-
npm run verify:
|
|
222
|
-
npm run verify:
|
|
223
|
-
npm run verify:
|
|
224
|
-
npm run verify:
|
|
224
|
+
npm run verify:analysis-tools
|
|
225
|
+
npm run verify:analysis-audience-tools
|
|
226
|
+
npm run verify:analysis-meta-tools
|
|
227
|
+
npm run verify:analysis-common-tools
|
|
225
228
|
```
|
|
226
229
|
|
|
227
230
|
## License
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
clearToken,
|
|
3
|
+
getAuthStatus,
|
|
4
|
+
getToken,
|
|
5
|
+
loadToken,
|
|
6
|
+
resolveHost,
|
|
7
|
+
saveToken,
|
|
8
|
+
setTokenManual
|
|
9
|
+
} from "./chunk-3CDLNXTD.js";
|
|
10
|
+
import "./chunk-EBFUVWIZ.js";
|
|
11
|
+
export {
|
|
12
|
+
clearToken,
|
|
13
|
+
getAuthStatus,
|
|
14
|
+
getToken,
|
|
15
|
+
loadToken,
|
|
16
|
+
resolveHost,
|
|
17
|
+
saveToken,
|
|
18
|
+
setTokenManual
|
|
19
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import {
|
|
2
|
+
printError,
|
|
3
|
+
printOutput
|
|
4
|
+
} from "./chunk-KM57HI5B.js";
|
|
5
|
+
import {
|
|
6
|
+
clearToken,
|
|
7
|
+
getAuthStatus,
|
|
8
|
+
getToken,
|
|
9
|
+
resolveHost,
|
|
10
|
+
setTokenManual
|
|
11
|
+
} from "./chunk-3CDLNXTD.js";
|
|
12
|
+
import {
|
|
13
|
+
loadConfig,
|
|
14
|
+
saveConfig
|
|
15
|
+
} from "./chunk-EBFUVWIZ.js";
|
|
16
|
+
|
|
17
|
+
// src/commands/auth.ts
|
|
18
|
+
function registerAuth(program) {
|
|
19
|
+
const auth = program.command("auth").description("Authentication management");
|
|
20
|
+
auth.command("login").description("Login to TE (auto-extract token from Chrome on macOS)").action(async () => {
|
|
21
|
+
const host = resolveHost(program.opts().host);
|
|
22
|
+
if (!host) {
|
|
23
|
+
printError("config", "No TE host configured.", "Run: ae-cli config set-host");
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const token = await getToken(host);
|
|
28
|
+
process.stderr.write(`[ae-cli] Authenticated to ${host}
|
|
29
|
+
`);
|
|
30
|
+
printOutput({ authenticated: true, host, token: token.slice(0, 8) + "..." }, program.opts().format || "json");
|
|
31
|
+
} catch (err) {
|
|
32
|
+
printError("auth", err.message);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
auth.command("set-token <token>").description("Manually set authentication token for active host").action((token) => {
|
|
37
|
+
const host = resolveHost(program.opts().host);
|
|
38
|
+
if (!host) {
|
|
39
|
+
printError("config", "No TE host configured.", "Run: ae-cli config set-host");
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
const config = loadConfig();
|
|
43
|
+
if (!config.hosts[host]) {
|
|
44
|
+
config.hosts[host] = { label: host };
|
|
45
|
+
if (!config.activeHost) {
|
|
46
|
+
config.activeHost = host;
|
|
47
|
+
}
|
|
48
|
+
saveConfig(config);
|
|
49
|
+
process.stderr.write(`[ae-cli] Config saved for ${host}
|
|
50
|
+
`);
|
|
51
|
+
printOutput({ saved: true, config }, program.opts().format || "json");
|
|
52
|
+
}
|
|
53
|
+
setTokenManual(token, host);
|
|
54
|
+
process.stderr.write(`[ae-cli] Token saved for ${host}
|
|
55
|
+
`);
|
|
56
|
+
printOutput({ saved: true, host }, program.opts().format || "json");
|
|
57
|
+
});
|
|
58
|
+
auth.command("status").description("Show current authentication status").action(() => {
|
|
59
|
+
const host = resolveHost(program.opts().host);
|
|
60
|
+
if (!host) {
|
|
61
|
+
printOutput({ authenticated: false, host: "(none)", hint: "Run: ae-cli config set-host" }, program.opts().format || "json");
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const status = getAuthStatus(host);
|
|
65
|
+
printOutput(status, program.opts().format || "json");
|
|
66
|
+
});
|
|
67
|
+
auth.command("logout").description("Clear stored token for active host").action(() => {
|
|
68
|
+
const host = resolveHost(program.opts().host);
|
|
69
|
+
if (!host) {
|
|
70
|
+
printError("config", "No TE host configured.", "Run: ae-cli config set-host");
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
clearToken(host);
|
|
74
|
+
process.stderr.write(`[ae-cli] Token cleared for ${host}
|
|
75
|
+
`);
|
|
76
|
+
printOutput({ cleared: true, host }, program.opts().format || "json");
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
export {
|
|
80
|
+
registerAuth
|
|
81
|
+
};
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import {
|
|
2
|
+
extractHostname,
|
|
3
|
+
getActiveHost,
|
|
4
|
+
getConfigDir,
|
|
5
|
+
safeReadJsonFile
|
|
6
|
+
} from "./chunk-EBFUVWIZ.js";
|
|
7
|
+
|
|
8
|
+
// src/core/auth.ts
|
|
9
|
+
import fs from "fs";
|
|
10
|
+
import path from "path";
|
|
11
|
+
import { execFileSync } from "child_process";
|
|
12
|
+
var TOKENS_FILE = path.join(getConfigDir(), "tokens.json");
|
|
13
|
+
var LEGACY_DIR = path.join(process.env.HOME || "", ".te-mcp");
|
|
14
|
+
var TOKEN_TTL_MS = 20 * 60 * 60 * 1e3;
|
|
15
|
+
var OSASCRIPT_POLL_INTERVAL_MS = 2e3;
|
|
16
|
+
var OSASCRIPT_POLL_TIMEOUT_MS = 6e4;
|
|
17
|
+
function ensureDir() {
|
|
18
|
+
const dir = getConfigDir();
|
|
19
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
20
|
+
}
|
|
21
|
+
function resolveHost(hostOverride) {
|
|
22
|
+
if (hostOverride) return hostOverride;
|
|
23
|
+
return getActiveHost();
|
|
24
|
+
}
|
|
25
|
+
function loadAllTokens() {
|
|
26
|
+
try {
|
|
27
|
+
const legacyTokens = path.join(LEGACY_DIR, "tokens.json");
|
|
28
|
+
if (fs.existsSync(legacyTokens) && !fs.existsSync(TOKENS_FILE)) {
|
|
29
|
+
const data = safeReadJsonFile(legacyTokens);
|
|
30
|
+
const migrated = {};
|
|
31
|
+
for (const [key, val] of Object.entries(data)) {
|
|
32
|
+
const url = key.startsWith("http") ? key : `https://${key}`;
|
|
33
|
+
migrated[url] = val;
|
|
34
|
+
}
|
|
35
|
+
ensureDir();
|
|
36
|
+
fs.writeFileSync(TOKENS_FILE, JSON.stringify(migrated, null, 2));
|
|
37
|
+
return migrated;
|
|
38
|
+
}
|
|
39
|
+
if (fs.existsSync(TOKENS_FILE)) {
|
|
40
|
+
const data = safeReadJsonFile(TOKENS_FILE);
|
|
41
|
+
let needsMigration = false;
|
|
42
|
+
const migrated = {};
|
|
43
|
+
for (const [key, val] of Object.entries(data)) {
|
|
44
|
+
if (!key.startsWith("http")) {
|
|
45
|
+
migrated[`https://${key}`] = val;
|
|
46
|
+
needsMigration = true;
|
|
47
|
+
} else {
|
|
48
|
+
migrated[key] = val;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (needsMigration) {
|
|
52
|
+
fs.writeFileSync(TOKENS_FILE, JSON.stringify(migrated, null, 2));
|
|
53
|
+
return migrated;
|
|
54
|
+
}
|
|
55
|
+
return data;
|
|
56
|
+
}
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
return {};
|
|
60
|
+
}
|
|
61
|
+
function saveAllTokens(tokens) {
|
|
62
|
+
ensureDir();
|
|
63
|
+
fs.writeFileSync(TOKENS_FILE, JSON.stringify(tokens, null, 2));
|
|
64
|
+
}
|
|
65
|
+
function loadToken(hostUrl) {
|
|
66
|
+
const tokens = loadAllTokens();
|
|
67
|
+
const entry = tokens[hostUrl];
|
|
68
|
+
if (!entry || !entry.token) return null;
|
|
69
|
+
if (entry.updatedAt) {
|
|
70
|
+
const age = Date.now() - new Date(entry.updatedAt).getTime();
|
|
71
|
+
if (age > TOKEN_TTL_MS) {
|
|
72
|
+
clearToken(hostUrl);
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return { host: hostUrl, token: entry.token, updatedAt: entry.updatedAt };
|
|
77
|
+
}
|
|
78
|
+
function saveToken(token, hostUrl) {
|
|
79
|
+
const tokens = loadAllTokens();
|
|
80
|
+
tokens[hostUrl] = { token, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
81
|
+
saveAllTokens(tokens);
|
|
82
|
+
}
|
|
83
|
+
function clearToken(hostUrl) {
|
|
84
|
+
const tokens = loadAllTokens();
|
|
85
|
+
delete tokens[hostUrl];
|
|
86
|
+
saveAllTokens(tokens);
|
|
87
|
+
}
|
|
88
|
+
function setTokenManual(token, hostUrl) {
|
|
89
|
+
saveToken(token, hostUrl);
|
|
90
|
+
}
|
|
91
|
+
function getAuthStatus(hostUrl) {
|
|
92
|
+
if (process.env.TE_TOKEN) {
|
|
93
|
+
return { authenticated: true, host: hostUrl, source: "env:TE_TOKEN" };
|
|
94
|
+
}
|
|
95
|
+
const cached = loadToken(hostUrl);
|
|
96
|
+
if (cached) {
|
|
97
|
+
const ageMs = Date.now() - new Date(cached.updatedAt).getTime();
|
|
98
|
+
const hours = Math.round(ageMs / 36e5);
|
|
99
|
+
return { authenticated: true, host: hostUrl, tokenAge: `${hours}h ago`, source: "cache" };
|
|
100
|
+
}
|
|
101
|
+
return { authenticated: false, host: hostUrl };
|
|
102
|
+
}
|
|
103
|
+
function extractTokenViaOsascript(hostUrl) {
|
|
104
|
+
if (process.platform !== "darwin") return { token: null, error: "not_mac" };
|
|
105
|
+
const hostname = extractHostname(hostUrl);
|
|
106
|
+
const lines = [
|
|
107
|
+
'tell application "Google Chrome"',
|
|
108
|
+
" repeat with w in windows",
|
|
109
|
+
" repeat with t in tabs of w",
|
|
110
|
+
` if URL of t contains "${hostname}" then`,
|
|
111
|
+
` return execute t javascript "localStorage.getItem('ACCESS_TOKEN')"`,
|
|
112
|
+
" end if",
|
|
113
|
+
" end repeat",
|
|
114
|
+
" end repeat",
|
|
115
|
+
' return "NO_TAB_FOUND"',
|
|
116
|
+
"end tell"
|
|
117
|
+
];
|
|
118
|
+
try {
|
|
119
|
+
const args = lines.flatMap((line) => ["-e", line]);
|
|
120
|
+
const result = execFileSync("osascript", args, { encoding: "utf8", timeout: 5e3 }).trim();
|
|
121
|
+
if (result === "NO_TAB_FOUND") return { token: null, error: "no_tab" };
|
|
122
|
+
if (!result || result === "missing value") return { token: null, error: "no_token" };
|
|
123
|
+
return { token: result.replace(/^["']|["']$/g, ""), error: null };
|
|
124
|
+
} catch (e) {
|
|
125
|
+
const msg = e.message || "";
|
|
126
|
+
if (msg.includes("not allowed") || msg.includes("assistive access") || msg.includes("(-1743)")) {
|
|
127
|
+
return { token: null, error: "no_js_permission" };
|
|
128
|
+
}
|
|
129
|
+
return { token: null, error: "no_tab" };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async function requestTokenViaOsascript(hostUrl) {
|
|
133
|
+
process.stderr.write(`[ae-cli] No TE tab found in Chrome. Opening ${hostUrl} ...
|
|
134
|
+
`);
|
|
135
|
+
process.stderr.write(`[ae-cli] Please login, then your token will be captured automatically.
|
|
136
|
+
`);
|
|
137
|
+
try {
|
|
138
|
+
const openScript = [
|
|
139
|
+
'tell application "Google Chrome"',
|
|
140
|
+
" activate",
|
|
141
|
+
" if (count of windows) > 0 then",
|
|
142
|
+
` make new tab at end of tabs of window 1 with properties {URL:"${hostUrl}"}`,
|
|
143
|
+
" else",
|
|
144
|
+
` open location "${hostUrl}"`,
|
|
145
|
+
" end if",
|
|
146
|
+
"end tell"
|
|
147
|
+
];
|
|
148
|
+
execFileSync("osascript", openScript.flatMap((line) => ["-e", line]), { timeout: 5e3 });
|
|
149
|
+
} catch {
|
|
150
|
+
process.stderr.write(`[ae-cli] Could not open browser. Please open ${hostUrl} in Chrome manually.
|
|
151
|
+
`);
|
|
152
|
+
}
|
|
153
|
+
const deadline = Date.now() + OSASCRIPT_POLL_TIMEOUT_MS;
|
|
154
|
+
while (Date.now() < deadline) {
|
|
155
|
+
await new Promise((r) => setTimeout(r, OSASCRIPT_POLL_INTERVAL_MS));
|
|
156
|
+
const { token, error } = extractTokenViaOsascript(hostUrl);
|
|
157
|
+
if (token) {
|
|
158
|
+
process.stderr.write(`[ae-cli] Token captured from Chrome for ${hostUrl}.
|
|
159
|
+
`);
|
|
160
|
+
return token;
|
|
161
|
+
}
|
|
162
|
+
if (error === "no_js_permission") return null;
|
|
163
|
+
}
|
|
164
|
+
process.stderr.write(`[ae-cli] Polling timed out after ${OSASCRIPT_POLL_TIMEOUT_MS / 1e3}s.
|
|
165
|
+
`);
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
async function getToken(hostUrl) {
|
|
169
|
+
if (!hostUrl) {
|
|
170
|
+
throw new Error(
|
|
171
|
+
`No TE host configured.
|
|
172
|
+
Run: ae-cli config set-host`
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
if (process.env.TE_TOKEN) {
|
|
176
|
+
return process.env.TE_TOKEN;
|
|
177
|
+
}
|
|
178
|
+
const cached = loadToken(hostUrl);
|
|
179
|
+
if (cached && cached.token) return cached.token;
|
|
180
|
+
const { token: osascriptToken, error } = extractTokenViaOsascript(hostUrl);
|
|
181
|
+
if (error === "no_js_permission") {
|
|
182
|
+
throw new Error(
|
|
183
|
+
`Chrome JavaScript from Apple Events is not enabled.
|
|
184
|
+
Enable it: Chrome menu \u2192 View \u2192 Developer \u2192 Allow JavaScript from Apple Events`
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
if (error === "not_mac") {
|
|
188
|
+
throw new Error(
|
|
189
|
+
`Auto token extraction is only available on macOS + Chrome.
|
|
190
|
+
Use: ae-cli auth set-token <token>`
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
if (osascriptToken) {
|
|
194
|
+
saveToken(osascriptToken, hostUrl);
|
|
195
|
+
return osascriptToken;
|
|
196
|
+
}
|
|
197
|
+
const polledToken = await requestTokenViaOsascript(hostUrl);
|
|
198
|
+
if (polledToken) {
|
|
199
|
+
saveToken(polledToken, hostUrl);
|
|
200
|
+
return polledToken;
|
|
201
|
+
}
|
|
202
|
+
throw new Error(
|
|
203
|
+
`Cannot obtain token for ${hostUrl}.
|
|
204
|
+
Options:
|
|
205
|
+
1. ae-cli auth login (macOS + Chrome)
|
|
206
|
+
2. ae-cli auth set-token <token>`
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export {
|
|
211
|
+
resolveHost,
|
|
212
|
+
loadToken,
|
|
213
|
+
saveToken,
|
|
214
|
+
clearToken,
|
|
215
|
+
setTokenManual,
|
|
216
|
+
getAuthStatus,
|
|
217
|
+
getToken
|
|
218
|
+
};
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getToken
|
|
3
|
+
} from "./chunk-3CDLNXTD.js";
|
|
4
|
+
import {
|
|
5
|
+
getActiveHost,
|
|
6
|
+
getConfigDir,
|
|
7
|
+
safeJsonParse,
|
|
8
|
+
safeReadJsonFile
|
|
9
|
+
} from "./chunk-EBFUVWIZ.js";
|
|
10
|
+
|
|
11
|
+
// src/core/mcp.ts
|
|
12
|
+
import fs from "fs";
|
|
13
|
+
import path from "path";
|
|
14
|
+
var MCP_PROTOCOL_VERSION = "2025-11-25";
|
|
15
|
+
var JSONRPC_VERSION = "2.0";
|
|
16
|
+
var serviceMappingMap = /* @__PURE__ */ new Map();
|
|
17
|
+
registerMcpMapping("analysis", {
|
|
18
|
+
componentName: "analysis",
|
|
19
|
+
mappingPath: "analysis"
|
|
20
|
+
});
|
|
21
|
+
registerMcpMapping("te_analysis_extend", {
|
|
22
|
+
componentName: "analysis",
|
|
23
|
+
mappingPath: "analysis-extend"
|
|
24
|
+
});
|
|
25
|
+
function registerMcpMapping(serviceName, mapping) {
|
|
26
|
+
serviceMappingMap.set(serviceName, mapping);
|
|
27
|
+
}
|
|
28
|
+
function registerMcpMappings(mappings) {
|
|
29
|
+
for (const [serviceName, mapping] of Object.entries(mappings)) {
|
|
30
|
+
serviceMappingMap.set(serviceName, mapping);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function getMcpMapping(serviceName) {
|
|
34
|
+
const mapping = serviceMappingMap.get(serviceName);
|
|
35
|
+
if (!mapping) {
|
|
36
|
+
throw new Error(`MCP mapping not found for service: '${serviceName}'. Please register it using registerMcpMapping() first.`);
|
|
37
|
+
}
|
|
38
|
+
return mapping;
|
|
39
|
+
}
|
|
40
|
+
function buildMcpUrl(host, serviceName) {
|
|
41
|
+
const mapping = getMcpMapping(serviceName);
|
|
42
|
+
const base = host.replace(/\/+$/, "");
|
|
43
|
+
return `${base}/mcp/${mapping.componentName}/http/${mapping.mappingPath}`;
|
|
44
|
+
}
|
|
45
|
+
function resolveMcpUrl(mcpUrlOverride, host, serviceName) {
|
|
46
|
+
if (mcpUrlOverride) {
|
|
47
|
+
return mcpUrlOverride;
|
|
48
|
+
}
|
|
49
|
+
return buildMcpUrl(host, serviceName);
|
|
50
|
+
}
|
|
51
|
+
var requestIdCounter = 1;
|
|
52
|
+
function genRequestId() {
|
|
53
|
+
return requestIdCounter++;
|
|
54
|
+
}
|
|
55
|
+
var MCP_TOKEN_FILE = path.join(getConfigDir(), "mcp-tokens.json");
|
|
56
|
+
var MCP_TOKEN_GENERATE_PATH = "/v1/ta/mcp/token/generate";
|
|
57
|
+
function loadMcpTokenStore() {
|
|
58
|
+
try {
|
|
59
|
+
if (fs.existsSync(MCP_TOKEN_FILE)) {
|
|
60
|
+
return safeReadJsonFile(MCP_TOKEN_FILE);
|
|
61
|
+
}
|
|
62
|
+
} catch {
|
|
63
|
+
}
|
|
64
|
+
return {};
|
|
65
|
+
}
|
|
66
|
+
function saveMcpTokenStore(store) {
|
|
67
|
+
const dir = getConfigDir();
|
|
68
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
69
|
+
fs.writeFileSync(MCP_TOKEN_FILE, JSON.stringify(store, null, 2));
|
|
70
|
+
}
|
|
71
|
+
async function generateMcpToken(hostUrl) {
|
|
72
|
+
const teToken = await getToken(hostUrl);
|
|
73
|
+
const base = hostUrl.replace(/\/+$/, "");
|
|
74
|
+
const url = `${base}${MCP_TOKEN_GENERATE_PATH}`;
|
|
75
|
+
const resp = await fetch(url, {
|
|
76
|
+
method: "GET",
|
|
77
|
+
headers: {
|
|
78
|
+
"Authorization": `bearer ${teToken}`
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
if (!resp.ok) {
|
|
82
|
+
throw new Error(`MCP token generate HTTP error: ${resp.status} ${resp.statusText}`);
|
|
83
|
+
}
|
|
84
|
+
const data = safeJsonParse(await resp.text());
|
|
85
|
+
if (data.return_code !== 0) {
|
|
86
|
+
throw new Error(`MCP token generate error: ${data.return_message || "unknown"} (code: ${data.return_code})`);
|
|
87
|
+
}
|
|
88
|
+
const mcpToken = data.data?.userSecret;
|
|
89
|
+
if (!mcpToken) {
|
|
90
|
+
throw new Error("MCP token generate error: empty userSecret in response");
|
|
91
|
+
}
|
|
92
|
+
return mcpToken;
|
|
93
|
+
}
|
|
94
|
+
async function getMcpToken(hostOverride) {
|
|
95
|
+
const hostUrl = hostOverride || getActiveHost();
|
|
96
|
+
const store = loadMcpTokenStore();
|
|
97
|
+
if (store[hostUrl]) {
|
|
98
|
+
return store[hostUrl];
|
|
99
|
+
}
|
|
100
|
+
const mcpToken = await generateMcpToken(hostUrl);
|
|
101
|
+
const updatedStore = { ...store, [hostUrl]: mcpToken };
|
|
102
|
+
saveMcpTokenStore(updatedStore);
|
|
103
|
+
return mcpToken;
|
|
104
|
+
}
|
|
105
|
+
async function mcpRequest(url, method, params = {}, hostOverride) {
|
|
106
|
+
const token = await getMcpToken(hostOverride);
|
|
107
|
+
const requestId = genRequestId();
|
|
108
|
+
const paramsWithMeta = {
|
|
109
|
+
...params,
|
|
110
|
+
_meta: { progressToken: requestId }
|
|
111
|
+
};
|
|
112
|
+
const body = {
|
|
113
|
+
jsonrpc: JSONRPC_VERSION,
|
|
114
|
+
id: requestId,
|
|
115
|
+
method,
|
|
116
|
+
params: paramsWithMeta
|
|
117
|
+
};
|
|
118
|
+
const headers = {
|
|
119
|
+
"Content-Type": "application/json",
|
|
120
|
+
"Accept": "application/json, text/event-stream",
|
|
121
|
+
"mcp-protocol-version": MCP_PROTOCOL_VERSION,
|
|
122
|
+
"mcp-token": token
|
|
123
|
+
};
|
|
124
|
+
const resp = await fetch(url, {
|
|
125
|
+
method: "POST",
|
|
126
|
+
headers,
|
|
127
|
+
body: JSON.stringify(body)
|
|
128
|
+
});
|
|
129
|
+
if (!resp.ok) {
|
|
130
|
+
throw new Error(`MCP HTTP error: ${resp.status} ${resp.statusText}`);
|
|
131
|
+
}
|
|
132
|
+
const data = safeJsonParse(await resp.text());
|
|
133
|
+
if (data.error) {
|
|
134
|
+
throw new Error(`MCP error: ${data.error.message || JSON.stringify(data.error)}`);
|
|
135
|
+
}
|
|
136
|
+
return data.result;
|
|
137
|
+
}
|
|
138
|
+
async function callMcpTool(url, toolName, args = {}, hostOverride) {
|
|
139
|
+
const params = {
|
|
140
|
+
name: toolName,
|
|
141
|
+
arguments: args
|
|
142
|
+
};
|
|
143
|
+
const result = await mcpRequest(url, "tools/call", params, hostOverride);
|
|
144
|
+
return {
|
|
145
|
+
content: result.content.map((item) => {
|
|
146
|
+
if (item.type === "text") {
|
|
147
|
+
return { type: "text", text: item.text };
|
|
148
|
+
} else if (item.type === "image") {
|
|
149
|
+
return { type: "image", data: item.data, mimeType: item.mimeType };
|
|
150
|
+
} else {
|
|
151
|
+
return { type: "resource", ...item };
|
|
152
|
+
}
|
|
153
|
+
}),
|
|
154
|
+
isError: result.isError
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function parseMcpResult(result) {
|
|
158
|
+
if (result.content.length > 0 && result.content[0].type === "text") {
|
|
159
|
+
const text = result.content[0].text || "";
|
|
160
|
+
try {
|
|
161
|
+
return safeJsonParse(text);
|
|
162
|
+
} catch {
|
|
163
|
+
return text;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return result;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export {
|
|
170
|
+
registerMcpMappings,
|
|
171
|
+
resolveMcpUrl,
|
|
172
|
+
callMcpTool,
|
|
173
|
+
parseMcpResult
|
|
174
|
+
};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// src/core/json-utils.ts
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import { createRequire } from "module";
|
|
4
|
+
var require2 = createRequire(import.meta.url);
|
|
5
|
+
var JSONbig = require2("json-bigint");
|
|
6
|
+
var jsonParser = JSONbig({ storeAsString: true });
|
|
7
|
+
function safeJsonParse(text) {
|
|
8
|
+
return jsonParser.parse(text);
|
|
9
|
+
}
|
|
10
|
+
function safeReadJsonFile(filePath) {
|
|
11
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
12
|
+
return safeJsonParse(content);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// src/core/config.ts
|
|
16
|
+
import fs2 from "fs";
|
|
17
|
+
import path from "path";
|
|
18
|
+
var CONFIG_DIR = path.join(process.env.HOME || "", ".ae-cli");
|
|
19
|
+
var CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
|
|
20
|
+
function ensureDir() {
|
|
21
|
+
if (!fs2.existsSync(CONFIG_DIR)) fs2.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
function loadConfig() {
|
|
24
|
+
try {
|
|
25
|
+
if (fs2.existsSync(CONFIG_FILE)) {
|
|
26
|
+
const raw = safeReadJsonFile(CONFIG_FILE);
|
|
27
|
+
if (raw.defaultHost && !raw.activeHost) {
|
|
28
|
+
const migrated = migrateConfig(raw);
|
|
29
|
+
saveConfig(migrated);
|
|
30
|
+
return migrated;
|
|
31
|
+
}
|
|
32
|
+
return raw;
|
|
33
|
+
}
|
|
34
|
+
} catch (err) {
|
|
35
|
+
console.error(`Error loading config: ${err.message}`);
|
|
36
|
+
}
|
|
37
|
+
return { activeHost: "", hosts: {} };
|
|
38
|
+
}
|
|
39
|
+
function migrateConfig(old) {
|
|
40
|
+
const hosts = {};
|
|
41
|
+
const oldHost = old.defaultHost;
|
|
42
|
+
if (old.hosts) {
|
|
43
|
+
for (const [key, val] of Object.entries(old.hosts)) {
|
|
44
|
+
const url = key.startsWith("http") ? key : `https://${key}`;
|
|
45
|
+
hosts[url] = val;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const activeHost = oldHost.startsWith("http") ? oldHost : `https://${oldHost}`;
|
|
49
|
+
if (!hosts[activeHost]) {
|
|
50
|
+
hosts[activeHost] = { label: "default" };
|
|
51
|
+
}
|
|
52
|
+
return { activeHost, hosts };
|
|
53
|
+
}
|
|
54
|
+
function saveConfig(config) {
|
|
55
|
+
ensureDir();
|
|
56
|
+
fs2.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
57
|
+
}
|
|
58
|
+
function getActiveHost() {
|
|
59
|
+
const config = loadConfig();
|
|
60
|
+
return config.activeHost;
|
|
61
|
+
}
|
|
62
|
+
function setActiveHost(url) {
|
|
63
|
+
const config = loadConfig();
|
|
64
|
+
if (!config.hosts[url]) {
|
|
65
|
+
throw new Error(`Host ${url} is not in the configured hosts list. Add it first.`);
|
|
66
|
+
}
|
|
67
|
+
config.activeHost = url;
|
|
68
|
+
saveConfig(config);
|
|
69
|
+
}
|
|
70
|
+
function addHost(url, label) {
|
|
71
|
+
const config = loadConfig();
|
|
72
|
+
config.hosts[url] = { label };
|
|
73
|
+
if (!config.activeHost) {
|
|
74
|
+
config.activeHost = url;
|
|
75
|
+
}
|
|
76
|
+
saveConfig(config);
|
|
77
|
+
}
|
|
78
|
+
function updateHostLabel(url, label) {
|
|
79
|
+
const config = loadConfig();
|
|
80
|
+
if (config.hosts[url]) {
|
|
81
|
+
config.hosts[url].label = label;
|
|
82
|
+
saveConfig(config);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function removeHost(url) {
|
|
86
|
+
const config = loadConfig();
|
|
87
|
+
delete config.hosts[url];
|
|
88
|
+
if (config.activeHost === url) {
|
|
89
|
+
const remaining = Object.keys(config.hosts);
|
|
90
|
+
config.activeHost = remaining.length > 0 ? remaining[0] : "";
|
|
91
|
+
}
|
|
92
|
+
saveConfig(config);
|
|
93
|
+
}
|
|
94
|
+
function listHosts() {
|
|
95
|
+
const config = loadConfig();
|
|
96
|
+
return Object.entries(config.hosts).map(([url, entry]) => ({
|
|
97
|
+
url,
|
|
98
|
+
label: entry.label,
|
|
99
|
+
active: url === config.activeHost
|
|
100
|
+
}));
|
|
101
|
+
}
|
|
102
|
+
function getConfigDir() {
|
|
103
|
+
return CONFIG_DIR;
|
|
104
|
+
}
|
|
105
|
+
function extractHostname(fullUrl) {
|
|
106
|
+
try {
|
|
107
|
+
const u = new URL(fullUrl);
|
|
108
|
+
return u.host;
|
|
109
|
+
} catch {
|
|
110
|
+
return fullUrl;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export {
|
|
115
|
+
safeJsonParse,
|
|
116
|
+
safeReadJsonFile,
|
|
117
|
+
loadConfig,
|
|
118
|
+
saveConfig,
|
|
119
|
+
getActiveHost,
|
|
120
|
+
setActiveHost,
|
|
121
|
+
addHost,
|
|
122
|
+
updateHostLabel,
|
|
123
|
+
removeHost,
|
|
124
|
+
listHosts,
|
|
125
|
+
getConfigDir,
|
|
126
|
+
extractHostname
|
|
127
|
+
};
|