@thinkingai/ae-cli 1.0.27 → 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/dist/auth-5NSDQFQB.js +454 -0
- package/dist/{auth-TXFJHPXU.js → auth-H2376DEF.js} +2 -3
- package/dist/chunk-AD5V3ZPJ.js +217 -0
- package/dist/chunk-CAQYQA4R.js +399 -0
- package/dist/{chunk-B4UL2VIJ.js → chunk-CLJF7MQA.js} +1 -1
- package/dist/chunk-GSJGPNKK.js +49 -0
- package/dist/{chunk-6T7245YE.js → chunk-IMBKVXKY.js} +8 -8
- package/dist/chunk-KAEZTSXN.js +99 -0
- package/dist/{chunk-VT6VC4PA.js → chunk-LU7SXK4Q.js} +24 -11
- package/dist/chunk-OJDNO5QY.js +63 -0
- package/dist/chunk-SRJIAOBN.js +244 -0
- package/dist/{chunk-2NGWYMLB.js → chunk-WCHI7725.js} +45 -50
- package/dist/{client-IU2E5IOL.js → client-FCMK3XEK.js} +3 -4
- package/dist/config-64G36TJL.js +130 -0
- package/dist/index.js +28 -23
- package/dist/{model-CC7U5XOJ.js → model-EZ6K7FXI.js} +15 -12
- package/dist/{raw-TUHZQ4UB.js → raw-VEF3D6UD.js} +11 -7
- package/dist/{sync-EVEDS7YE.js → sync-NRODEVNH.js} +36 -34
- package/dist/{te-agent-FLTDYV3L.js → te-agent-7U7JLJKU.js} +25 -23
- package/dist/{te-analysis-CMXADIJM.js → te-analysis-A2OSGHQ4.js} +59 -13
- package/dist/{te-audience-LPZFFSXM.js → te-audience-LI67PPGO.js} +5 -5
- package/dist/{te-common-BXQFKTB3.js → te-common-35WVU3C5.js} +19 -9
- package/dist/{te-community-K5S3VOZY.js → te-community-LEAP6PWT.js} +4 -4
- package/dist/{te-dataops-567IMCQG.js → te-dataops-N2LGZBOX.js} +4 -4
- package/dist/{te-engage-TBUTGW5F.js → te-engage-H7C72Z46.js} +4 -4
- package/dist/{te-kb-XHVBWP5E.js → te-kb-I2OKTBAI.js} +5 -5
- package/dist/{te-meta-NQ4U3VJW.js → te-meta-HBTIEDRO.js} +4 -4
- package/dist/{te-team-556APRIU.js → te-team-EM5OWRQL.js} +7 -7
- package/package.json +6 -2
- package/skills/ae-agent/SKILL.md +1 -0
- package/skills/ae-analysis/SKILL.md +1 -1
- package/skills/ae-analysis/references/list_clusters.md +4 -2
- 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 +1 -0
- 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/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/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 +1 -0
- package/skills/ae-team/SKILL.md +1 -1
- package/dist/auth-W2JZ3DKA.js +0 -170
- package/dist/chunk-BI5ZVD6T.js +0 -186
- package/dist/chunk-MNHE6SSI.js +0 -79
- package/dist/chunk-MWWYGZ76.js +0 -130
- package/dist/chunk-SRRUNQIM.js +0 -312
- package/dist/chunk-TG64AQV4.js +0 -262
- package/dist/config-WH3R3IRY.js +0 -258
package/skills/ae-kb/SKILL.md
CHANGED
|
@@ -20,6 +20,7 @@ ae-cli kb +<command> [options]
|
|
|
20
20
|
- Do not invent knowledge base names, scopes, source display names, or JSON payloads. Ask the user or query known context when values are missing.
|
|
21
21
|
- JSON flags must be valid JSON strings, usually wrapped in single quotes in shell commands.
|
|
22
22
|
- Successful commands return JSON by default. Use `--format table` only when a table is easier for a human to scan.
|
|
23
|
+
- `--host <url>` overrides the active AE host. It is available on every command and may be placed after the subcommand, e.g. `ae-cli kb +<command> --host <url>`.
|
|
23
24
|
- For external-agent retrieval, prefer the deterministic flow `+index` -> `+grep` -> `+read`: inspect navigation, locate candidate pages, then open the exact page or line window.
|
|
24
25
|
|
|
25
26
|
## Commands
|
package/skills/ae-team/SKILL.md
CHANGED
|
@@ -20,7 +20,7 @@ Global parameters:
|
|
|
20
20
|
|---|---|
|
|
21
21
|
| `--format <json\|table>` | Output format. Default is JSON. |
|
|
22
22
|
| `--jq <expr>` | jq filter expression for JSON output. |
|
|
23
|
-
| `--host <url>` | Override active AE host
|
|
23
|
+
| `--host <url>` | Override the active AE host. Available on every command and may be placed after the subcommand, e.g. `ae-cli team +<command> --host <url>`. |
|
|
24
24
|
| `--yes` | Skip confirmation for write operations. |
|
|
25
25
|
| `--dry-run` | Show request details without executing. |
|
|
26
26
|
|
package/dist/auth-W2JZ3DKA.js
DELETED
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
printError,
|
|
3
|
-
printOutput
|
|
4
|
-
} from "./chunk-B4UL2VIJ.js";
|
|
5
|
-
import {
|
|
6
|
-
clearMcpToken,
|
|
7
|
-
loadMcpTokenStore,
|
|
8
|
-
setMcpTokenManual,
|
|
9
|
-
validateMcpToken
|
|
10
|
-
} from "./chunk-2NGWYMLB.js";
|
|
11
|
-
import {
|
|
12
|
-
clearToken,
|
|
13
|
-
getAuthStatus,
|
|
14
|
-
getToken,
|
|
15
|
-
resolveHost,
|
|
16
|
-
setTokenManual,
|
|
17
|
-
validateToken
|
|
18
|
-
} from "./chunk-SRRUNQIM.js";
|
|
19
|
-
import {
|
|
20
|
-
loadConfig,
|
|
21
|
-
saveConfig
|
|
22
|
-
} from "./chunk-BI5ZVD6T.js";
|
|
23
|
-
import {
|
|
24
|
-
logger
|
|
25
|
-
} from "./chunk-MNHE6SSI.js";
|
|
26
|
-
|
|
27
|
-
// src/commands/auth.ts
|
|
28
|
-
function registerAuth(program) {
|
|
29
|
-
const auth = program.command("auth").description("Authentication management");
|
|
30
|
-
auth.command("login").description("Login to AE (auto-extract token from Chrome on macOS)").action(async () => {
|
|
31
|
-
const host = resolveHost(program.opts().host);
|
|
32
|
-
if (!host) {
|
|
33
|
-
printError("config", "No AE host configured.", "Run: ae-cli config set-host");
|
|
34
|
-
process.exit(1);
|
|
35
|
-
}
|
|
36
|
-
const config = loadConfig();
|
|
37
|
-
if (!config.hosts[host]) {
|
|
38
|
-
config.hosts[host] = { label: host };
|
|
39
|
-
if (!config.activeHost) {
|
|
40
|
-
config.activeHost = host;
|
|
41
|
-
}
|
|
42
|
-
saveConfig(config);
|
|
43
|
-
logger.info(`Host config auto-saved for ${host}`);
|
|
44
|
-
process.stderr.write(`[ae-cli] Config saved for ${host}
|
|
45
|
-
`);
|
|
46
|
-
}
|
|
47
|
-
try {
|
|
48
|
-
const token = await getToken(host);
|
|
49
|
-
logger.info(`Auth login successful for ${host}`);
|
|
50
|
-
process.stderr.write(`[ae-cli] Authenticated to ${host}
|
|
51
|
-
`);
|
|
52
|
-
printOutput({ authenticated: true, host, token: token.slice(0, 8) + "..." }, program.opts().format || "json");
|
|
53
|
-
} catch (err) {
|
|
54
|
-
printError("auth", err.message);
|
|
55
|
-
process.exit(1);
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
auth.command("set-token <token>").description("Manually set authentication token for active host").action(async (token) => {
|
|
59
|
-
const host = resolveHost(program.opts().host);
|
|
60
|
-
if (!host) {
|
|
61
|
-
printError("config", "No AE host configured.", "Run: ae-cli config set-host");
|
|
62
|
-
process.exit(1);
|
|
63
|
-
}
|
|
64
|
-
const config = loadConfig();
|
|
65
|
-
if (!config.hosts[host]) {
|
|
66
|
-
config.hosts[host] = { label: host };
|
|
67
|
-
if (!config.activeHost) {
|
|
68
|
-
config.activeHost = host;
|
|
69
|
-
}
|
|
70
|
-
saveConfig(config);
|
|
71
|
-
process.stderr.write(`[ae-cli] Config saved for ${host}
|
|
72
|
-
`);
|
|
73
|
-
printOutput({ saved: true, config }, program.opts().format || "json");
|
|
74
|
-
}
|
|
75
|
-
process.stderr.write(`[ae-cli] Validating token...
|
|
76
|
-
`);
|
|
77
|
-
const isValid = await validateToken(token, host);
|
|
78
|
-
if (!isValid) {
|
|
79
|
-
logger.warn(`Token validation failed for ${host}`);
|
|
80
|
-
process.stderr.write(`[ae-cli] Token validation failed
|
|
81
|
-
`);
|
|
82
|
-
printError("auth", "Invalid token", "Please check your token and try again");
|
|
83
|
-
process.exit(1);
|
|
84
|
-
}
|
|
85
|
-
setTokenManual(token, host);
|
|
86
|
-
logger.info(`Token manually set and validated for ${host}`);
|
|
87
|
-
process.stderr.write(`[ae-cli] Token verified and saved for ${host}
|
|
88
|
-
`);
|
|
89
|
-
printOutput({ saved: true, host, verified: true }, program.opts().format || "json");
|
|
90
|
-
});
|
|
91
|
-
auth.command("status").description("Show current authentication status").action(async () => {
|
|
92
|
-
const host = resolveHost(program.opts().host);
|
|
93
|
-
if (!host) {
|
|
94
|
-
printOutput({ authenticated: false, host: "(none)", hint: "Run: ae-cli config set-host" }, program.opts().format || "json");
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
const tokenStatus = getAuthStatus(host);
|
|
98
|
-
const hasValidToken = tokenStatus.authenticated;
|
|
99
|
-
const mcpStore = loadMcpTokenStore();
|
|
100
|
-
const cachedMcpToken = mcpStore[host];
|
|
101
|
-
let hasValidMcpToken = false;
|
|
102
|
-
if (cachedMcpToken) {
|
|
103
|
-
hasValidMcpToken = await validateMcpToken(cachedMcpToken, host);
|
|
104
|
-
}
|
|
105
|
-
const authenticated = hasValidToken || hasValidMcpToken;
|
|
106
|
-
printOutput({ authenticated, host }, program.opts().format || "json");
|
|
107
|
-
});
|
|
108
|
-
auth.command("logout").description("Clear stored token for active host").action(() => {
|
|
109
|
-
const host = resolveHost(program.opts().host);
|
|
110
|
-
if (!host) {
|
|
111
|
-
printError("config", "No AE host configured.", "Run: ae-cli config set-host");
|
|
112
|
-
process.exit(1);
|
|
113
|
-
}
|
|
114
|
-
clearToken(host);
|
|
115
|
-
process.stderr.write(`[ae-cli] Token cleared for ${host}
|
|
116
|
-
`);
|
|
117
|
-
printOutput({ cleared: true, host }, program.opts().format || "json");
|
|
118
|
-
});
|
|
119
|
-
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) => {
|
|
120
|
-
const host = opts.host || resolveHost(program.opts().host);
|
|
121
|
-
if (!host) {
|
|
122
|
-
printError("config", "No AE host configured.", "Use --host <url> or run: ae-cli config set-host");
|
|
123
|
-
process.exit(1);
|
|
124
|
-
}
|
|
125
|
-
const config = loadConfig();
|
|
126
|
-
if (!config.hosts[host]) {
|
|
127
|
-
config.hosts[host] = { label: host };
|
|
128
|
-
if (!config.activeHost) {
|
|
129
|
-
config.activeHost = host;
|
|
130
|
-
}
|
|
131
|
-
saveConfig(config);
|
|
132
|
-
process.stderr.write(`[ae-cli] Host config saved for ${host}
|
|
133
|
-
`);
|
|
134
|
-
}
|
|
135
|
-
process.stderr.write(`[ae-cli] Validating MCP token...
|
|
136
|
-
`);
|
|
137
|
-
const isValid = await validateMcpToken(token, host);
|
|
138
|
-
if (!isValid) {
|
|
139
|
-
process.stderr.write(`[ae-cli] MCP token validation failed
|
|
140
|
-
`);
|
|
141
|
-
printError("auth", "Invalid MCP token", "Please check your MCP token and try again");
|
|
142
|
-
process.exit(1);
|
|
143
|
-
}
|
|
144
|
-
setMcpTokenManual(token, host);
|
|
145
|
-
process.stderr.write(`[ae-cli] MCP token verified and saved for ${host}
|
|
146
|
-
`);
|
|
147
|
-
printOutput({ saved: true, host, verified: true, type: "mcp-token" }, program.opts().format || "json");
|
|
148
|
-
});
|
|
149
|
-
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) => {
|
|
150
|
-
if (opts.all) {
|
|
151
|
-
clearMcpToken();
|
|
152
|
-
process.stderr.write(`[ae-cli] All MCP tokens cleared
|
|
153
|
-
`);
|
|
154
|
-
printOutput({ cleared: true, type: "all-mcp-tokens" }, program.opts().format || "json");
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
const host = opts.host || resolveHost(program.opts().host);
|
|
158
|
-
if (!host) {
|
|
159
|
-
printError("config", "No AE host configured.", "Use --host <url> or run: ae-cli config set-host");
|
|
160
|
-
process.exit(1);
|
|
161
|
-
}
|
|
162
|
-
clearMcpToken(host);
|
|
163
|
-
process.stderr.write(`[ae-cli] MCP token cleared for ${host}
|
|
164
|
-
`);
|
|
165
|
-
printOutput({ cleared: true, host }, program.opts().format || "json");
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
export {
|
|
169
|
-
registerAuth
|
|
170
|
-
};
|
package/dist/chunk-BI5ZVD6T.js
DELETED
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
logger
|
|
3
|
-
} from "./chunk-MNHE6SSI.js";
|
|
4
|
-
|
|
5
|
-
// src/core/json-utils.ts
|
|
6
|
-
import fs from "fs";
|
|
7
|
-
import { createRequire } from "module";
|
|
8
|
-
var require2 = createRequire(import.meta.url);
|
|
9
|
-
var JSONbig = require2("json-bigint");
|
|
10
|
-
var jsonParser = JSONbig({ storeAsString: true });
|
|
11
|
-
function safeJsonParse(text) {
|
|
12
|
-
return jsonParser.parse(text);
|
|
13
|
-
}
|
|
14
|
-
function safeReadJsonFile(filePath) {
|
|
15
|
-
const content = fs.readFileSync(filePath, "utf-8");
|
|
16
|
-
return safeJsonParse(content);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// src/core/config.ts
|
|
20
|
-
import fs2 from "fs";
|
|
21
|
-
import path from "path";
|
|
22
|
-
var CONFIG_DIR = path.join(process.env.HOME || "", ".ae-cli");
|
|
23
|
-
var CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
|
|
24
|
-
var MCP_TOKENS_FILE = path.join(CONFIG_DIR, "mcp-tokens.json");
|
|
25
|
-
var FALLBACK_MCP_TOKEN_FILE = "/home/ta/te_agent_ta/.ae-config/mcp-token.json";
|
|
26
|
-
function ensureDir() {
|
|
27
|
-
if (!fs2.existsSync(CONFIG_DIR)) fs2.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
28
|
-
}
|
|
29
|
-
function migrateFromFallback() {
|
|
30
|
-
if (!fs2.existsSync(FALLBACK_MCP_TOKEN_FILE) || fs2.existsSync(MCP_TOKENS_FILE)) {
|
|
31
|
-
return null;
|
|
32
|
-
}
|
|
33
|
-
return doMigrateFromFallback();
|
|
34
|
-
}
|
|
35
|
-
function forceMigrateFromFallback() {
|
|
36
|
-
if (!fs2.existsSync(FALLBACK_MCP_TOKEN_FILE)) {
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
return doMigrateFromFallback();
|
|
40
|
-
}
|
|
41
|
-
function doMigrateFromFallback() {
|
|
42
|
-
try {
|
|
43
|
-
const fallbackData = safeReadJsonFile(FALLBACK_MCP_TOKEN_FILE);
|
|
44
|
-
const tokenStore = {};
|
|
45
|
-
if (fallbackData && typeof fallbackData === "object" && !Array.isArray(fallbackData)) {
|
|
46
|
-
const url = fallbackData.url;
|
|
47
|
-
const token = fallbackData["mcp-token"];
|
|
48
|
-
if (url && token) {
|
|
49
|
-
tokenStore[url] = token;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
if (Object.keys(tokenStore).length > 0) {
|
|
53
|
-
ensureDir();
|
|
54
|
-
fs2.writeFileSync(MCP_TOKENS_FILE, JSON.stringify(tokenStore, null, 2));
|
|
55
|
-
return tokenStore;
|
|
56
|
-
}
|
|
57
|
-
} catch {
|
|
58
|
-
}
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
|
-
function loadConfig() {
|
|
62
|
-
try {
|
|
63
|
-
const migratedTokens = migrateFromFallback();
|
|
64
|
-
if (fs2.existsSync(CONFIG_FILE)) {
|
|
65
|
-
const raw = safeReadJsonFile(CONFIG_FILE);
|
|
66
|
-
if (raw.defaultHost && !raw.activeHost) {
|
|
67
|
-
const migrated = migrateConfig(raw);
|
|
68
|
-
saveConfig(migrated);
|
|
69
|
-
return migrated;
|
|
70
|
-
}
|
|
71
|
-
return raw;
|
|
72
|
-
}
|
|
73
|
-
if (migratedTokens && Object.keys(migratedTokens).length > 0) {
|
|
74
|
-
const hosts = {};
|
|
75
|
-
for (const url of Object.keys(migratedTokens)) {
|
|
76
|
-
hosts[url] = { label: url };
|
|
77
|
-
process.stderr.write(`[ae-cli] Added host: ${url}
|
|
78
|
-
`);
|
|
79
|
-
}
|
|
80
|
-
const activeHost = Object.keys(migratedTokens)[0];
|
|
81
|
-
process.stderr.write(`[ae-cli] Active host set to: ${activeHost}
|
|
82
|
-
`);
|
|
83
|
-
process.stderr.write(`[ae-cli] MCP tokens migrated from ${FALLBACK_MCP_TOKEN_FILE}
|
|
84
|
-
`);
|
|
85
|
-
logger.info(`MCP tokens migrated from fallback: activeHost=${activeHost}, hosts=${Object.keys(hosts).length}`);
|
|
86
|
-
const config = { activeHost, hosts };
|
|
87
|
-
saveConfig(config);
|
|
88
|
-
return config;
|
|
89
|
-
}
|
|
90
|
-
} catch (err) {
|
|
91
|
-
logger.error(`Error loading config: ${err.message}`);
|
|
92
|
-
console.error(`Error loading config: ${err.message}`);
|
|
93
|
-
}
|
|
94
|
-
return { activeHost: "", hosts: {} };
|
|
95
|
-
}
|
|
96
|
-
function migrateConfig(old) {
|
|
97
|
-
const hosts = {};
|
|
98
|
-
const oldHost = old.defaultHost;
|
|
99
|
-
if (old.hosts) {
|
|
100
|
-
for (const [key, val] of Object.entries(old.hosts)) {
|
|
101
|
-
const url = key.startsWith("http") ? key : `https://${key}`;
|
|
102
|
-
hosts[url] = val;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
const activeHost = oldHost.startsWith("http") ? oldHost : `https://${oldHost}`;
|
|
106
|
-
if (!hosts[activeHost]) {
|
|
107
|
-
hosts[activeHost] = { label: "default" };
|
|
108
|
-
}
|
|
109
|
-
return { activeHost, hosts };
|
|
110
|
-
}
|
|
111
|
-
function saveConfig(config) {
|
|
112
|
-
ensureDir();
|
|
113
|
-
fs2.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
114
|
-
logger.info(`Config saved: activeHost=${config.activeHost}, hosts=${Object.keys(config.hosts).length}`);
|
|
115
|
-
}
|
|
116
|
-
function getActiveHost() {
|
|
117
|
-
const config = loadConfig();
|
|
118
|
-
return config.activeHost;
|
|
119
|
-
}
|
|
120
|
-
function setActiveHost(url) {
|
|
121
|
-
const config = loadConfig();
|
|
122
|
-
if (!config.hosts[url]) {
|
|
123
|
-
throw new Error(`Host ${url} is not in the configured hosts list. Add it first.`);
|
|
124
|
-
}
|
|
125
|
-
config.activeHost = url;
|
|
126
|
-
saveConfig(config);
|
|
127
|
-
}
|
|
128
|
-
function addHost(url, label) {
|
|
129
|
-
const config = loadConfig();
|
|
130
|
-
config.hosts[url] = { label };
|
|
131
|
-
if (!config.activeHost) {
|
|
132
|
-
config.activeHost = url;
|
|
133
|
-
}
|
|
134
|
-
saveConfig(config);
|
|
135
|
-
}
|
|
136
|
-
function updateHostLabel(url, label) {
|
|
137
|
-
const config = loadConfig();
|
|
138
|
-
if (config.hosts[url]) {
|
|
139
|
-
config.hosts[url].label = label;
|
|
140
|
-
saveConfig(config);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
function removeHost(url) {
|
|
144
|
-
const config = loadConfig();
|
|
145
|
-
delete config.hosts[url];
|
|
146
|
-
if (config.activeHost === url) {
|
|
147
|
-
const remaining = Object.keys(config.hosts);
|
|
148
|
-
config.activeHost = remaining.length > 0 ? remaining[0] : "";
|
|
149
|
-
}
|
|
150
|
-
saveConfig(config);
|
|
151
|
-
}
|
|
152
|
-
function listHosts() {
|
|
153
|
-
const config = loadConfig();
|
|
154
|
-
return Object.entries(config.hosts).map(([url, entry]) => ({
|
|
155
|
-
url,
|
|
156
|
-
label: entry.label,
|
|
157
|
-
active: url === config.activeHost
|
|
158
|
-
}));
|
|
159
|
-
}
|
|
160
|
-
function getConfigDir() {
|
|
161
|
-
return CONFIG_DIR;
|
|
162
|
-
}
|
|
163
|
-
function extractHostname(fullUrl) {
|
|
164
|
-
try {
|
|
165
|
-
const u = new URL(fullUrl);
|
|
166
|
-
return u.host;
|
|
167
|
-
} catch {
|
|
168
|
-
return fullUrl;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
export {
|
|
173
|
-
safeJsonParse,
|
|
174
|
-
safeReadJsonFile,
|
|
175
|
-
forceMigrateFromFallback,
|
|
176
|
-
loadConfig,
|
|
177
|
-
saveConfig,
|
|
178
|
-
getActiveHost,
|
|
179
|
-
setActiveHost,
|
|
180
|
-
addHost,
|
|
181
|
-
updateHostLabel,
|
|
182
|
-
removeHost,
|
|
183
|
-
listHosts,
|
|
184
|
-
getConfigDir,
|
|
185
|
-
extractHostname
|
|
186
|
-
};
|