@finebi/finebi-cli 1.2.1 → 1.3.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.
- package/README.md +63 -39
- package/package.json +38 -35
- package/skills/finebi-cli-agent/SKILL.md +133 -0
- package/skills/finebi-cli-agent/agents/openai.yaml +7 -0
- package/skills/finebi-cli-agent/references/analysis-dsl-api.md +318 -0
- package/skills/finebi-cli-agent/references/artifact-ordering.md +29 -0
- package/skills/finebi-cli-agent/references/command-workflows.md +194 -0
- package/skills/finebi-cli-agent/references/dashboard-dsl-api.md +1025 -0
- package/skills/finebi-cli-agent/references/dsl-handoff-contract.md +195 -0
- package/skills/finebi-cli-agent/references/file-to-dashboard.md +65 -0
- package/skills/finebi-cli-agent/references/formula-basics.md +102 -0
- package/skills/finebi-cli-agent/references/formula-contexts.md +66 -0
- package/skills/finebi-cli-agent/references/formula-functions.md +247 -0
- package/skills/finebi-cli-agent/references/formula-generation.md +121 -0
- package/skills/finebi-cli-agent/references/formula-glossary.md +17 -0
- package/skills/finebi-cli-agent/references/formula-help-boundaries.md +87 -0
- package/skills/finebi-cli-agent/references/formula-help-index.md +264 -0
- package/skills/finebi-cli-agent/references/formula-parameters-aggregate.md +143 -0
- package/skills/finebi-cli-agent/references/formula-parameters-analysis.md +223 -0
- package/skills/finebi-cli-agent/references/formula-parameters-date.md +236 -0
- package/skills/finebi-cli-agent/references/formula-parameters-math-logic.md +271 -0
- package/skills/finebi-cli-agent/references/formula-parameters-text.md +217 -0
- package/skills/finebi-cli-agent/references/formula-recipes-analysis.md +124 -0
- package/skills/finebi-cli-agent/references/formula-recipes-text-date.md +86 -0
- package/skills/finebi-cli-agent/references/formula-semantics.md +39 -0
- package/skills/finebi-cli-agent/references/formula-syntax.md +70 -0
- package/skills/finebi-cli-agent/references/formula-troubleshooting.md +63 -0
- package/skills/finebi-cli-agent/references/formula-validation.md +36 -0
- package/skills/finebi-cli-agent/references/formula.md +37 -0
- package/skills/finebi-cli-agent/references/handoff-contract.md +210 -0
- package/skills/finebi-cli-agent/references/mapping-rules.md +423 -0
- package/skills/finebi-cli-agent/references/mutation-confirmation-contract.md +150 -0
- package/skills/finebi-cli-agent/references/scenario-workflows.md +75 -0
- package/skills/finebi-cli-agent/references/widget-dsl-api-examples.md +457 -0
- package/skills/finebi-cli-agent/references/widget-dsl-api.md +1340 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/package.json +11 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/access-key-contract.js +2 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/access-key.js +174 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/auth-failure.js +18 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/auth-headers.js +21 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/auth.js +116 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/branding.js +31 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/browser-auth.js +331 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/capability.js +122 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/config-permissions.js +87 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/config.js +189 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/dynamic-command.js +324 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/error-catalog.js +482 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/errors.js +22 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/help.js +467 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/index.js +1343 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/install-skill.js +87 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/output-limits.js +188 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/output.js +33 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/profiles.js +158 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/schema-options.js +265 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/schema-validator.js +194 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/skill-files.js +94 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli-runner-core.mjs +399 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli-runner.mjs +29 -0
- package/src/error-catalog.js +12 -0
- package/src/help.js +15 -0
- package/src/index.js +6 -1
- package/src/install-skill.js +87 -0
- package/src/skill-files.js +94 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import {failure, success} from "./output.js";
|
|
2
|
+
import {ACCESS_KEY_BASE_PATH} from "./access-key-contract.js";
|
|
3
|
+
import {authenticationFailure} from "./auth-failure.js";
|
|
4
|
+
import {buildAuthHeaders} from "./auth-headers.js";
|
|
5
|
+
import {cliError} from "./errors.js";
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 创建 Access Key。使用 JWT 登录态(Cookie/Bearer)认证,服务端据 Cookie 识别当前用户。
|
|
10
|
+
* 校验响应中含 token/id,防止契约漂移时把无效响应当成功。
|
|
11
|
+
*/
|
|
12
|
+
export async function createAccessKey(options) {
|
|
13
|
+
const query = options.description ? `?description=${encodeURIComponent(options.description)}` : "";
|
|
14
|
+
const envelope = await sendAccessKeyRequest({
|
|
15
|
+
...options,
|
|
16
|
+
command: "access-key.create",
|
|
17
|
+
method: "POST",
|
|
18
|
+
endpoint: `${ACCESS_KEY_BASE_PATH}/create${query}`,
|
|
19
|
+
});
|
|
20
|
+
if (!envelope.ok) {
|
|
21
|
+
return envelope;
|
|
22
|
+
}
|
|
23
|
+
const key = envelope.data;
|
|
24
|
+
if (!key || !key.token || !key.id) {
|
|
25
|
+
return failure("access-key.create", cliError("ACCESS_KEY_CREATE_INVALID", {
|
|
26
|
+
path: "data.token",
|
|
27
|
+
message: "Access Key create response did not contain a token.",
|
|
28
|
+
hint: "Check the FineBI CLI plugin version and access-key endpoint contract.",
|
|
29
|
+
retryable: false,
|
|
30
|
+
nextActions: ["finebi-cli auth info"],
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
return success("access-key.create", key);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 列出当前用户(管理员为全部)的 Access Key。响应不含 token 明文。
|
|
38
|
+
*/
|
|
39
|
+
export async function listAccessKeys(options) {
|
|
40
|
+
return sendAccessKeyRequest({
|
|
41
|
+
...options,
|
|
42
|
+
command: "access-key.list",
|
|
43
|
+
method: "GET",
|
|
44
|
+
endpoint: `${ACCESS_KEY_BASE_PATH}/list`,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 按 id 删除 Access Key。
|
|
50
|
+
*/
|
|
51
|
+
export async function deleteAccessKey(options) {
|
|
52
|
+
return sendAccessKeyRequest({
|
|
53
|
+
...options,
|
|
54
|
+
command: "access-key.delete",
|
|
55
|
+
method: "POST",
|
|
56
|
+
endpoint: `${ACCESS_KEY_BASE_PATH}/delete?id=${encodeURIComponent(options.id)}`,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 自撤销:用 X-BI-CLI-Access-Key 头调 /self-revoke 删除自己出示的这把 key。
|
|
62
|
+
* 认证方式为「出示 key 即证明持有」,不依赖 JWT 登录态,适用于 JWT 过期后 logout 撤销。
|
|
63
|
+
*/
|
|
64
|
+
export async function selfRevokeAccessKey(options) {
|
|
65
|
+
return sendAccessKeyRequest({
|
|
66
|
+
...options,
|
|
67
|
+
command: "access-key.self-revoke",
|
|
68
|
+
method: "POST",
|
|
69
|
+
endpoint: `${ACCESS_KEY_BASE_PATH}/self-revoke`,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 删旧建新:列出同 description 的旧 Key 并删除,再创建一把新 Key。
|
|
75
|
+
* list 失败时停止签发,避免无法确认旧 Key 状态却继续创建而产生重复;旧 Key 删除失败逐条告警。
|
|
76
|
+
*/
|
|
77
|
+
export async function provisionAccessKey(options) {
|
|
78
|
+
const warnings = [];
|
|
79
|
+
const listed = await listAccessKeys(options);
|
|
80
|
+
if (listed.ok) {
|
|
81
|
+
const existing = Array.isArray(listed.data?.tokens) ? listed.data.tokens : [];
|
|
82
|
+
const stale = existing.filter((item) => isStaleKeyForCurrentUser(item, options));
|
|
83
|
+
for (const item of stale) {
|
|
84
|
+
const removed = await deleteAccessKey({ ...options, id: item.id });
|
|
85
|
+
if (!removed.ok) {
|
|
86
|
+
warnings.push(cliError("ACCESS_KEY_STALE_DELETE_FAILED", {
|
|
87
|
+
message: `Failed to delete a stale Access Key (id=${item.id}).`,
|
|
88
|
+
id: item.id,
|
|
89
|
+
cause: removed.errors?.[0],
|
|
90
|
+
}));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
warnings.push(cliError("ACCESS_KEY_LIST_FAILED", {
|
|
95
|
+
message: "Could not list existing Access Keys; issuance was stopped to avoid creating a duplicate.",
|
|
96
|
+
cause: listed.errors?.[0],
|
|
97
|
+
}));
|
|
98
|
+
return { created: listed, warnings };
|
|
99
|
+
}
|
|
100
|
+
const created = await createAccessKey(options);
|
|
101
|
+
return { created, warnings };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function isStaleKeyForCurrentUser(item, options) {
|
|
105
|
+
if (!options.username || !item?.id || item.description !== options.description) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
// 管理员 list 可能返回所有用户;缺少当前用户名时宁可保留旧 Key,也不能误删他人的 Key。
|
|
109
|
+
return item.username === options.username;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function sendAccessKeyRequest(options) {
|
|
113
|
+
const baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
114
|
+
const controller = new AbortController();
|
|
115
|
+
const timer = setTimeout(() => controller.abort(), options.timeoutMs || 30000);
|
|
116
|
+
try {
|
|
117
|
+
const response = await (options.fetchImpl || fetch)(`${baseUrl}${options.endpoint}`, {
|
|
118
|
+
method: options.method,
|
|
119
|
+
headers: buildAuthHeaders(options),
|
|
120
|
+
signal: controller.signal,
|
|
121
|
+
});
|
|
122
|
+
return await parseAccessKeyResponse(response, options.command, options.endpoint);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
return failure(options.command, cliError("NETWORK_ERROR", {
|
|
125
|
+
path: options.endpoint,
|
|
126
|
+
message: error instanceof Error ? error.message : String(error),
|
|
127
|
+
hint: "Check the FineBI base URL and network connection.",
|
|
128
|
+
retryable: true,
|
|
129
|
+
nextActions: ["finebi-cli auth info"],
|
|
130
|
+
}));
|
|
131
|
+
} finally {
|
|
132
|
+
clearTimeout(timer);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async function parseAccessKeyResponse(response, command, path) {
|
|
137
|
+
const text = await response.text();
|
|
138
|
+
if (response.ok && (response.status === 204 || text.trim() === "")) {
|
|
139
|
+
return success(command, null);
|
|
140
|
+
}
|
|
141
|
+
const contentType = response.headers.get("content-type") || "";
|
|
142
|
+
if (!contentType.includes("json") || text.trim().startsWith("<")) {
|
|
143
|
+
return failure(command, cliError("AUTH_REDIRECT_OR_HTML_RESPONSE", {
|
|
144
|
+
path,
|
|
145
|
+
message: "FineBI returned an HTML login or redirect page instead of JSON.",
|
|
146
|
+
hint: "Run finebi-cli auth login and retry.",
|
|
147
|
+
retryable: true,
|
|
148
|
+
nextActions: ["finebi-cli auth login"],
|
|
149
|
+
}), { status: response.status });
|
|
150
|
+
}
|
|
151
|
+
if (!response.ok) {
|
|
152
|
+
if (response.status === 401 || response.status === 403) {
|
|
153
|
+
return authenticationFailure(command, path, response.status);
|
|
154
|
+
}
|
|
155
|
+
return failure(command, cliError("HTTP_ERROR", {
|
|
156
|
+
path,
|
|
157
|
+
message: `FineBI returned HTTP ${response.status}: ${text.slice(0, 300)}`,
|
|
158
|
+
hint: "Check FineBI server logs and access-key endpoint availability.",
|
|
159
|
+
retryable: false,
|
|
160
|
+
nextActions: ["finebi-cli auth info"],
|
|
161
|
+
}), { status: response.status });
|
|
162
|
+
}
|
|
163
|
+
const payload = JSON.parse(text);
|
|
164
|
+
if (payload && payload.errorCode) {
|
|
165
|
+
return failure(command, cliError("ACCESS_KEY_ERROR", {
|
|
166
|
+
path,
|
|
167
|
+
message: payload.errorMsg || String(payload.errorCode),
|
|
168
|
+
hint: "Check the username, permissions, and access-key endpoint.",
|
|
169
|
+
retryable: false,
|
|
170
|
+
nextActions: ["finebi-cli auth info"],
|
|
171
|
+
}), { status: response.status });
|
|
172
|
+
}
|
|
173
|
+
return success(command, payload.data ?? null);
|
|
174
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {cliError} from "./errors.js";
|
|
2
|
+
import {failure} from "./output.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 将 401/403 统一映射为可恢复的认证错误。
|
|
6
|
+
*
|
|
7
|
+
* Access Key 被撤销或失效时,业务接口与 capability 接口必须给出相同的重新签发入口,
|
|
8
|
+
* 不能落入“检查服务端日志”一类无法自助恢复的通用 HTTP 提示。
|
|
9
|
+
*/
|
|
10
|
+
export function authenticationFailure(command, path, status) {
|
|
11
|
+
return failure(command, cliError("AUTHENTICATION_FAILED", {
|
|
12
|
+
path,
|
|
13
|
+
message: `FineBI rejected the authentication credentials with HTTP ${status}.`,
|
|
14
|
+
hint: "Run finebi-cli auth login and retry.",
|
|
15
|
+
retryable: true,
|
|
16
|
+
nextActions: ["finebi-cli auth login"],
|
|
17
|
+
}), { status });
|
|
18
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ACCESS_KEY_HEADER_NAME } from "./access-key-contract.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 统一构造 FineBI CLI 认证请求头。
|
|
5
|
+
*
|
|
6
|
+
* Access Key 与平台登录态互斥:一旦存在 CLI Access Key,只发送插件专属 Header;
|
|
7
|
+
* 否则才回退到 Bearer + Cookie,避免调用方各自实现后产生双认证或旧 Header 漂移。
|
|
8
|
+
*/
|
|
9
|
+
export function buildAuthHeaders({ token, accessKey, contentType }) {
|
|
10
|
+
const headers = { Accept: "application/json" };
|
|
11
|
+
if (contentType) {
|
|
12
|
+
headers["Content-Type"] = contentType;
|
|
13
|
+
}
|
|
14
|
+
if (accessKey) {
|
|
15
|
+
headers[ACCESS_KEY_HEADER_NAME] = accessKey;
|
|
16
|
+
} else if (token) {
|
|
17
|
+
headers.Authorization = `Bearer ${token}`;
|
|
18
|
+
headers.Cookie = `fine_auth_token=${token}`;
|
|
19
|
+
}
|
|
20
|
+
return headers;
|
|
21
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import {cliError} from "./errors.js";
|
|
2
|
+
import {failure, success} from "./output.js";
|
|
3
|
+
|
|
4
|
+
export async function loginWithPassword(options) {
|
|
5
|
+
const baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
6
|
+
const controller = new AbortController();
|
|
7
|
+
const timeoutMs = options.timeoutMs || 30000;
|
|
8
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
9
|
+
try {
|
|
10
|
+
const response = await (options.fetchImpl || fetch)(`${baseUrl}/login`, {
|
|
11
|
+
method: "POST",
|
|
12
|
+
headers: {
|
|
13
|
+
Accept: "application/json",
|
|
14
|
+
"Content-Type": "application/json",
|
|
15
|
+
},
|
|
16
|
+
body: JSON.stringify({
|
|
17
|
+
username: options.username,
|
|
18
|
+
password: options.password,
|
|
19
|
+
encrypted: false,
|
|
20
|
+
tenantId: "",
|
|
21
|
+
}),
|
|
22
|
+
signal: controller.signal,
|
|
23
|
+
});
|
|
24
|
+
return await parseLoginResponse(response, options);
|
|
25
|
+
} catch (error) {
|
|
26
|
+
return failure("auth.login", cliError("NETWORK_ERROR", {
|
|
27
|
+
path: "/login",
|
|
28
|
+
message: error instanceof Error ? error.message : String(error),
|
|
29
|
+
hint: "Check the FineBI base URL and network connection.",
|
|
30
|
+
retryable: true,
|
|
31
|
+
nextActions: ["finebi-cli config set base-url <baseUrl>", "finebi-cli auth login --username <username>"],
|
|
32
|
+
}));
|
|
33
|
+
} finally {
|
|
34
|
+
clearTimeout(timer);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function buildLoginAuth(options) {
|
|
39
|
+
const auth = {
|
|
40
|
+
username: options.username,
|
|
41
|
+
token: options.token,
|
|
42
|
+
loginAt: options.now(),
|
|
43
|
+
expiresAt: parseJwtExpiresAt(options.token),
|
|
44
|
+
source: options.source,
|
|
45
|
+
};
|
|
46
|
+
if (!auth.expiresAt) {
|
|
47
|
+
delete auth.expiresAt;
|
|
48
|
+
}
|
|
49
|
+
return auth;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function parseJwtExpiresAt(token) {
|
|
53
|
+
const parts = token.split(".");
|
|
54
|
+
if (parts.length !== 3) {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
|
|
59
|
+
if (!Number.isFinite(payload.exp)) {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
return new Date(payload.exp * 1000).toISOString();
|
|
63
|
+
} catch (_error) {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function resolveLoginInputFromEnv(env = process.env) {
|
|
69
|
+
return {
|
|
70
|
+
baseUrl: env.FINEBI_BASE_URL,
|
|
71
|
+
username: env.FINEBI_USERNAME,
|
|
72
|
+
password: env.FINEBI_PASSWORD,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function parseLoginResponse(response, options) {
|
|
77
|
+
const text = await response.text();
|
|
78
|
+
const contentType = response.headers.get("content-type") || "";
|
|
79
|
+
if (!contentType.includes("json") || text.trim().startsWith("<")) {
|
|
80
|
+
return failure("auth.login", cliError("AUTH_REDIRECT_OR_HTML_RESPONSE", {
|
|
81
|
+
path: "/login",
|
|
82
|
+
message: "FineBI login returned an HTML page instead of JSON.",
|
|
83
|
+
hint: "Check that base-url points to the FineBI decision root.",
|
|
84
|
+
retryable: false,
|
|
85
|
+
nextActions: ["finebi-cli config set base-url <baseUrl>"],
|
|
86
|
+
}), {status: response.status});
|
|
87
|
+
}
|
|
88
|
+
if (!response.ok) {
|
|
89
|
+
return failure("auth.login", cliError("HTTP_ERROR", {
|
|
90
|
+
path: "/login",
|
|
91
|
+
message: `FineBI login returned HTTP ${response.status}: ${text.slice(0, 300)}`,
|
|
92
|
+
hint: "Check the username, password, and FineBI server logs.",
|
|
93
|
+
retryable: false,
|
|
94
|
+
nextActions: ["finebi-cli auth login --username <username>"],
|
|
95
|
+
}), {status: response.status});
|
|
96
|
+
}
|
|
97
|
+
const payload = JSON.parse(text);
|
|
98
|
+
const token = payload?.data?.accessToken;
|
|
99
|
+
if (!token) {
|
|
100
|
+
return failure("auth.login", cliError("LOGIN_TOKEN_NOT_FOUND", {
|
|
101
|
+
path: "data.accessToken",
|
|
102
|
+
message: "FineBI login response does not contain data.accessToken.",
|
|
103
|
+
hint: "Check whether the FineBI login endpoint contract has changed.",
|
|
104
|
+
retryable: false,
|
|
105
|
+
nextActions: ["finebi-cli auth info"],
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
return success("auth.login", {
|
|
109
|
+
auth: buildLoginAuth({
|
|
110
|
+
username: options.username,
|
|
111
|
+
token,
|
|
112
|
+
source: "login",
|
|
113
|
+
now: options.now || (() => new Date().toISOString()),
|
|
114
|
+
}),
|
|
115
|
+
});
|
|
116
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const CLI_COMMAND_NAME = "finebi-cli";
|
|
2
|
+
export const CLI_PACKAGE_NAME = "@finebi/finebi-cli";
|
|
3
|
+
export const CLI_CONFIG_DIRECTORY = ".finebi-cli";
|
|
4
|
+
export const CLI_DISPLAY_NAME = "FineBI CLI";
|
|
5
|
+
|
|
6
|
+
export const CLI_LEGACY_COMMAND_NAME = "finebi-cli";
|
|
7
|
+
export const CLI_API_BASE_PATH = "/api/bi-cli";
|
|
8
|
+
|
|
9
|
+
export function commandLine(...parts) {
|
|
10
|
+
return [CLI_COMMAND_NAME, ...parts].filter((part) => part !== undefined && part !== null && part !== "").join(" ");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function brandCommandText(value) {
|
|
14
|
+
if (typeof value !== "string") {
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
return value.replaceAll(CLI_LEGACY_COMMAND_NAME, CLI_COMMAND_NAME);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function brandCommandValue(value) {
|
|
21
|
+
if (typeof value === "string") {
|
|
22
|
+
return brandCommandText(value);
|
|
23
|
+
}
|
|
24
|
+
if (Array.isArray(value)) {
|
|
25
|
+
return value.map((item) => brandCommandValue(item));
|
|
26
|
+
}
|
|
27
|
+
if (value && typeof value === "object") {
|
|
28
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, brandCommandValue(item)]));
|
|
29
|
+
}
|
|
30
|
+
return value;
|
|
31
|
+
}
|