@zhoujinandrew/te-cli 1.0.0

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 (72) hide show
  1. package/README.md +109 -0
  2. package/bin/te-cli.js +2 -0
  3. package/dist/analysis-SFPXKD3E.js +218 -0
  4. package/dist/audience-AZ72TU5F.js +119 -0
  5. package/dist/auth-2OVPSXHK.js +19 -0
  6. package/dist/auth-6QCDAUYV.js +67 -0
  7. package/dist/auth-CFVQVVQN.js +51 -0
  8. package/dist/auth-QNSRS5ZR.js +19 -0
  9. package/dist/auth-V4MDYFHU.js +67 -0
  10. package/dist/auth-Z652VWDK.js +19 -0
  11. package/dist/chunk-4YPCK7T5.js +217 -0
  12. package/dist/chunk-C3VJLU5Y.js +206 -0
  13. package/dist/chunk-CFCHSAMQ.js +108 -0
  14. package/dist/chunk-KA66K7EL.js +144 -0
  15. package/dist/chunk-KAWQCMDL.js +44 -0
  16. package/dist/chunk-KM57HI5B.js +107 -0
  17. package/dist/chunk-KNKRSOCY.js +144 -0
  18. package/dist/chunk-P7NQZGSZ.js +217 -0
  19. package/dist/chunk-STTYG7WN.js +91 -0
  20. package/dist/chunk-V3D6XL5Z.js +134 -0
  21. package/dist/client-CSJ3XBC4.js +16 -0
  22. package/dist/client-EZSKQFIH.js +16 -0
  23. package/dist/client-OT7PTMI2.js +16 -0
  24. package/dist/config-53KUB7OG.js +146 -0
  25. package/dist/config-QQE7LMPZ.js +40 -0
  26. package/dist/config-S53L4MQK.js +242 -0
  27. package/dist/index.js +239 -0
  28. package/dist/meta-ZXBX5RER.js +135 -0
  29. package/dist/operation-6POPVVIZ.js +214 -0
  30. package/dist/raw-76GSW4Q4.js +59 -0
  31. package/dist/raw-K2FR7QAI.js +55 -0
  32. package/dist/raw-YVFTDHSI.js +59 -0
  33. package/package.json +40 -0
  34. package/skills/te-analysis/SKILL.md +93 -0
  35. package/skills/te-analysis/references/create-dashboard.md +30 -0
  36. package/skills/te-analysis/references/get-dashboard.md +24 -0
  37. package/skills/te-analysis/references/get-report.md +24 -0
  38. package/skills/te-analysis/references/list-dashboard-reports.md +24 -0
  39. package/skills/te-analysis/references/list-dashboards.md +23 -0
  40. package/skills/te-analysis/references/list-reports.md +26 -0
  41. package/skills/te-analysis/references/query-report-data.md +34 -0
  42. package/skills/te-analysis/references/query-sql.md +31 -0
  43. package/skills/te-analysis/references/save-report.md +42 -0
  44. package/skills/te-analysis/references/update-dashboard.md +33 -0
  45. package/skills/te-audience/SKILL.md +60 -0
  46. package/skills/te-audience/references/te-audience-get-tag.md +21 -0
  47. package/skills/te-audience/references/te-audience-list-audience-events.md +20 -0
  48. package/skills/te-audience/references/te-audience-list-clusters.md +20 -0
  49. package/skills/te-audience/references/te-audience-list-tags.md +23 -0
  50. package/skills/te-audience/references/te-audience-load-audience-props.md +25 -0
  51. package/skills/te-audience/references/te-audience-predict-cluster-count.md +22 -0
  52. package/skills/te-meta/SKILL.md +80 -0
  53. package/skills/te-meta/references/get-table-columns.md +29 -0
  54. package/skills/te-meta/references/list-entities.md +26 -0
  55. package/skills/te-meta/references/list-events.md +29 -0
  56. package/skills/te-meta/references/list-metrics.md +26 -0
  57. package/skills/te-meta/references/list-tables.md +26 -0
  58. package/skills/te-meta/references/load-event-props.md +27 -0
  59. package/skills/te-meta/references/load-measure-props.md +24 -0
  60. package/skills/te-operation/SKILL.md +75 -0
  61. package/skills/te-operation/references/te-operation-create-task.md +29 -0
  62. package/skills/te-operation/references/te-operation-get-channel.md +20 -0
  63. package/skills/te-operation/references/te-operation-get-flow.md +20 -0
  64. package/skills/te-operation/references/te-operation-get-space-tree.md +19 -0
  65. package/skills/te-operation/references/te-operation-get-task-stats.md +20 -0
  66. package/skills/te-operation/references/te-operation-get-timezone.md +19 -0
  67. package/skills/te-operation/references/te-operation-list-channels.md +19 -0
  68. package/skills/te-operation/references/te-operation-list-flows.md +19 -0
  69. package/skills/te-operation/references/te-operation-list-mark-times.md +19 -0
  70. package/skills/te-operation/references/te-operation-list-tasks.md +25 -0
  71. package/skills/te-operation/references/te-operation-save-flow.md +29 -0
  72. package/skills/te-shared/SKILL.md +115 -0
