timarai-dashboard-mcp 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/CHANGELOG.md +105 -0
- package/README.md +266 -0
- package/dist/charts/build.js +130 -0
- package/dist/charts/build.js.map +1 -0
- package/dist/charts/format.js +101 -0
- package/dist/charts/format.js.map +1 -0
- package/dist/charts/specs.js +708 -0
- package/dist/charts/specs.js.map +1 -0
- package/dist/charts/svg.js +371 -0
- package/dist/charts/svg.js.map +1 -0
- package/dist/client.js +284 -0
- package/dist/client.js.map +1 -0
- package/dist/config.js +164 -0
- package/dist/config.js.map +1 -0
- package/dist/date-range.js +190 -0
- package/dist/date-range.js.map +1 -0
- package/dist/format.js +369 -0
- package/dist/format.js.map +1 -0
- package/dist/index.js +63 -0
- package/dist/index.js.map +1 -0
- package/dist/output.js +46 -0
- package/dist/output.js.map +1 -0
- package/dist/registry.js +231 -0
- package/dist/registry.js.map +1 -0
- package/dist/reports/html.js +84 -0
- package/dist/reports/html.js.map +1 -0
- package/dist/reports/templates.js +113 -0
- package/dist/reports/templates.js.map +1 -0
- package/dist/resources.js +265 -0
- package/dist/resources.js.map +1 -0
- package/dist/server.js +588 -0
- package/dist/server.js.map +1 -0
- package/dist/setup.js +344 -0
- package/dist/setup.js.map +1 -0
- package/dist/signature.js +35 -0
- package/dist/signature.js.map +1 -0
- package/docs/CAPABILITIES.md +275 -0
- package/docs/CONTRACT.md +170 -0
- package/docs/DESIGN.md +367 -0
- package/docs/DISTRIBUTION.md +214 -0
- package/docs/VERIFICATION.md +120 -0
- package/package.json +42 -0
package/dist/setup.js
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 首次使用向导(交互式),产物是仓库根目录的 `.env`。
|
|
3
|
+
*
|
|
4
|
+
* 为什么不把交互塞进 `index.ts`:
|
|
5
|
+
* Server 走 STDIO 时 stdout 被 MCP 协议占用、stdin 是 Agent 发来的 JSON-RPC,
|
|
6
|
+
* 任何提问都会被当成协议噪音。所以引导必须是**独立的、人手动触发的命令**。
|
|
7
|
+
* Server 的职责只到「检测到没配置 → 指路到这里」。
|
|
8
|
+
*
|
|
9
|
+
* 设计取舍:
|
|
10
|
+
* - 每一步都解释「这个值是什么、去哪拿、填错会怎样」,而不是甩三个变量名让人猜;
|
|
11
|
+
* - Secret 走隐藏输入,且任何回显都脱敏;
|
|
12
|
+
* - 全部答案集中到最后**一次写入**,中途 Ctrl+C 不会留下残缺的 .env;
|
|
13
|
+
* - 写完立刻做一次真实调用自检——配错地址比配错密码更常见,而密码错有明确 401,
|
|
14
|
+
* 地址错只会得到一句 fetch failed。
|
|
15
|
+
*/
|
|
16
|
+
import { createInterface } from "node:readline";
|
|
17
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
18
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
19
|
+
import { DashboardClient } from "./client.js";
|
|
20
|
+
import { loadConfig } from "./config.js";
|
|
21
|
+
import { getEndpointById } from "./registry.js";
|
|
22
|
+
const C = {
|
|
23
|
+
reset: "\x1b[0m",
|
|
24
|
+
dim: "\x1b[2m",
|
|
25
|
+
bold: "\x1b[1m",
|
|
26
|
+
cyan: "\x1b[36m",
|
|
27
|
+
green: "\x1b[32m",
|
|
28
|
+
yellow: "\x1b[33m",
|
|
29
|
+
red: "\x1b[31m",
|
|
30
|
+
};
|
|
31
|
+
const ENV_FILE = new URL("../.env", import.meta.url);
|
|
32
|
+
const ENV_LABEL = fileURLToPath(ENV_FILE);
|
|
33
|
+
// ------------------------------------------------------------------ 输入原语
|
|
34
|
+
function openPrompt() {
|
|
35
|
+
return createInterface({ input: process.stdin, output: process.stdout, terminal: true });
|
|
36
|
+
}
|
|
37
|
+
async function ask(rl, question, hint) {
|
|
38
|
+
process.stdout.write(`\n${C.bold}${question}${C.reset}\n`);
|
|
39
|
+
if (hint)
|
|
40
|
+
process.stdout.write(`${C.dim}${hint}${C.reset}\n`);
|
|
41
|
+
const answer = await new Promise((resolve) => rl.question(`${C.cyan}› ${C.reset}`, resolve));
|
|
42
|
+
return answer.trim();
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 隐藏式输入 + 长度校验,用于 Secret。
|
|
46
|
+
* 终端不支持 raw mode 时退回明文,并明确告知——静默地「假装隐藏」才是最糟的结果。
|
|
47
|
+
*/
|
|
48
|
+
async function requireSecret(rl, question, hint) {
|
|
49
|
+
const hidden = Boolean(process.stdin.isTTY && typeof process.stdin.setRawMode === "function");
|
|
50
|
+
let hiddenNoticeShown = false;
|
|
51
|
+
for (let i = 0; i < 5; i += 1) {
|
|
52
|
+
process.stdout.write(`\n${C.bold}${question}${C.reset}\n`);
|
|
53
|
+
if (hint)
|
|
54
|
+
process.stdout.write(`${C.dim}${hint}${C.reset}\n`);
|
|
55
|
+
if (!hidden && !hiddenNoticeShown) {
|
|
56
|
+
process.stdout.write(`${C.yellow}(当前终端不支持隐藏输入,将以星号或明文显示)${C.reset}\n`);
|
|
57
|
+
hiddenNoticeShown = true;
|
|
58
|
+
}
|
|
59
|
+
const value = hidden
|
|
60
|
+
? await readMasked()
|
|
61
|
+
: (await new Promise((resolve) => rl.question(`${C.cyan}› ${C.reset}`, resolve))).trim();
|
|
62
|
+
if (value.length >= 8)
|
|
63
|
+
return value;
|
|
64
|
+
process.stdout.write(`${C.red}✗ Secret 至少需要 8 位,当前 ${value.length} 位${C.reset}\n`);
|
|
65
|
+
}
|
|
66
|
+
throw new Error("重试次数过多,请重新运行 npm run setup");
|
|
67
|
+
}
|
|
68
|
+
/** 逐字符读取并回显星号,支持退格与 Ctrl+C。 */
|
|
69
|
+
function readMasked() {
|
|
70
|
+
process.stdout.write(`${C.cyan}› ${C.reset}`);
|
|
71
|
+
return new Promise((resolve) => {
|
|
72
|
+
const stdin = process.stdin;
|
|
73
|
+
let value = "";
|
|
74
|
+
const onData = (chunk) => {
|
|
75
|
+
for (const ch of String(chunk)) {
|
|
76
|
+
if (ch === "\n" || ch === "\r") {
|
|
77
|
+
cleanup();
|
|
78
|
+
process.stdout.write("\n");
|
|
79
|
+
resolve(value.trim());
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (ch === "\u0003") {
|
|
83
|
+
cleanup();
|
|
84
|
+
process.stdout.write("\n");
|
|
85
|
+
process.exit(130);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (ch === "\u007f") {
|
|
89
|
+
value = value.slice(0, -1);
|
|
90
|
+
if (value.length)
|
|
91
|
+
process.stdout.write("\b \b");
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (ch < " ")
|
|
95
|
+
continue;
|
|
96
|
+
value += ch;
|
|
97
|
+
process.stdout.write("*");
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
function cleanup() {
|
|
101
|
+
stdin.off("data", onData);
|
|
102
|
+
if (typeof stdin.setRawMode === "function")
|
|
103
|
+
stdin.setRawMode(false);
|
|
104
|
+
}
|
|
105
|
+
if (typeof stdin.setRawMode === "function")
|
|
106
|
+
stdin.setRawMode(true);
|
|
107
|
+
stdin.on("data", onData);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
async function askOptional(rl, question, defaultValue, hint) {
|
|
111
|
+
const answer = await ask(rl, `${question}`, `${hint ?? ""}${hint ? " " : ""}留空则用默认值 ${defaultValue}`);
|
|
112
|
+
return answer || defaultValue;
|
|
113
|
+
}
|
|
114
|
+
// ------------------------------------------------------------------ 校验
|
|
115
|
+
function maskSecret(secret) {
|
|
116
|
+
if (!secret)
|
|
117
|
+
return "(未设置)";
|
|
118
|
+
if (secret.length <= 8)
|
|
119
|
+
return "*".repeat(secret.length);
|
|
120
|
+
return `${secret.slice(0, 3)}***${secret.slice(-3)} (${secret.length} 位)`;
|
|
121
|
+
}
|
|
122
|
+
const URL_RE = /^https?:\/\/[^\s/$.?#].[^\s]*$/i;
|
|
123
|
+
async function requireUrl(rl, question, hint) {
|
|
124
|
+
for (let i = 0; i < 5; i += 1) {
|
|
125
|
+
const v = await ask(rl, question, hint);
|
|
126
|
+
if (URL_RE.test(v))
|
|
127
|
+
return v.replace(/\/+$/, "");
|
|
128
|
+
process.stdout.write(`${C.red}✗ 需要完整的 http(s) 地址,形如 https://<你的接口域名>${C.reset}\n`);
|
|
129
|
+
}
|
|
130
|
+
throw new Error("重试次数过多,请重新运行 npm run setup");
|
|
131
|
+
}
|
|
132
|
+
async function requireNonEmpty(rl, question, hint) {
|
|
133
|
+
for (let i = 0; i < 5; i += 1) {
|
|
134
|
+
const v = await ask(rl, question, hint);
|
|
135
|
+
if (v)
|
|
136
|
+
return v;
|
|
137
|
+
process.stdout.write(`${C.red}✗ 不能为空${C.reset}\n`);
|
|
138
|
+
}
|
|
139
|
+
throw new Error("重试次数过多,请重新运行 npm run setup");
|
|
140
|
+
}
|
|
141
|
+
// ------------------------------------------------------------------ 主流程
|
|
142
|
+
function banner() {
|
|
143
|
+
process.stdout.write([
|
|
144
|
+
"",
|
|
145
|
+
`${C.bold}TimarAI 驾驶舱 MCP · 首次配置${C.reset}`,
|
|
146
|
+
`${C.dim}下面几步会把配置写进仓库根目录的 .env,随时可以 Ctrl+C 退出(退出前不会写入)。${C.reset}`,
|
|
147
|
+
`${C.dim}本服务只读取平台驾驶舱接口的数据,不会写入任何业务数据。${C.reset}`,
|
|
148
|
+
"",
|
|
149
|
+
].join("\n"));
|
|
150
|
+
}
|
|
151
|
+
function readExisting() {
|
|
152
|
+
if (!existsSync(ENV_FILE))
|
|
153
|
+
return { found: false, values: {} };
|
|
154
|
+
const values = {};
|
|
155
|
+
for (const line of readFileSync(ENV_FILE, "utf8").split(/\r?\n/)) {
|
|
156
|
+
const trimmed = line.trim();
|
|
157
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
158
|
+
continue;
|
|
159
|
+
const eq = trimmed.indexOf("=");
|
|
160
|
+
if (eq <= 0)
|
|
161
|
+
continue;
|
|
162
|
+
values[trimmed.slice(0, eq).trim()] = trimmed.slice(eq + 1).trim();
|
|
163
|
+
}
|
|
164
|
+
return { found: true, values };
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* 渲染 .env 全文。
|
|
168
|
+
*
|
|
169
|
+
* 导出是为了让冒烟测试能校验「向导产出的文件」确实能通过 Server 的配置校验——
|
|
170
|
+
* 少写一个必填项,用户跑完向导仍会在启动时失败,而那时他只会觉得是这个工具不会用。
|
|
171
|
+
*/
|
|
172
|
+
export function renderEnv(answers) {
|
|
173
|
+
return [
|
|
174
|
+
"# 由 npm run setup 生成。切勿提交进版本库(已在 .gitignore 中排除)。",
|
|
175
|
+
`# 生成时间:${new Date().toISOString()}`,
|
|
176
|
+
"",
|
|
177
|
+
"# 平台驾驶舱 OpenAPI 地址(不含末尾斜杠)",
|
|
178
|
+
`TIMARAI_API_BASE_URL=${answers.TIMARAI_API_BASE_URL}`,
|
|
179
|
+
"",
|
|
180
|
+
"# 平台数据字典中登记的凭证(Code=PlatformDashboardOpenApi)",
|
|
181
|
+
"# 注意:与商户 OpenAPI 的 X-Api-Key 凭证完全隔离,不可混用。",
|
|
182
|
+
`TIMARAI_PLATFORM_CLIENT_ID=${answers.TIMARAI_PLATFORM_CLIENT_ID}`,
|
|
183
|
+
`TIMARAI_PLATFORM_SECRET=${answers.TIMARAI_PLATFORM_SECRET}`,
|
|
184
|
+
"",
|
|
185
|
+
"# 可选调优(以下均为默认值,按需修改)",
|
|
186
|
+
`TIMARAI_HTTP_TIMEOUT_MS=${answers.TIMARAI_HTTP_TIMEOUT_MS}`,
|
|
187
|
+
`TIMARAI_MAX_RETRIES=${answers.TIMARAI_MAX_RETRIES}`,
|
|
188
|
+
`TIMARAI_MAX_DATE_SPAN_DAYS=${answers.TIMARAI_MAX_DATE_SPAN_DAYS}`,
|
|
189
|
+
`TIMARAI_ALLOW_RAW_ENDPOINT=${answers.TIMARAI_ALLOW_RAW_ENDPOINT}`,
|
|
190
|
+
`TIMARAI_FILTER_CACHE_TTL_MS=${answers.TIMARAI_FILTER_CACHE_TTL_MS}`,
|
|
191
|
+
"",
|
|
192
|
+
"# 图表与报表的落盘目录,相对路径按「运行目录」解析(想要固定位置就填绝对路径)",
|
|
193
|
+
`TIMARAI_OUTPUT_DIR=${answers.TIMARAI_OUTPUT_DIR}`,
|
|
194
|
+
"",
|
|
195
|
+
"# 传输方式:stdio(本地 Agent,默认)| http(Streamable HTTP,团队共用)",
|
|
196
|
+
`MCP_TRANSPORT=${answers.MCP_TRANSPORT}`,
|
|
197
|
+
`MCP_HTTP_PORT=${answers.MCP_HTTP_PORT}`,
|
|
198
|
+
"",
|
|
199
|
+
].join("\n");
|
|
200
|
+
}
|
|
201
|
+
/** 用刚写入的配置真连一次,配错地址/凭证在这里就能暴露。 */
|
|
202
|
+
async function selfCheck(answers) {
|
|
203
|
+
process.stdout.write(`\n${C.bold}正在验证连通性…${C.reset}\n`);
|
|
204
|
+
for (const [k, v] of Object.entries(answers))
|
|
205
|
+
process.env[k] = v;
|
|
206
|
+
let cfg;
|
|
207
|
+
try {
|
|
208
|
+
cfg = loadConfig();
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
process.stdout.write(`${C.red}✗ 配置自检未通过:${String(error)}${C.reset}\n`);
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
const spec = getEndpointById("filters");
|
|
215
|
+
if (!spec)
|
|
216
|
+
return true;
|
|
217
|
+
try {
|
|
218
|
+
const client = new DashboardClient(cfg);
|
|
219
|
+
const result = await client.call(spec, { dashboardScope: "CEO" });
|
|
220
|
+
process.stdout.write(`${C.green}✓ 连通正常(${result.meta.elapsedMs}ms),已取到 CEO 舱筛选项${C.reset}\n`);
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
const detail = error.detail ?? {};
|
|
225
|
+
const code = detail.code;
|
|
226
|
+
const msg = String(error?.message ?? error);
|
|
227
|
+
process.stdout.write(`${C.red}✗ 连通失败${code ? `(业务码 ${code})` : ""}:${msg}${C.reset}\n`);
|
|
228
|
+
if (detail.hint)
|
|
229
|
+
process.stdout.write(`${C.dim}${detail.hint}${C.reset}\n`);
|
|
230
|
+
if (/fetch failed|ENOTFOUND|ECONNREFUSED|timeout/i.test(msg)) {
|
|
231
|
+
process.stdout.write(`${C.yellow}→ 这通常意味着地址不可达:确认域名/端口正确、服务已启动、本机可访问。${C.reset}\n`);
|
|
232
|
+
}
|
|
233
|
+
else if (code === "401") {
|
|
234
|
+
process.stdout.write(`${C.yellow}→ 地址通了但签名不通过:检查 ClientId 与 Secret 是否配对(Secret 需与平台登记的明文一致)。${C.reset}\n`);
|
|
235
|
+
}
|
|
236
|
+
else if (code === "403") {
|
|
237
|
+
process.stdout.write(`${C.yellow}→ 凭证有效但无权限:确认该 ClientId 已启用且具备 dashboard.read 权限。${C.reset}\n`);
|
|
238
|
+
}
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function printNextSteps(answers, ok) {
|
|
243
|
+
const clientJson = {
|
|
244
|
+
mcpServers: {
|
|
245
|
+
"timarai-dashboard": {
|
|
246
|
+
command: "node",
|
|
247
|
+
args: ["<仓库绝对路径>/dist/index.js"],
|
|
248
|
+
env: {
|
|
249
|
+
TIMARAI_API_BASE_URL: answers.TIMARAI_API_BASE_URL,
|
|
250
|
+
TIMARAI_PLATFORM_CLIENT_ID: answers.TIMARAI_PLATFORM_CLIENT_ID,
|
|
251
|
+
TIMARAI_PLATFORM_SECRET: "<明文密钥>",
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
process.stdout.write([
|
|
257
|
+
"",
|
|
258
|
+
`${C.bold}配置已写入 ${ENV_LABEL}${C.reset}`,
|
|
259
|
+
"",
|
|
260
|
+
`${C.bold}接下来${C.reset}`,
|
|
261
|
+
ok
|
|
262
|
+
? ` 1. 在你的 Agent 里加上这个 MCP 服务:`
|
|
263
|
+
: ` 1. ${C.yellow}先修掉上面的连通问题${C.reset},再把它接进 Agent:`,
|
|
264
|
+
"",
|
|
265
|
+
JSON.stringify(clientJson, null, 2)
|
|
266
|
+
.split("\n")
|
|
267
|
+
.map((l) => ` ${l}`)
|
|
268
|
+
.join("\n"),
|
|
269
|
+
"",
|
|
270
|
+
` 2. 也可以不写在客户端配置里——值已在 .env 中,进程会自动加载。`,
|
|
271
|
+
` 3. 若是 npm 全局安装 / npx 运行:command 写 "timarai-dashboard-mcp",args 留空 [];`,
|
|
272
|
+
` 但 .env 不会被带进全局包,凭证必须放在客户端配置的 env 里。`,
|
|
273
|
+
` 4. 想复查:npm run smoke(离线) / npm run live-check(联网)`,
|
|
274
|
+
` 5. 文档:docs/CAPABILITIES.md(能查什么、能出什么报表)`,
|
|
275
|
+
` 给别的机器装:docs/DISTRIBUTION.md(三条分发路线与客户端配置位置)`,
|
|
276
|
+
"",
|
|
277
|
+
].join("\n"));
|
|
278
|
+
}
|
|
279
|
+
async function main() {
|
|
280
|
+
if (!process.stdin.isTTY) {
|
|
281
|
+
process.stdout.write([
|
|
282
|
+
"本向导需要交互式终端。当前不是 TTY 环境,无法提问。",
|
|
283
|
+
"请改为手动创建 .env(可复制 .env.example 后填写),或在本机终端里重跑 npm run setup。",
|
|
284
|
+
].join("\n"));
|
|
285
|
+
process.exit(1);
|
|
286
|
+
}
|
|
287
|
+
banner();
|
|
288
|
+
const existing = readExisting();
|
|
289
|
+
if (existing.found) {
|
|
290
|
+
process.stdout.write([
|
|
291
|
+
`${C.yellow}检测到已存在的 .env:${C.reset}`,
|
|
292
|
+
` 地址: ${existing.values.TIMARAI_API_BASE_URL ?? "(未设置)"}`,
|
|
293
|
+
` ClientId:${existing.values.TIMARAI_PLATFORM_CLIENT_ID ?? "(未设置)"}`,
|
|
294
|
+
` Secret: ${maskSecret(existing.values.TIMARAI_PLATFORM_SECRET ?? "")}`,
|
|
295
|
+
`${C.dim}继续将覆盖它(旧值不再保留,Secret 无法找回,请确保手上有正确的明文密钥)。${C.reset}`,
|
|
296
|
+
"",
|
|
297
|
+
].join("\n"));
|
|
298
|
+
}
|
|
299
|
+
const rl = openPrompt();
|
|
300
|
+
const answers = {};
|
|
301
|
+
try {
|
|
302
|
+
answers.TIMARAI_API_BASE_URL = await requireUrl(rl, `${C.bold}①/⑤ 接口地址${C.reset}`, "驾驶舱 OpenAPI 的根地址,不含 /swagger、不含路径。无默认值,问平台管理员要(形如 https://<你的接口域名>)");
|
|
303
|
+
answers.TIMARAI_PLATFORM_CLIENT_ID = await requireNonEmpty(rl, `${C.bold}②/⑤ 平台 ClientId${C.reset}`, "平台数据字典里为该项 OpenAPI 登记的客户端标识。无默认值,问平台管理员要");
|
|
304
|
+
answers.TIMARAI_PLATFORM_SECRET = await requireSecret(rl, `${C.bold}③/⑤ 平台 Secret${C.reset}`, "与 ClientId 配对的明文密钥(至少 8 位)。输入不回显。");
|
|
305
|
+
process.stdout.write(`\n${C.dim}④/⑤ 以下使用默认值即可,想改就输入新值。${C.reset}\n`);
|
|
306
|
+
answers.TIMARAI_HTTP_TIMEOUT_MS = await askOptional(rl, "单次请求超时(毫秒)", "15000");
|
|
307
|
+
answers.TIMARAI_OUTPUT_DIR = await askOptional(rl, "图表/报表落盘目录", "out");
|
|
308
|
+
answers.TIMARAI_MAX_DATE_SPAN_DAYS = await askOptional(rl, "日期跨度上限(天)", "90");
|
|
309
|
+
answers.TIMARAI_MAX_RETRIES = await askOptional(rl, "失败重试次数", "1");
|
|
310
|
+
answers.TIMARAI_ALLOW_RAW_ENDPOINT = await askOptional(rl, "是否注册 call_endpoint 逃生阀", "true");
|
|
311
|
+
answers.TIMARAI_FILTER_CACHE_TTL_MS = await askOptional(rl, "筛选项缓存时长(毫秒)", "300000");
|
|
312
|
+
answers.MCP_TRANSPORT = await askOptional(rl, "传输方式(stdio / http)", "stdio");
|
|
313
|
+
answers.MCP_HTTP_PORT = await askOptional(rl, "HTTP 模式端口", "8899");
|
|
314
|
+
process.stdout.write([
|
|
315
|
+
"",
|
|
316
|
+
`${C.bold}⑤/⑤ 确认${C.reset}`,
|
|
317
|
+
` 地址: ${answers.TIMARAI_API_BASE_URL}`,
|
|
318
|
+
` ClientId:${answers.TIMARAI_PLATFORM_CLIENT_ID}`,
|
|
319
|
+
` Secret: ${maskSecret(answers.TIMARAI_PLATFORM_SECRET)}`,
|
|
320
|
+
"",
|
|
321
|
+
].join("\n"));
|
|
322
|
+
rl.close();
|
|
323
|
+
writeFileSync(ENV_FILE, renderEnv(answers), { encoding: "utf8", mode: 0o600 });
|
|
324
|
+
}
|
|
325
|
+
catch (error) {
|
|
326
|
+
rl.close();
|
|
327
|
+
process.stdout.write(`\n${C.red}${String(error instanceof Error ? error.message : error)}${C.reset}\n`);
|
|
328
|
+
process.exit(1);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
const ok = await selfCheck(answers);
|
|
332
|
+
printNextSteps(answers, ok);
|
|
333
|
+
process.exit(ok ? 0 : 1);
|
|
334
|
+
}
|
|
335
|
+
// 仅当作为命令直接运行时才询问。被 import(例如冒烟测试校验 renderEnv)时不能副作用式地
|
|
336
|
+
// 弹问题——那会把测试进程变成一次对话,或者直接 exit 掉。
|
|
337
|
+
const invokedDirectly = process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
338
|
+
if (invokedDirectly) {
|
|
339
|
+
main().catch((error) => {
|
|
340
|
+
process.stdout.write(`\n${C.red}向导异常终止:${String(error)}${C.reset}\n`);
|
|
341
|
+
process.exit(1);
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 平台 OpenAPI 的 HMAC-SHA256 签名实现。
|
|
3
|
+
*
|
|
4
|
+
* 契约规定的签名算法:
|
|
5
|
+
* payload = timestamp + requestId [+ rawBody]
|
|
6
|
+
* sign = Base64( HMAC-SHA256( payload, secret ) )
|
|
7
|
+
*
|
|
8
|
+
* 三个必须严格对齐的点:
|
|
9
|
+
* 1. timestamp 必须是 **Unix 毫秒**字符串,且与 `X-Platform-Timestamp` 头完全一致。
|
|
10
|
+
* 2. rawBody 必须是**实际发送的字节原文**,序列化后不得被二次格式化。
|
|
11
|
+
* 3. 无 body 时不拼接第三段。
|
|
12
|
+
*/
|
|
13
|
+
import { createHmac } from "node:crypto";
|
|
14
|
+
export function timestampMillis(now = new Date()) {
|
|
15
|
+
return String(Math.floor(now.getTime()));
|
|
16
|
+
}
|
|
17
|
+
export function buildCanonicalPayload(timestamp, requestId, rawBody) {
|
|
18
|
+
return rawBody === undefined ? timestamp + requestId : timestamp + requestId + rawBody;
|
|
19
|
+
}
|
|
20
|
+
export function signPayload(payload, secret) {
|
|
21
|
+
return createHmac("sha256", secret).update(payload, "utf8").digest("base64");
|
|
22
|
+
}
|
|
23
|
+
export function buildSignedHeaders(options) {
|
|
24
|
+
const canonicalPayload = buildCanonicalPayload(options.timestamp, options.requestId, options.rawBody);
|
|
25
|
+
return {
|
|
26
|
+
canonicalPayload,
|
|
27
|
+
headers: {
|
|
28
|
+
"X-Platform-Client-Id": options.clientId,
|
|
29
|
+
"X-Platform-Timestamp": options.timestamp,
|
|
30
|
+
"X-Platform-Request-Id": options.requestId,
|
|
31
|
+
"X-Platform-Sign": signPayload(canonicalPayload, options.secret),
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=signature.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signature.js","sourceRoot":"","sources":["../src/signature.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AASzC,MAAM,UAAU,eAAe,CAAC,MAAY,IAAI,IAAI,EAAE;IACpD,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,SAAiB,EACjB,SAAiB,EACjB,OAAgB;IAEhB,OAAO,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AACzF,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,MAAc;IACzD,OAAO,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC/E,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,OAOlC;IACC,MAAM,gBAAgB,GAAG,qBAAqB,CAC5C,OAAO,CAAC,SAAS,EACjB,OAAO,CAAC,SAAS,EACjB,OAAO,CAAC,OAAO,CAChB,CAAC;IACF,OAAO;QACL,gBAAgB;QAChB,OAAO,EAAE;YACP,sBAAsB,EAAE,OAAO,CAAC,QAAQ;YACxC,sBAAsB,EAAE,OAAO,CAAC,SAAS;YACzC,uBAAuB,EAAE,OAAO,CAAC,SAAS;YAC1C,iBAAiB,EAAE,WAAW,CAAC,gBAAgB,EAAE,OAAO,CAAC,MAAM,CAAC;SACjE;KACF,CAAC;AACJ,CAAC"}
|