tyc-cli 0.3.8 → 0.3.9
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 +9 -0
- package/README.md +5 -1
- package/dist/catalog.json +332 -170
- package/dist/commands/capabilities.js +1 -2
- package/dist/commands/category.js +2 -3
- package/dist/commands/init.js +1 -3
- package/dist/commands/login.js +85 -88
- package/dist/commands/output.js +2 -4
- package/dist/config.js +1 -2
- package/dist/index.js +10 -2
- package/dist/oauth.js +35 -25
- package/dist/registry.js +1 -1
- package/dist/utils/history.js +68 -0
- package/dist/utils/jsonToMarkdown.js +134 -26
- package/dist/utils/personnelChanges.js +74 -0
- package/package.json +1 -1
|
@@ -25,8 +25,7 @@ export function registerCompanyCapabilitiesCommand(companyCmd, program) {
|
|
|
25
25
|
}
|
|
26
26
|
catch (err) {
|
|
27
27
|
const msg = err instanceof Error ? err.message : String(err);
|
|
28
|
-
|
|
29
|
-
process.exit(1);
|
|
28
|
+
throw new Error(`请求失败: ${msg}`, { cause: err });
|
|
30
29
|
}
|
|
31
30
|
});
|
|
32
31
|
}
|
|
@@ -36,7 +36,7 @@ function bindMethod(catCmd, tool, program) {
|
|
|
36
36
|
// 都能直观看到分层信息;徽章不影响 Agent 对 description 的语义理解。
|
|
37
37
|
let methodCmd = catCmd
|
|
38
38
|
.command(tool.cliMethod)
|
|
39
|
-
.description(`[${tool.layer}] ${tool.description}
|
|
39
|
+
.description(`[${tool.layer}] ${tool.description} 调用额度:成功且返回有效结果计 1 次;报错、全部无结果和系统异常计 0 次。`);
|
|
40
40
|
if (positional) {
|
|
41
41
|
methodCmd = methodCmd.argument(`<${positional.name}>`, positional.description);
|
|
42
42
|
}
|
|
@@ -63,8 +63,7 @@ function bindMethod(catCmd, tool, program) {
|
|
|
63
63
|
}
|
|
64
64
|
catch (err) {
|
|
65
65
|
const msg = err instanceof Error ? err.message : String(err);
|
|
66
|
-
|
|
67
|
-
process.exit(1);
|
|
66
|
+
throw new Error(`请求失败: ${msg}`, { cause: err });
|
|
68
67
|
}
|
|
69
68
|
});
|
|
70
69
|
}
|
package/dist/commands/init.js
CHANGED
|
@@ -91,9 +91,7 @@ export function registerInitCommand(program) {
|
|
|
91
91
|
}
|
|
92
92
|
catch (err) {
|
|
93
93
|
const msg = err instanceof Error ? err.message : String(err);
|
|
94
|
-
|
|
95
|
-
console.error("配置已保存;确认 --url 可达且 Authorization 有效后重新运行 tyc init,或加 --no-verify 跳过校验。");
|
|
96
|
-
process.exit(1);
|
|
94
|
+
throw new Error(`配置校验失败:${msg}\n配置已保存;确认 --url 可达且 Authorization 有效后重新运行 tyc init,或加 --no-verify 跳过校验。`, { cause: err });
|
|
97
95
|
}
|
|
98
96
|
});
|
|
99
97
|
}
|
package/dist/commands/login.js
CHANGED
|
@@ -275,17 +275,15 @@ export function registerLoginCommand(program) {
|
|
|
275
275
|
state,
|
|
276
276
|
timeoutMs: 5 * 60 * 1000,
|
|
277
277
|
})).redirectUri;
|
|
278
|
-
|
|
279
|
-
if (!
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
try {
|
|
288
|
-
registeredClient = await registerDynamicOAuthClient({
|
|
278
|
+
try {
|
|
279
|
+
if (!clientId) {
|
|
280
|
+
if (!endpoints.registrationEndpoint) {
|
|
281
|
+
throw new Error("OAuth metadata 未提供 registration_endpoint,请用 --client-id 指定预注册客户端。");
|
|
282
|
+
}
|
|
283
|
+
if (verbose) {
|
|
284
|
+
console.error("> registering OAuth client via Dynamic Client Registration");
|
|
285
|
+
}
|
|
286
|
+
const registeredClient = await registerDynamicOAuthClient({
|
|
289
287
|
registrationEndpoint: endpoints.registrationEndpoint,
|
|
290
288
|
clientName: "tyc-cli",
|
|
291
289
|
redirectUris: [redirectUri],
|
|
@@ -295,97 +293,96 @@ export function registerLoginCommand(program) {
|
|
|
295
293
|
responseTypes: ["code"],
|
|
296
294
|
tokenEndpointAuthMethod: "none",
|
|
297
295
|
});
|
|
296
|
+
clientId = registeredClient.clientId;
|
|
297
|
+
clientSecret ||= registeredClient.clientSecret;
|
|
298
|
+
if (verbose) {
|
|
299
|
+
console.error(`> registered client_id: ${clientId}`);
|
|
300
|
+
}
|
|
298
301
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
+
if (noBlock) {
|
|
303
|
+
if (!endpoints.deviceAuthorizationEndpoint) {
|
|
304
|
+
throw new Error("OAuth metadata 未提供 device_authorization_endpoint,请升级授权服务或使用 --device-authorization-endpoint 指定。");
|
|
305
|
+
}
|
|
306
|
+
const device = await requestDeviceAuthorization({
|
|
307
|
+
deviceAuthorizationEndpoint: endpoints.deviceAuthorizationEndpoint,
|
|
308
|
+
clientId,
|
|
309
|
+
clientSecret,
|
|
310
|
+
scope,
|
|
311
|
+
resource,
|
|
312
|
+
});
|
|
313
|
+
savePendingOAuthLogin({
|
|
314
|
+
version: 2,
|
|
315
|
+
flow: "device",
|
|
316
|
+
createdAt: Date.now(),
|
|
317
|
+
mcpURL,
|
|
318
|
+
tokenEndpoint: endpoints.tokenEndpoint,
|
|
319
|
+
clientId,
|
|
320
|
+
clientSecret,
|
|
321
|
+
deviceCode: device.device_code,
|
|
322
|
+
userCode: device.user_code,
|
|
323
|
+
verificationUri: device.verification_uri,
|
|
324
|
+
expiresAt: Date.now() + device.expires_in * 1000,
|
|
325
|
+
interval: device.interval || 5,
|
|
326
|
+
resource,
|
|
327
|
+
scope,
|
|
328
|
+
verify: opts.verify !== false,
|
|
329
|
+
});
|
|
330
|
+
console.error("请在浏览器打开以下 URL 完成登录:");
|
|
331
|
+
console.error(device.verification_uri);
|
|
332
|
+
console.error("");
|
|
333
|
+
console.error(`验证码:${device.user_code}`);
|
|
334
|
+
console.error("");
|
|
335
|
+
console.error("在网页输入 6 位验证码并确认授权后,运行:");
|
|
336
|
+
console.error("tyc login --resume");
|
|
337
|
+
return;
|
|
302
338
|
}
|
|
303
|
-
|
|
304
|
-
|
|
339
|
+
const authorizationURL = buildAuthorizationURL({
|
|
340
|
+
authorizationEndpoint: endpoints.authorizationEndpoint,
|
|
341
|
+
clientId,
|
|
342
|
+
redirectUri,
|
|
343
|
+
scope,
|
|
344
|
+
state,
|
|
345
|
+
codeChallenge: pkce.codeChallenge,
|
|
346
|
+
resource,
|
|
347
|
+
});
|
|
305
348
|
if (verbose) {
|
|
306
|
-
console.error(`>
|
|
349
|
+
console.error(`> OAuth callback: ${redirectUri}`);
|
|
307
350
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
351
|
+
if (opts.open === false) {
|
|
352
|
+
console.error("请在浏览器打开以下 URL 完成登录:");
|
|
353
|
+
console.error(authorizationURL);
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
console.error("正在打开浏览器完成 OAuth 登录...");
|
|
357
|
+
openBrowser(authorizationURL);
|
|
358
|
+
}
|
|
359
|
+
if (!callback) {
|
|
360
|
+
throw new Error("OAuth callback server not initialized");
|
|
361
|
+
}
|
|
362
|
+
const code = await callback.waitForCode();
|
|
363
|
+
if (verbose) {
|
|
364
|
+
console.error("> authorization code received; exchanging access token");
|
|
312
365
|
}
|
|
313
|
-
const
|
|
314
|
-
|
|
366
|
+
const token = await exchangeAuthorizationCode({
|
|
367
|
+
tokenEndpoint: endpoints.tokenEndpoint,
|
|
315
368
|
clientId,
|
|
316
369
|
clientSecret,
|
|
317
|
-
|
|
370
|
+
code,
|
|
371
|
+
redirectUri,
|
|
372
|
+
codeVerifier: pkce.codeVerifier,
|
|
318
373
|
resource,
|
|
319
374
|
});
|
|
320
|
-
|
|
321
|
-
version: 2,
|
|
322
|
-
flow: "device",
|
|
323
|
-
createdAt: Date.now(),
|
|
324
|
-
mcpURL,
|
|
375
|
+
saveOAuthTokenConfig(mcpURL, token, {
|
|
325
376
|
tokenEndpoint: endpoints.tokenEndpoint,
|
|
326
377
|
clientId,
|
|
327
378
|
clientSecret,
|
|
328
|
-
deviceCode: device.device_code,
|
|
329
|
-
userCode: device.user_code,
|
|
330
|
-
verificationUri: device.verification_uri,
|
|
331
|
-
expiresAt: Date.now() + device.expires_in * 1000,
|
|
332
|
-
interval: device.interval || 5,
|
|
333
379
|
resource,
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
console.error("请在浏览器打开以下 URL 完成登录:");
|
|
338
|
-
console.error(device.verification_uri);
|
|
339
|
-
console.error("");
|
|
340
|
-
console.error(`验证码:${device.user_code}`);
|
|
341
|
-
console.error("");
|
|
342
|
-
console.error("在网页输入 6 位验证码并确认授权后,运行:");
|
|
343
|
-
console.error("tyc login --resume");
|
|
344
|
-
return;
|
|
345
|
-
}
|
|
346
|
-
const authorizationURL = buildAuthorizationURL({
|
|
347
|
-
authorizationEndpoint: endpoints.authorizationEndpoint,
|
|
348
|
-
clientId,
|
|
349
|
-
redirectUri,
|
|
350
|
-
scope,
|
|
351
|
-
state,
|
|
352
|
-
codeChallenge: pkce.codeChallenge,
|
|
353
|
-
resource,
|
|
354
|
-
});
|
|
355
|
-
if (verbose) {
|
|
356
|
-
console.error(`> OAuth callback: ${redirectUri}`);
|
|
357
|
-
}
|
|
358
|
-
if (opts.open === false) {
|
|
359
|
-
console.error("请在浏览器打开以下 URL 完成登录:");
|
|
360
|
-
console.error(authorizationURL);
|
|
361
|
-
}
|
|
362
|
-
else {
|
|
363
|
-
console.error("正在打开浏览器完成 OAuth 登录...");
|
|
364
|
-
openBrowser(authorizationURL);
|
|
380
|
+
}, verbose);
|
|
381
|
+
clearPendingOAuthLogin(verbose);
|
|
382
|
+
await verifySavedOAuthLogin(opts.verify !== false, verbose);
|
|
365
383
|
}
|
|
366
|
-
|
|
367
|
-
|
|
384
|
+
finally {
|
|
385
|
+
callback?.close();
|
|
368
386
|
}
|
|
369
|
-
const code = await callback.waitForCode();
|
|
370
|
-
if (verbose) {
|
|
371
|
-
console.error("> authorization code received; exchanging access token");
|
|
372
|
-
}
|
|
373
|
-
const token = await exchangeAuthorizationCode({
|
|
374
|
-
tokenEndpoint: endpoints.tokenEndpoint,
|
|
375
|
-
clientId,
|
|
376
|
-
clientSecret,
|
|
377
|
-
code,
|
|
378
|
-
redirectUri,
|
|
379
|
-
codeVerifier: pkce.codeVerifier,
|
|
380
|
-
resource,
|
|
381
|
-
});
|
|
382
|
-
saveOAuthTokenConfig(mcpURL, token, {
|
|
383
|
-
tokenEndpoint: endpoints.tokenEndpoint,
|
|
384
|
-
clientId,
|
|
385
|
-
clientSecret,
|
|
386
|
-
resource,
|
|
387
|
-
}, verbose);
|
|
388
|
-
clearPendingOAuthLogin(verbose);
|
|
389
|
-
await verifySavedOAuthLogin(opts.verify !== false, verbose);
|
|
390
387
|
});
|
|
391
388
|
}
|
package/dist/commands/output.js
CHANGED
|
@@ -3,13 +3,11 @@ import { applyTruncation, resolveOptionalInt, TRUNCATE_DEFAULTS, } from "../util
|
|
|
3
3
|
export function emitToolResult(result, opts, toolName) {
|
|
4
4
|
if (result.isError) {
|
|
5
5
|
const errText = extractText(result) || "未知业务错误";
|
|
6
|
-
|
|
7
|
-
process.exit(1);
|
|
6
|
+
throw new Error(errText);
|
|
8
7
|
}
|
|
9
8
|
const text = extractText(result);
|
|
10
9
|
if (text === null) {
|
|
11
|
-
|
|
12
|
-
process.exit(1);
|
|
10
|
+
throw new Error("tools/call 返回无可解析内容");
|
|
13
11
|
}
|
|
14
12
|
let payload;
|
|
15
13
|
try {
|
package/dist/config.js
CHANGED
|
@@ -109,8 +109,7 @@ export function resolveConfig() {
|
|
|
109
109
|
headers.Authorization = process.env.TYC_AUTHORIZATION;
|
|
110
110
|
}
|
|
111
111
|
if (!headers.Authorization) {
|
|
112
|
-
|
|
113
|
-
process.exit(1);
|
|
112
|
+
throw new Error("未配置 Authorization。请先运行:tyc init --authorization YOUR_API_KEY");
|
|
114
113
|
}
|
|
115
114
|
return { url, coreUrl, headers, oauth: cfg.oauth ? { ...cfg.oauth } : undefined };
|
|
116
115
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { Command } from "commander";
|
|
2
|
+
import { Command, CommanderError } from "commander";
|
|
3
3
|
import { registerInitCommand } from "./commands/init.js";
|
|
4
4
|
import { registerLoginCommand } from "./commands/login.js";
|
|
5
5
|
import { registerCategoryCommands } from "./commands/category.js";
|
|
@@ -93,6 +93,7 @@ function renderPriorityLines() {
|
|
|
93
93
|
return lines.join("\n");
|
|
94
94
|
}
|
|
95
95
|
const program = new Command()
|
|
96
|
+
.exitOverride()
|
|
96
97
|
.name("tyc")
|
|
97
98
|
.description(`天眼查 AI CLI:${getTotalCount()} 个商业数据工具 · L0=${getLayerCount("L0")} L1=${getLayerCount("L1")} L2=${getLayerCount("L2")} L3=${getLayerCount("L3")} · 面向 Agent 的 MCP 工具网关`)
|
|
98
99
|
.version(VERSION, "-V, --version", "输出版本号")
|
|
@@ -125,7 +126,14 @@ registerLoginCommand(program);
|
|
|
125
126
|
registerLayerCommands(program);
|
|
126
127
|
registerCategoryCommands(program);
|
|
127
128
|
program.parseAsync(process.argv).catch((err) => {
|
|
129
|
+
// Commander has already printed help/version/argument errors. Preserve its
|
|
130
|
+
// exit status without printing twice or interrupting pending stream writes.
|
|
131
|
+
if (err instanceof CommanderError) {
|
|
132
|
+
process.exitCode = err.exitCode;
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
128
135
|
const msg = err instanceof Error ? err.message : String(err);
|
|
129
136
|
console.error(msg);
|
|
130
|
-
|
|
137
|
+
// Let Node finish asynchronous I/O and teardown (including fetch on Windows).
|
|
138
|
+
process.exitCode = 1;
|
|
131
139
|
});
|
package/dist/oauth.js
CHANGED
|
@@ -208,13 +208,20 @@ export async function startCallbackServer(opts) {
|
|
|
208
208
|
let server;
|
|
209
209
|
let settled = false;
|
|
210
210
|
let timer;
|
|
211
|
+
const close = () => {
|
|
212
|
+
settled = true;
|
|
213
|
+
if (timer)
|
|
214
|
+
clearTimeout(timer);
|
|
215
|
+
server?.close();
|
|
216
|
+
server?.closeIdleConnections?.();
|
|
217
|
+
};
|
|
211
218
|
const codePromise = new Promise((resolve, reject) => {
|
|
212
219
|
timer = setTimeout(() => {
|
|
213
220
|
if (settled)
|
|
214
221
|
return;
|
|
215
222
|
settled = true;
|
|
216
223
|
reject(new Error("等待 OAuth 回调超时"));
|
|
217
|
-
|
|
224
|
+
close();
|
|
218
225
|
}, opts.timeoutMs);
|
|
219
226
|
server = createServer((req, res) => {
|
|
220
227
|
const url = new URL(req.url || "/", `http://${req.headers.host || opts.host}`);
|
|
@@ -237,7 +244,7 @@ export async function startCallbackServer(opts) {
|
|
|
237
244
|
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
|
|
238
245
|
res.end(callbackHTML("OAuth 登录失败", "state 校验失败,请回到终端重新运行 tyc login。"));
|
|
239
246
|
reject(new Error("OAuth state mismatch"));
|
|
240
|
-
|
|
247
|
+
close();
|
|
241
248
|
return;
|
|
242
249
|
}
|
|
243
250
|
if (error) {
|
|
@@ -245,7 +252,7 @@ export async function startCallbackServer(opts) {
|
|
|
245
252
|
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
|
|
246
253
|
res.end(callbackHTML("OAuth 登录失败", errorDescription || error));
|
|
247
254
|
reject(new Error(`OAuth error: ${errorDescription || error}`));
|
|
248
|
-
|
|
255
|
+
close();
|
|
249
256
|
return;
|
|
250
257
|
}
|
|
251
258
|
if (!code) {
|
|
@@ -253,49 +260,52 @@ export async function startCallbackServer(opts) {
|
|
|
253
260
|
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
|
|
254
261
|
res.end(callbackHTML("OAuth 登录失败", "授权服务未返回 code。"));
|
|
255
262
|
reject(new Error("OAuth callback missing code"));
|
|
256
|
-
|
|
263
|
+
close();
|
|
257
264
|
return;
|
|
258
265
|
}
|
|
259
266
|
settled = true;
|
|
260
267
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
261
268
|
res.end(callbackHTML("OAuth 登录成功", "tyc CLI 已收到授权结果,可以关闭这个窗口。"));
|
|
262
269
|
resolve(code);
|
|
263
|
-
|
|
270
|
+
close();
|
|
264
271
|
});
|
|
265
272
|
});
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
273
|
+
// Registration/browser setup happens before waitForCode(). Keep an early
|
|
274
|
+
// callback rejection handled while preserving it for that eventual await.
|
|
275
|
+
void codePromise.catch(() => { });
|
|
276
|
+
try {
|
|
277
|
+
await new Promise((resolve, reject) => {
|
|
278
|
+
if (!server) {
|
|
279
|
+
reject(new Error("callback server not initialized"));
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
const onError = (err) => reject(err);
|
|
283
|
+
server.once("error", onError);
|
|
284
|
+
server.listen(opts.port, opts.host, () => {
|
|
285
|
+
server?.off("error", onError);
|
|
286
|
+
resolve();
|
|
287
|
+
});
|
|
276
288
|
});
|
|
277
|
-
}
|
|
289
|
+
}
|
|
290
|
+
catch (err) {
|
|
291
|
+
close();
|
|
292
|
+
throw err;
|
|
293
|
+
}
|
|
278
294
|
const address = server?.address();
|
|
279
295
|
if (!address || typeof address.port !== "number") {
|
|
280
|
-
|
|
296
|
+
close();
|
|
281
297
|
throw new Error("无法启动 OAuth 本地回调服务");
|
|
282
298
|
}
|
|
283
299
|
const redirectUri = `http://${opts.host}:${address.port}${opts.path}`;
|
|
284
300
|
return {
|
|
285
301
|
redirectUri,
|
|
286
|
-
close
|
|
287
|
-
if (timer)
|
|
288
|
-
clearTimeout(timer);
|
|
289
|
-
server?.close();
|
|
290
|
-
},
|
|
302
|
+
close,
|
|
291
303
|
waitForCode: async () => {
|
|
292
304
|
try {
|
|
293
305
|
return await codePromise;
|
|
294
306
|
}
|
|
295
307
|
finally {
|
|
296
|
-
|
|
297
|
-
clearTimeout(timer);
|
|
298
|
-
server?.close();
|
|
308
|
+
close();
|
|
299
309
|
}
|
|
300
310
|
},
|
|
301
311
|
};
|
package/dist/registry.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// 从 src/catalog.json 加载命令树元数据
|
|
2
2
|
//
|
|
3
3
|
// catalog.json 由 apimcp 主仓 scripts/gen_cli_catalog 生成;仅包含 CLI
|
|
4
|
-
// 需要的字段(name / group / layer / cliMethod / categoryNameZh /
|
|
4
|
+
// 需要的字段(name / group / layer / requiredPlan / cliMethod / categoryNameZh /
|
|
5
5
|
// description / params)。不含 sources / execution / summary 等服务端
|
|
6
6
|
// 内部映射,服务端实现细节不泄露。
|
|
7
7
|
import { readFileSync } from "node:fs";
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const object = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
2
|
+
const text = (v) => typeof v === "string" ? v : JSON.stringify(v) ?? "null";
|
|
3
|
+
const cell = (v) => text(v).replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
|
|
4
|
+
export function renderHistoryGuide(value) {
|
|
5
|
+
if (!object(value))
|
|
6
|
+
return [];
|
|
7
|
+
const lines = [`> 查询范围:${cell(value.scope)}`];
|
|
8
|
+
if (Array.isArray(value.nextTools))
|
|
9
|
+
for (const next of value.nextTools) {
|
|
10
|
+
if (object(next))
|
|
11
|
+
lines.push(`> 后续查询:\`${cell(next.toolName)}\` — ${cell(next.purpose)}`);
|
|
12
|
+
}
|
|
13
|
+
if (Array.isArray(value.notes))
|
|
14
|
+
for (const note of value.notes)
|
|
15
|
+
lines.push(`> 历史查询说明:${cell(note)}`);
|
|
16
|
+
return lines;
|
|
17
|
+
}
|
|
18
|
+
export function splitHistoricalPersonnel(obj, toolName) {
|
|
19
|
+
if (toolName !== "get_historical_registration" || !object(obj.sources) || !object(obj.sources.hi))
|
|
20
|
+
return undefined;
|
|
21
|
+
const hi = { ...obj.sources.hi };
|
|
22
|
+
const lines = [];
|
|
23
|
+
for (const [key, title] of [["pastLegalPersonList", "历史法定代表人"], ["pastStafferList", "历史主要人员"]]) {
|
|
24
|
+
if (!(key in hi))
|
|
25
|
+
continue;
|
|
26
|
+
const value = hi[key];
|
|
27
|
+
delete hi[key];
|
|
28
|
+
lines.push(`### ${title}\n`, "> 按来源分组完整展示;组号不代表任职阶段。\n");
|
|
29
|
+
let groups = Array.isArray(value) ? value : [value];
|
|
30
|
+
if (groups.length === 0)
|
|
31
|
+
lines.push("_(空列表)_\n");
|
|
32
|
+
if (groups.length && groups.every(object)) {
|
|
33
|
+
lines.push("单层名单:\n");
|
|
34
|
+
groups = [groups];
|
|
35
|
+
}
|
|
36
|
+
groups.forEach((group, i) => {
|
|
37
|
+
const people = Array.isArray(group) ? group : [group];
|
|
38
|
+
lines.push(`#### 第 ${i + 1} 组(${people.length} 项)\n`);
|
|
39
|
+
people.forEach((person, j) => {
|
|
40
|
+
lines.push(`人员 ${j + 1}:\n`, "| 字段 | 值 |", "|---|---|");
|
|
41
|
+
if (object(person))
|
|
42
|
+
for (const field of Object.keys(person).sort()) {
|
|
43
|
+
const label = field === "name" ? "名称" : field === "time" ? "变更时间" : field;
|
|
44
|
+
lines.push(`| ${cell(label)} | ${cell(person[field])} |`);
|
|
45
|
+
}
|
|
46
|
+
else
|
|
47
|
+
lines.push(`| 原始项 | ${cell(person)} |`);
|
|
48
|
+
lines.push("");
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return lines.length ? { rest: { ...obj, sources: { ...obj.sources, hi } }, markdown: lines.join("\n") } : undefined;
|
|
53
|
+
}
|
|
54
|
+
function changeDate(value) {
|
|
55
|
+
if (!object(value) || typeof value.changeTime !== "string")
|
|
56
|
+
return "";
|
|
57
|
+
const match = /^(\d{4}-\d{2}-\d{2})(?:$|[ T])/.exec(value.changeTime);
|
|
58
|
+
if (!match)
|
|
59
|
+
return "";
|
|
60
|
+
const day = new Date(`${match[1]}T00:00:00Z`);
|
|
61
|
+
return !Number.isNaN(day.getTime()) && day.toISOString().slice(0, 10) === match[1] ? match[1] : "";
|
|
62
|
+
}
|
|
63
|
+
export function changeRecordsForDisplay(records) {
|
|
64
|
+
return [...records].sort((a, b) => {
|
|
65
|
+
const x = changeDate(a), y = changeDate(b);
|
|
66
|
+
return x < y ? 1 : x > y ? -1 : 0;
|
|
67
|
+
});
|
|
68
|
+
}
|