@@ -0,0 +1,134 @@
1
+ import {
2
+ clearToken,
3
+ getToken,
4
+ resolveHost
5
+ } from "./chunk-C3VJLU5Y.js";
6
+
7
+ // src/core/client.ts
8
+ import WebSocket from "ws";
9
+ import { randomBytes } from "crypto";
10
+ function genRequestId(prefix) {
11
+ const rand = randomBytes(4).toString("base64url").slice(0, 8);
12
+ return `${prefix}@@${rand}`;
13
+ }
14
+ async function request(method, modulePath, params = {}, body = null, retry = true, host) {
15
+ const resolvedHost = resolveHost(host);
16
+ const token = await getToken(resolvedHost);
17
+ const url = new URL(`https://${resolvedHost}${modulePath}`);
18
+ for (const [k, v] of Object.entries(params)) {
19
+ if (v !== void 0 && v !== null) url.searchParams.set(k, String(v));
20
+ }
21
+ const headers = {
22
+ "Authorization": `bearer ${token}`,
23
+ "Content-Type": "application/json"
24
+ };
25
+ const options = { method, headers };
26
+ if (body && method !== "GET") options.body = JSON.stringify(body);
27
+ const resp = await fetch(url.toString(), options);
28
+ if ((resp.status === 401 || resp.status === 403) && retry) {
29
+ clearToken(resolvedHost);
30
+ return request(method, modulePath, params, body, false, resolvedHost);
31
+ }
32
+ const data = await resp.json();
33
+ if (data.return_code === -1001 && retry) {
34
+ clearToken(resolvedHost);
35
+ return request(method, modulePath, params, body, false, resolvedHost);
36
+ }
37
+ if (data.return_code !== 0 && data.return_code !== void 0) {
38
+ throw new Error(`TE API error: ${data.return_message || "unknown"} (code: ${data.return_code})`);
39
+ }
40
+ return data.data !== void 0 ? data.data : data;
41
+ }
42
+ async function httpGet(modulePath, params = {}, host) {
43
+ return request("GET", modulePath, params, null, true, host);
44
+ }
45
+ async function httpPost(modulePath, params = {}, body, host) {
46
+ return request("POST", modulePath, params, body ?? {}, true, host);
47
+ }
48
+ async function wsQueryOnce(projectId, requestId, qp, eventModel, options = {}, token, host, timeoutMs = 3e4) {
49
+ const resolvedHost = resolveHost(host);
50
+ const wsUrl = `wss://${resolvedHost}/v1/ta-websocket/query/${token}`;
51
+ return new Promise((resolve, reject) => {
52
+ const ws = new WebSocket(wsUrl);
53
+ const timer = setTimeout(() => {
54
+ ws.close();
55
+ reject(new Error(`WebSocket query timed out after ${timeoutMs}ms`));
56
+ }, timeoutMs);
57
+ ws.on("open", () => {
58
+ const payload = {
59
+ requestId,
60
+ projectId,
61
+ eventModel,
62
+ qp: typeof qp === "string" ? qp : JSON.stringify(qp),
63
+ searchSource: options.searchSource || "reportQuery",
64
+ querySource: options.querySource || "single_report",
65
+ contentTranslate: options.contentTranslate !== false,
66
+ ...options.extra || {}
67
+ };
68
+ ws.send(JSON.stringify(["data", payload, { channel: "ta" }]));
69
+ });
70
+ ws.on("message", (raw) => {
71
+ try {
72
+ const msg = JSON.parse(raw.toString());
73
+ if (Array.isArray(msg) && msg.length >= 2) {
74
+ const data = msg[1];
75
+ if (data.requestId === requestId && data.progress === 100) {
76
+ clearTimeout(timer);
77
+ ws.close();
78
+ resolve(data);
79
+ }
80
+ }
81
+ } catch {
82
+ }
83
+ });
84
+ ws.on("error", (err) => {
85
+ clearTimeout(timer);
86
+ reject(err);
87
+ });
88
+ ws.on("close", () => {
89
+ clearTimeout(timer);
90
+ });
91
+ });
92
+ }
93
+ async function wsQuery(projectId, requestId, qp, eventModel, options = {}, host) {
94
+ const resolvedHost = resolveHost(host);
95
+ const token = await getToken(resolvedHost);
96
+ try {
97
+ return await wsQueryOnce(projectId, requestId, qp, eventModel, options, token, resolvedHost);
98
+ } catch (err) {
99
+ const msg = err.message || "";
100
+ if (msg.includes("401") || msg.includes("403") || msg.includes("auth")) {
101
+ clearToken(resolvedHost);
102
+ const newToken = await getToken(resolvedHost);
103
+ return wsQueryOnce(projectId, requestId, qp, eventModel, options, newToken, resolvedHost);
104
+ }
105
+ throw err;
106
+ }
107
+ }
108
+ async function querySql(projectId, sql, host) {
109
+ const requestId = genRequestId("sqlIde");
110
+ const qp = {
111
+ events: { sql },
112
+ eventView: { sqlViewParams: [] }
113
+ };
114
+ return wsQuery(projectId, requestId, qp, 10, {
115
+ searchSource: "sqlIde",
116
+ querySource: "sqlIde"
117
+ }, host);
118
+ }
119
+ async function queryReportData(projectId, reportId, qp, eventModel, options = {}, host) {
120
+ const requestId = genRequestId("reportQuery");
121
+ return wsQuery(projectId, requestId, qp, eventModel, {
122
+ searchSource: "reportQuery",
123
+ querySource: "single_report",
124
+ ...options
125
+ }, host);
126
+ }
127
+
128
+ export {
129
+ httpGet,
130
+ httpPost,
131
+ wsQuery,
132
+ querySql,
133
+ queryReportData
134
+ };
@@ -0,0 +1,16 @@
1
+ import {
2
+ httpGet,
3
+ httpPost,
4
+ queryReportData,
5
+ querySql,
6
+ wsQuery
7
+ } from "./chunk-V3D6XL5Z.js";
8
+ import "./chunk-C3VJLU5Y.js";
9
+ import "./chunk-KAWQCMDL.js";
10
+ export {
11
+ httpGet,
12
+ httpPost,
13
+ queryReportData,
14
+ querySql,
15
+ wsQuery
16
+ };
@@ -0,0 +1,16 @@
1
+ import {
2
+ httpGet,
3
+ httpPost,
4
+ queryReportData,
5
+ querySql,
6
+ wsQuery
7
+ } from "./chunk-KNKRSOCY.js";
8
+ import "./chunk-P7NQZGSZ.js";
9
+ import "./chunk-CFCHSAMQ.js";
10
+ export {
11
+ httpGet,
12
+ httpPost,
13
+ queryReportData,
14
+ querySql,
15
+ wsQuery
16
+ };
@@ -0,0 +1,16 @@
1
+ import {
2
+ httpGet,
3
+ httpPost,
4
+ queryReportData,
5
+ querySql,
6
+ wsQuery
7
+ } from "./chunk-KA66K7EL.js";
8
+ import "./chunk-4YPCK7T5.js";
9
+ import "./chunk-STTYG7WN.js";
10
+ export {
11
+ httpGet,
12
+ httpPost,
13
+ queryReportData,
14
+ querySql,
15
+ wsQuery
16
+ };
@@ -0,0 +1,146 @@
1
+ import {
2
+ printOutput
3
+ } from "./chunk-KM57HI5B.js";
4
+ import {
5
+ addHost,
6
+ getConfigDir,
7
+ listHosts,
8
+ loadConfig,
9
+ setActiveHost
10
+ } from "./chunk-STTYG7WN.js";
11
+
12
+ // src/commands/config.ts
13
+ import * as readline from "readline";
14
+ function registerConfig(program) {
15
+ const config = program.command("config").description("Configuration management");
16
+ config.command("init").description("Initialize te-cli configuration with a TE host").action(async () => {
17
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
18
+ const ask = (q, def) => new Promise((resolve) => rl.question(`${q} [${def}]: `, (a) => resolve(a || def)));
19
+ const url = await ask("TE host URL", "https://ta.thinkingdata.cn");
20
+ const label = await ask("Label for this host", "default");
21
+ rl.close();
22
+ const normalized = normalizeUrl(url);
23
+ addHost(normalized, label);
24
+ setActiveHost(normalized);
25
+ process.stderr.write(`[te-cli] Config saved. Active host: ${normalized}
26
+ `);
27
+ printOutput(loadConfig(), program.opts().format || "json");
28
+ });
29
+ config.command("show").description("Show current configuration").action(() => {
30
+ const cfg = loadConfig();
31
+ const hosts = listHosts();
32
+ printOutput({ configDir: getConfigDir(), activeHost: cfg.activeHost, hosts }, program.opts().format || "json");
33
+ });
34
+ config.command("add-host").description("Add a new TE host").argument("<url>", "Full URL (e.g., https://ta.thinkingdata.cn:8080)").option("--label <label>", "Label for this host", "default").action((url, opts) => {
35
+ const normalized = normalizeUrl(url);
36
+ addHost(normalized, opts.label);
37
+ process.stderr.write(`[te-cli] Host added: ${normalized} (${opts.label})
38
+ `);
39
+ printOutput({ added: normalized, label: opts.label }, program.opts().format || "json");
40
+ });
41
+ config.command("set-host").description("Switch active TE host (interactive selector)").action(async () => {
42
+ const hosts = listHosts();
43
+ if (hosts.length === 0) {
44
+ process.stderr.write("[te-cli] No hosts configured. Run: te-cli config add-host <url>\n");
45
+ process.exit(1);
46
+ }
47
+ const options = [
48
+ ...hosts.map((h) => ({
49
+ label: `${h.active ? "\u25CF " : " "}${h.url} (${h.label})`,
50
+ value: h.url,
51
+ isNew: false
52
+ })),
53
+ { label: " + Add new host...", value: "__new__", isNew: true }
54
+ ];
55
+ const activeIdx = hosts.findIndex((h) => h.active);
56
+ const selected = await interactiveSelect(options.map((o) => o.label), activeIdx >= 0 ? activeIdx : 0);
57
+ if (selected === -1) {
58
+ process.stderr.write("Cancelled.\n");
59
+ return;
60
+ }
61
+ const chosen = options[selected];
62
+ if (chosen.isNew) {
63
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
64
+ const ask = (q) => new Promise((resolve) => rl.question(q, (a) => resolve(a.trim())));
65
+ const url = await ask("TE host URL: ");
66
+ const label = await ask("Label: ");
67
+ rl.close();
68
+ if (!url) {
69
+ process.stderr.write("Cancelled.\n");
70
+ return;
71
+ }
72
+ const normalized = normalizeUrl(url);
73
+ addHost(normalized, label || "default");
74
+ setActiveHost(normalized);
75
+ process.stderr.write(`[te-cli] Added and switched to: ${normalized}
76
+ `);
77
+ } else {
78
+ setActiveHost(chosen.value);
79
+ process.stderr.write(`[te-cli] Switched to: ${chosen.value}
80
+ `);
81
+ }
82
+ printOutput(loadConfig(), program.opts().format || "json");
83
+ });
84
+ }
85
+ function normalizeUrl(url) {
86
+ if (!url.startsWith("http://") && !url.startsWith("https://")) {
87
+ url = `https://${url}`;
88
+ }
89
+ return url.replace(/\/$/, "");
90
+ }
91
+ function interactiveSelect(items, initialIndex) {
92
+ return new Promise((resolve) => {
93
+ let cursor = initialIndex;
94
+ const stdin = process.stdin;
95
+ const stderr = process.stderr;
96
+ stderr.write("\x1B[?25l");
97
+ function render() {
98
+ if (renderCount > 0) {
99
+ stderr.write(`\x1B[${items.length}A`);
100
+ }
101
+ for (let i = 0; i < items.length; i++) {
102
+ const prefix = i === cursor ? "\x1B[36m\u276F\x1B[0m" : " ";
103
+ const text = i === cursor ? `\x1B[1m${items[i]}\x1B[0m` : items[i];
104
+ stderr.write(`${prefix} ${text}\x1B[K
105
+ `);
106
+ }
107
+ renderCount++;
108
+ }
109
+ let renderCount = 0;
110
+ stderr.write("Use \u2191\u2193 arrows to select, Enter to confirm, Esc to cancel:\n");
111
+ render();
112
+ if (!stdin.isTTY) {
113
+ stderr.write("\x1B[?25h");
114
+ resolve(initialIndex);
115
+ return;
116
+ }
117
+ stdin.setRawMode(true);
118
+ stdin.resume();
119
+ stdin.setEncoding("utf8");
120
+ function cleanup() {
121
+ stdin.setRawMode(false);
122
+ stdin.pause();
123
+ stdin.removeListener("data", onData);
124
+ stderr.write("\x1B[?25h");
125
+ }
126
+ function onData(key) {
127
+ if (key === "\x1B[A" || key === "k") {
128
+ cursor = (cursor - 1 + items.length) % items.length;
129
+ render();
130
+ } else if (key === "\x1B[B" || key === "j") {
131
+ cursor = (cursor + 1) % items.length;
132
+ render();
133
+ } else if (key === "\r" || key === "\n") {
134
+ cleanup();
135
+ resolve(cursor);
136
+ } else if (key === "\x1B" || key === "") {
137
+ cleanup();
138
+ resolve(-1);
139
+ }
140
+ }
141
+ stdin.on("data", onData);
142
+ });
143
+ }
144
+ export {
145
+ registerConfig
146
+ };
@@ -0,0 +1,40 @@
1
+ import {
2
+ printOutput
3
+ } from "./chunk-KM57HI5B.js";
4
+ import {
5
+ getConfigDir,
6
+ loadConfig,
7
+ saveConfig,
8
+ setConfigValue
9
+ } from "./chunk-KAWQCMDL.js";
10
+
11
+ // src/commands/config.ts
12
+ import * as readline from "readline";
13
+ function registerConfig(program) {
14
+ const config = program.command("config").description("Configuration management");
15
+ config.command("init").description("Initialize te-cli configuration").action(async () => {
16
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
17
+ const ask = (q, def) => new Promise((resolve) => rl.question(`${q} [${def}]: `, (a) => resolve(a || def)));
18
+ const host = await ask("Default TE host", "ta.thinkingdata.cn");
19
+ const label = await ask("Label for this host", "default");
20
+ rl.close();
21
+ const cfg = { defaultHost: host, hosts: { [host]: { label } } };
22
+ saveConfig(cfg);
23
+ process.stderr.write(`[te-cli] Config saved to ${getConfigDir()}/config.json
24
+ `);
25
+ printOutput(cfg, program.opts().format || "json");
26
+ });
27
+ config.command("show").description("Show current configuration").action(() => {
28
+ const cfg = loadConfig();
29
+ printOutput({ configDir: getConfigDir(), ...cfg }, program.opts().format || "json");
30
+ });
31
+ config.command("set <key> <value>").description("Set a configuration value (e.g., defaultHost)").action((key, value) => {
32
+ setConfigValue(key, value);
33
+ process.stderr.write(`[te-cli] Set ${key} = ${value}
34
+ `);
35
+ printOutput({ [key]: value }, program.opts().format || "json");
36
+ });
37
+ }
38
+ export {
39
+ registerConfig
40
+ };
@@ -0,0 +1,242 @@
1
+ import {
2
+ getAuthStatus,
3
+ getToken
4
+ } from "./chunk-P7NQZGSZ.js";
5
+ import {
6
+ addHost,
7
+ listHosts,
8
+ removeHost,
9
+ setActiveHost,
10
+ updateHostLabel
11
+ } from "./chunk-CFCHSAMQ.js";
12
+
13
+ // src/commands/config.ts
14
+ import * as readline from "readline";
15
+ function registerConfig(program) {
16
+ program.command("config").description("Interactive TE host configuration manager").action(async () => {
17
+ await runConfigUI(program);
18
+ });
19
+ }
20
+ async function runConfigUI(program) {
21
+ const hosts = listHosts();
22
+ if (hosts.length === 0) {
23
+ process.stderr.write("\x1B[1m[te-cli] No TE hosts configured. Let's add one.\x1B[0m\n\n");
24
+ const newHost = await promptNewHost();
25
+ if (!newHost) {
26
+ process.stderr.write("Cancelled.\n");
27
+ return;
28
+ }
29
+ addHost(newHost.url, newHost.label);
30
+ setActiveHost(newHost.url);
31
+ process.stderr.write(`
32
+ \x1B[32m\u2713\x1B[0m Added and activated: \x1B[1m${newHost.label}\x1B[0m ${newHost.url}
33
+ `);
34
+ await ensureAuth(newHost.url);
35
+ return;
36
+ }
37
+ await hostManagerLoop(program);
38
+ }
39
+ async function hostManagerLoop(program) {
40
+ while (true) {
41
+ const hosts = listHosts();
42
+ const items = hosts.map((h) => ({
43
+ display: formatHostLine(h),
44
+ type: "host",
45
+ url: h.url,
46
+ label: h.label,
47
+ active: h.active
48
+ }));
49
+ items.push({ display: "\x1B[2m + Add new host...\x1B[0m", type: "add", url: "", label: "", active: false });
50
+ const activeIdx = items.findIndex((i) => i.active);
51
+ 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");
52
+ const result = await interactiveHostSelect(items, activeIdx >= 0 ? activeIdx : 0);
53
+ if (result.action === "quit" || result.action === "cancel") {
54
+ break;
55
+ }
56
+ const item = items[result.index];
57
+ if (result.action === "select") {
58
+ if (item.type === "add") {
59
+ await handleAddHost();
60
+ } else {
61
+ setActiveHost(item.url);
62
+ process.stderr.write(`
63
+ \x1B[32m\u2713\x1B[0m Switched to: \x1B[1m${item.label}\x1B[0m ${item.url}
64
+ `);
65
+ await ensureAuth(item.url);
66
+ break;
67
+ }
68
+ } else if (result.action === "edit" && item.type === "host") {
69
+ await handleEditLabel(item.url, item.label);
70
+ } else if (result.action === "delete" && item.type === "host") {
71
+ await handleDeleteHost(item.url, item.label, item.active);
72
+ } else if (result.action === "add") {
73
+ await handleAddHost();
74
+ }
75
+ }
76
+ }
77
+ function formatHostLine(h) {
78
+ const marker = h.active ? "\x1B[32m\u25CF\x1B[0m" : "\x1B[2m\u25CB\x1B[0m";
79
+ const labelText = h.active ? `\x1B[1;32m${h.label}\x1B[0m` : `\x1B[1m${h.label}\x1B[0m`;
80
+ const authInfo = getAuthStatus(h.url);
81
+ const authBadge = authInfo.authenticated ? `\x1B[32m\u2713\x1B[0m` : `\x1B[31m\u2717\x1B[0m`;
82
+ return `${marker} ${labelText} ${h.url} ${authBadge}`;
83
+ }
84
+ async function handleAddHost() {
85
+ const newHost = await promptNewHost();
86
+ if (!newHost) {
87
+ process.stderr.write("Cancelled.\n");
88
+ return;
89
+ }
90
+ addHost(newHost.url, newHost.label);
91
+ setActiveHost(newHost.url);
92
+ process.stderr.write(`
93
+ \x1B[32m\u2713\x1B[0m Added and activated: \x1B[1m${newHost.label}\x1B[0m ${newHost.url}
94
+ `);
95
+ await ensureAuth(newHost.url);
96
+ }
97
+ async function handleEditLabel(url, currentLabel) {
98
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
99
+ const newLabel = await new Promise(
100
+ (resolve) => rl.question(`
101
+ New label [${currentLabel}]: `, (a) => {
102
+ rl.close();
103
+ resolve(a.trim() || currentLabel);
104
+ })
105
+ );
106
+ if (newLabel !== currentLabel) {
107
+ updateHostLabel(url, newLabel);
108
+ process.stderr.write(`\x1B[32m\u2713\x1B[0m Label updated: ${newLabel}
109
+ `);
110
+ }
111
+ }
112
+ async function handleDeleteHost(url, label, isActive) {
113
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
114
+ const answer = await new Promise(
115
+ (resolve) => rl.question(`
116
+ Delete \x1B[1m${label}\x1B[0m (${url})? [y/N]: `, (a) => {
117
+ rl.close();
118
+ resolve(a.trim().toLowerCase());
119
+ })
120
+ );
121
+ if (answer === "y" || answer === "yes") {
122
+ removeHost(url);
123
+ process.stderr.write(`\x1B[32m\u2713\x1B[0m Deleted: ${label}
124
+ `);
125
+ if (isActive) {
126
+ const remaining = listHosts();
127
+ if (remaining.length > 0) {
128
+ process.stderr.write(` Active host switched to: ${remaining[0].label}
129
+ `);
130
+ } else {
131
+ process.stderr.write(" No hosts remaining. Run te-cli config to add one.\n");
132
+ }
133
+ }
134
+ }
135
+ }
136
+ async function ensureAuth(hostUrl) {
137
+ const status = getAuthStatus(hostUrl);
138
+ if (status.authenticated) {
139
+ process.stderr.write(`\x1B[32m\u2713\x1B[0m Token valid for ${hostUrl}
140
+ `);
141
+ return;
142
+ }
143
+ process.stderr.write(`
144
+ \x1B[33m!\x1B[0m No valid token for ${hostUrl}. Starting login...
145
+ `);
146
+ try {
147
+ await getToken(hostUrl);
148
+ process.stderr.write(`\x1B[32m\u2713\x1B[0m Authenticated to ${hostUrl}
149
+ `);
150
+ } catch (err) {
151
+ process.stderr.write(`\x1B[31m\u2717\x1B[0m Auth failed: ${err.message}
152
+ `);
153
+ process.stderr.write(` You can set token manually: te-cli auth set-token <token>
154
+ `);
155
+ }
156
+ }
157
+ async function promptNewHost() {
158
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
159
+ const ask = (q) => new Promise((resolve) => rl.question(q, (a) => resolve(a.trim())));
160
+ const url = await ask("TE host URL (e.g., https://ta.thinkingdata.cn): ");
161
+ if (!url) {
162
+ rl.close();
163
+ return null;
164
+ }
165
+ const label = await ask("Label: ");
166
+ rl.close();
167
+ if (!label) return null;
168
+ return { url: normalizeUrl(url), label };
169
+ }
170
+ function normalizeUrl(url) {
171
+ if (!url.startsWith("http://") && !url.startsWith("https://")) {
172
+ url = `https://${url}`;
173
+ }
174
+ return url.replace(/\/$/, "");
175
+ }
176
+ function interactiveHostSelect(items, initialIndex) {
177
+ return new Promise((resolve) => {
178
+ let cursor = initialIndex;
179
+ const stdin = process.stdin;
180
+ const stderr = process.stderr;
181
+ let renderCount = 0;
182
+ stderr.write("\x1B[?25l");
183
+ function render() {
184
+ if (renderCount > 0) {
185
+ stderr.write(`\x1B[${items.length}A`);
186
+ }
187
+ for (let i = 0; i < items.length; i++) {
188
+ const pointer = i === cursor ? "\x1B[36m\u276F\x1B[0m" : " ";
189
+ stderr.write(`${pointer} ${items[i].display}\x1B[K
190
+ `);
191
+ }
192
+ renderCount++;
193
+ }
194
+ render();
195
+ if (!stdin.isTTY) {
196
+ stderr.write("\x1B[?25h");
197
+ resolve({ action: "select", index: initialIndex });
198
+ return;
199
+ }
200
+ stdin.setRawMode(true);
201
+ stdin.resume();
202
+ stdin.setEncoding("utf8");
203
+ function cleanup() {
204
+ stdin.setRawMode(false);
205
+ stdin.pause();
206
+ stdin.removeListener("data", onData);
207
+ stderr.write("\x1B[?25h");
208
+ }
209
+ function onData(key) {
210
+ if (key === "\x1B[A" || key === "k") {
211
+ cursor = (cursor - 1 + items.length) % items.length;
212
+ render();
213
+ } else if (key === "\x1B[B" || key === "j") {
214
+ cursor = (cursor + 1) % items.length;
215
+ render();
216
+ } else if (key === "\r" || key === "\n") {
217
+ cleanup();
218
+ resolve({ action: "select", index: cursor });
219
+ } else if (key === "e" || key === "E") {
220
+ if (items[cursor].type === "host") {
221
+ cleanup();
222
+ resolve({ action: "edit", index: cursor });
223
+ }
224
+ } else if (key === "d" || key === "D") {
225
+ if (items[cursor].type === "host") {
226
+ cleanup();
227
+ resolve({ action: "delete", index: cursor });
228
+ }
229
+ } else if (key === "a" || key === "A") {
230
+ cleanup();
231
+ resolve({ action: "add", index: cursor });
232
+ } else if (key === "q" || key === "Q" || key === "\x1B" || key === "") {
233
+ cleanup();
234
+ resolve({ action: "quit", index: cursor });
235
+ }
236
+ }
237
+ stdin.on("data", onData);
238
+ });
239
+ }
240
+ export {
241
+ registerConfig
242
+ };