tyc-cli 0.1.1 → 0.2.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.
@@ -1,137 +0,0 @@
1
- // Aggregator (TS 版本)
2
- // 对齐 tools/t1_1/aggregator/aggregator.go 的语义:
3
- // - parallel: 并发调用全部 sources,required 失败抛错,非 required 失败记 warnings
4
- // - serial: 前一步结果注入后一步 params 上下文({{.scope.result.path}})
5
- // - condition: Go text/template 风格;当前仅支持 {{ne .X ""}} 形式(足够覆盖 registry)
6
- // - params_template: 字符串模板求值,空值不传
7
- import { callApi } from "./client.js";
8
- /** 简化版 Go text/template 字符串求值:
9
- * 支持 `{{.X}}` 和嵌套 `{{.a.b.c}}`;未匹配字段返回空串。
10
- */
11
- export function renderString(tpl, ctx) {
12
- return tpl.replace(/\{\{\s*\.([\w.[\]0-9]+)\s*\}\}/g, (_m, path) => {
13
- const v = getPath(ctx, path);
14
- return v === undefined || v === null ? "" : String(v);
15
- });
16
- }
17
- /** condition 求值:只支持 `{{ne .X ""}}` 的简化语法 → true/false */
18
- export function evalCondition(cond, ctx) {
19
- if (!cond)
20
- return true;
21
- const m = cond.match(/\{\{\s*ne\s+\.(\S+)\s+"([^"]*)"\s*\}\}/);
22
- if (m) {
23
- const v = getPath(ctx, m[1]);
24
- return String(v ?? "") !== m[2];
25
- }
26
- // 其他未识别条件保守放行
27
- return true;
28
- }
29
- function getPath(obj, path) {
30
- const parts = path.split(/[.[\]]+/).filter(Boolean);
31
- let cur = obj;
32
- for (const p of parts) {
33
- if (cur === null || cur === undefined)
34
- return undefined;
35
- if (Array.isArray(cur)) {
36
- const i = Number(p);
37
- if (Number.isNaN(i))
38
- return undefined;
39
- cur = cur[i];
40
- }
41
- else if (typeof cur === "object") {
42
- cur = cur[p];
43
- }
44
- else {
45
- return undefined;
46
- }
47
- }
48
- return cur;
49
- }
50
- function renderParams(tpl, ctx) {
51
- const out = {};
52
- if (!tpl)
53
- return out;
54
- for (const [k, v] of Object.entries(tpl)) {
55
- const s = renderString(v, ctx);
56
- if (s !== "")
57
- out[k] = s;
58
- }
59
- return out;
60
- }
61
- async function callSource(src, ctx, auth, verbose) {
62
- const params = renderParams(src.params_template, ctx);
63
- const resp = await callApi(src.path, params, auth, { verbose });
64
- // tyc 错误码语义归一化:
65
- // 0 = 成功
66
- // 300000 = 经查无结果 → 业务空结果,返回 {}(不报错)
67
- // 其他非 0 = 真实错误 → 抛错
68
- if (resp.error_code === 300000) {
69
- // 归一化为列表类空结果,便于 transformer 注入 _empty/_summary
70
- return { items: [], total: 0 };
71
- }
72
- if (resp.error_code && resp.error_code !== 0) {
73
- throw new Error(`OpenAPI ${resp.error_code}: ${resp.reason ?? ""}`);
74
- }
75
- const result = resp.result;
76
- if (result === null || result === undefined)
77
- return {};
78
- if (typeof result === "object" && !Array.isArray(result)) {
79
- return result;
80
- }
81
- if (Array.isArray(result)) {
82
- return { items: result, total: result.length };
83
- }
84
- return { _raw: result };
85
- }
86
- export async function runTool(tool, args, auth, opts) {
87
- const verbose = !!opts?.verbose;
88
- const exec = tool.execution || "parallel";
89
- if (exec === "serial") {
90
- return runSerial(tool, args, auth, verbose);
91
- }
92
- return runParallel(tool, args, auth, verbose);
93
- }
94
- async function runParallel(tool, args, auth, verbose) {
95
- const ctx = { ...args };
96
- const results = {};
97
- const warnings = {};
98
- await Promise.all(tool.sources.map(async (src) => {
99
- try {
100
- if (!evalCondition(src.condition || "", ctx))
101
- return;
102
- const r = await callSource(src, ctx, auth, verbose);
103
- if (r !== null)
104
- results[src.scope] = r;
105
- }
106
- catch (err) {
107
- const msg = err instanceof Error ? err.message : String(err);
108
- if (src.required)
109
- throw new Error(`required source ${src.path} failed: ${msg}`);
110
- warnings[src.path] = msg;
111
- }
112
- }));
113
- return { results, warnings };
114
- }
115
- async function runSerial(tool, args, auth, verbose) {
116
- const ctx = { ...args };
117
- const results = {};
118
- const warnings = {};
119
- for (const src of tool.sources) {
120
- try {
121
- if (!evalCondition(src.condition || "", ctx))
122
- continue;
123
- const r = await callSource(src, ctx, auth, verbose);
124
- if (r !== null) {
125
- results[src.scope] = r;
126
- ctx[src.scope] = { result: r };
127
- }
128
- }
129
- catch (err) {
130
- const msg = err instanceof Error ? err.message : String(err);
131
- if (src.required)
132
- throw new Error(`required source ${src.path} failed: ${msg}`);
133
- warnings[src.path] = msg;
134
- }
135
- }
136
- return { results, warnings };
137
- }
package/dist/client.js DELETED
@@ -1,29 +0,0 @@
1
- // tyc OpenAPI HTTP 客户端逻辑。
2
- const DOMAIN = "https://open.api.tianyancha.com";
3
- const BASE_PATH = "/services/open";
4
- export async function callApi(path, params, authorization, options) {
5
- let base;
6
- if (path.startsWith("/")) {
7
- base = DOMAIN + path;
8
- }
9
- else {
10
- base = DOMAIN + BASE_PATH + "/" + path;
11
- }
12
- const qs = new URLSearchParams(params).toString();
13
- const url = qs ? `${base}?${qs}` : base;
14
- const headers = { Authorization: authorization };
15
- if (options?.verbose) {
16
- console.error(`> GET ${url}`);
17
- console.error(`> Authorization: ${maskToken(authorization)}`);
18
- }
19
- const resp = await fetch(url, { method: "GET", headers });
20
- if (!resp.ok) {
21
- throw new Error(`HTTP ${resp.status}: ${resp.statusText}`);
22
- }
23
- return (await resp.json());
24
- }
25
- function maskToken(token) {
26
- if (token.length <= 8)
27
- return "****";
28
- return token.slice(0, 4) + "****" + token.slice(-4);
29
- }
@@ -1,133 +0,0 @@
1
- // Transformer (TS 版本)
2
- // 对齐 tools/t1_1/transformer/transformer.go:
3
- // 1. 按 sources 声明顺序做顶层 map 覆盖合并
4
- // 2. 时间戳字段值格式化为 Asia/Shanghai 字符串(key 不变)
5
- // 3. 注入 _summary / _warnings / _empty
6
- const TIMESTAMP_FIELDS = new Set([
7
- "estiblishTime", "approvedTime", "fromTime", "toTime", "updateTimes",
8
- "cancelDate", "revokeDate",
9
- "changeTime", "createTime", "subscriptionTime", "paidinTime", "ftShareholding",
10
- "publishDate", "publishTime", "judgeTime", "submitTime", "submittime",
11
- "caseRegTime", "caseCreateTime", "filingDate", "endCaseDate",
12
- "decisionDate", "startDate",
13
- "appDate", "regDate", "rtm", "eventTime", "pubDate",
14
- "applicationTime", "regtime", "publishtime",
15
- "releaseTime", "grantDate", "applicationPublishTime",
16
- "freezeStartDate", "freezeEndDate", "expiryDate", "issueDate",
17
- "recordDate", "establishDate", "joinDate",
18
- "investTime", "time", "pubTime",
19
- "reportDate", "listingDate", "validFrom", "validTo",
20
- "occurDate", "checkDate", "startdate", "bidWinDate",
21
- "noticePeriodStart", "noticePeriodEnd", "evaluationDate",
22
- "occurTime", "finishDate", "firstPublishDate", "ratingTime",
23
- "examineDate",
24
- ]);
25
- const DATETIME_FIELDS = new Set([
26
- "updateTimes", "rtm", "pubTime", "eventTime", "startTime",
27
- ]);
28
- function pad(n) {
29
- return n < 10 ? "0" + n : String(n);
30
- }
31
- function formatMs(ms, dateTime) {
32
- if (!ms || ms <= 0)
33
- return "";
34
- // 转换到 Asia/Shanghai(UTC+8)
35
- const shanghai = new Date(ms + 8 * 3600 * 1000);
36
- const y = shanghai.getUTCFullYear();
37
- const mo = pad(shanghai.getUTCMonth() + 1);
38
- const d = pad(shanghai.getUTCDate());
39
- if (!dateTime)
40
- return `${y}-${mo}-${d}`;
41
- const h = pad(shanghai.getUTCHours());
42
- const mi = pad(shanghai.getUTCMinutes());
43
- const s = pad(shanghai.getUTCSeconds());
44
- return `${y}-${mo}-${d} ${h}:${mi}:${s}`;
45
- }
46
- function formatTimestampsInPlace(v) {
47
- if (v === null || v === undefined)
48
- return;
49
- if (Array.isArray(v)) {
50
- for (const item of v)
51
- formatTimestampsInPlace(item);
52
- return;
53
- }
54
- if (typeof v === "object") {
55
- const obj = v;
56
- for (const [k, val] of Object.entries(obj)) {
57
- if (TIMESTAMP_FIELDS.has(k) && typeof val === "number" && val > 0) {
58
- obj[k] = formatMs(val, DATETIME_FIELDS.has(k));
59
- continue;
60
- }
61
- if (TIMESTAMP_FIELDS.has(k) && typeof val === "string" && /^\d{10,13}$/.test(val)) {
62
- const n = Number(val);
63
- obj[k] = formatMs(n, DATETIME_FIELDS.has(k));
64
- continue;
65
- }
66
- formatTimestampsInPlace(val);
67
- }
68
- }
69
- }
70
- function summaryCount(count, label) {
71
- return `该查询实体共有${count}条${label}记录。`;
72
- }
73
- function summaryCountTruncated(total, returned, label) {
74
- return `该查询实体共有${total}条${label}记录。已为您展示前${returned}条。`;
75
- }
76
- function summaryEmpty(label) {
77
- return `已全量扫描该主体${label}数据库,未发现任何记录。`;
78
- }
79
- function summaryEmptyRisk(name, label) {
80
- return `经天眼查底层数据库全量核查实体 ${name},当前未发现任何【${label}】记录。此项核心合规风控排查安全,允许进入下一步审计。`;
81
- }
82
- export function applyTransformer(tool, results, warnings) {
83
- const out = {};
84
- // 1. 按 sources 顺序合并
85
- const singleSource = tool.sources.length <= 1;
86
- for (const src of tool.sources) {
87
- const r = results[src.scope];
88
- if (!r)
89
- continue;
90
- if (singleSource || src.scope === "root") {
91
- for (const [k, v] of Object.entries(r))
92
- out[k] = v;
93
- }
94
- else {
95
- for (const [k, v] of Object.entries(r))
96
- out[k] = v;
97
- out["_" + src.scope] = r;
98
- }
99
- }
100
- // 2. 时间戳格式化
101
- formatTimestampsInPlace(out);
102
- // 3. 元数据
103
- if (Object.keys(warnings).length > 0)
104
- out._warnings = warnings;
105
- const items = Array.isArray(out.items) ? out.items : null;
106
- if (items && items.length === 0)
107
- out._empty = true;
108
- if (tool.summary) {
109
- const name = typeof out.name === "string" ? out.name : "";
110
- const count = items ? items.length : 0;
111
- const label = tool.summary.label;
112
- switch (tool.summary.template) {
113
- case "count":
114
- out._summary = count > 0 ? summaryCount(count, label) : summaryEmpty(label);
115
- break;
116
- case "count_truncated": {
117
- let total = count;
118
- if (typeof out.total === "number" && out.total > 0)
119
- total = out.total;
120
- out._summary = count > 0 ? summaryCountTruncated(total, count, label) : summaryEmpty(label);
121
- break;
122
- }
123
- case "empty_risk":
124
- out._summary = count > 0 ? summaryCount(count, label) : summaryEmptyRisk(name, label);
125
- break;
126
- case "empty_generic":
127
- default:
128
- out._summary = count > 0 ? summaryCount(count, label) : summaryEmpty(label);
129
- break;
130
- }
131
- }
132
- return out;
133
- }