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
|
@@ -5,21 +5,14 @@
|
|
|
5
5
|
* - 顶层简单字段渲染为 key/value 表格
|
|
6
6
|
* - 项目元数据(_summary / _empty / _warnings)单独凸显
|
|
7
7
|
* - items 列表自动渲染为表格
|
|
8
|
-
* -
|
|
8
|
+
* - 嵌套对象/对象数组完整保留;显式截断由统一输出层负责
|
|
9
9
|
*/
|
|
10
|
-
|
|
10
|
+
import { renderChangeRecords, splitChangeRecords } from "./personnelChanges.js";
|
|
11
|
+
import { renderHistoryGuide, splitHistoricalPersonnel } from "./history.js";
|
|
12
|
+
const META_KEYS = new Set(["_summary", "_empty", "_warnings", "_tip", "_truncated", "_pagination", "_riskOverview", "_dateSemantics", "_fieldSemantics", "_historyGuide", "_source"]);
|
|
11
13
|
function isPlainObject(v) {
|
|
12
14
|
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
13
15
|
}
|
|
14
|
-
function isEmptyValue(v) {
|
|
15
|
-
if (v === null || v === undefined || v === "")
|
|
16
|
-
return true;
|
|
17
|
-
if (Array.isArray(v) && v.length === 0)
|
|
18
|
-
return true;
|
|
19
|
-
if (isPlainObject(v) && Object.keys(v).length === 0)
|
|
20
|
-
return true;
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
23
16
|
function formatPrimitive(v) {
|
|
24
17
|
if (v === null || v === undefined || v === "")
|
|
25
18
|
return "-";
|
|
@@ -49,21 +42,16 @@ function formatArrayCell(arr) {
|
|
|
49
42
|
return arr.map(formatPrimitive).join(", ");
|
|
50
43
|
}
|
|
51
44
|
// 对象数组:紧凑 JSON
|
|
52
|
-
return JSON.stringify(arr)
|
|
45
|
+
return JSON.stringify(arr);
|
|
53
46
|
}
|
|
54
47
|
function formatObjectInline(obj) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
else {
|
|
63
|
-
parts.push(`${k}=${formatPrimitive(v)}`);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return parts.join("; ") || "-";
|
|
48
|
+
return JSON.stringify(obj);
|
|
49
|
+
}
|
|
50
|
+
function renderJSONBlock(value) {
|
|
51
|
+
const text = JSON.stringify(value, null, 2);
|
|
52
|
+
const longestFence = Math.max(0, ...(text.match(/`+/g) || []).map((s) => s.length));
|
|
53
|
+
const fence = "`".repeat(Math.max(3, longestFence + 1));
|
|
54
|
+
return `${fence}json\n${text}\n${fence}\n`;
|
|
67
55
|
}
|
|
68
56
|
function renderItemsTable(items, title = "items") {
|
|
69
57
|
if (items.length === 0) {
|
|
@@ -75,6 +63,9 @@ function renderItemsTable(items, title = "items") {
|
|
|
75
63
|
items.forEach((v, i) => lines.push(`${i + 1}. ${formatPrimitive(v)}`));
|
|
76
64
|
return lines.join("\n") + "\n";
|
|
77
65
|
}
|
|
66
|
+
if (!items.every(isPlainObject)) {
|
|
67
|
+
return `\n### ${title}(${items.length} 条)\n\n${renderJSONBlock(items)}`;
|
|
68
|
+
}
|
|
78
69
|
// 对象数组 → Markdown 表格
|
|
79
70
|
const cols = getAllKeys(items);
|
|
80
71
|
if (cols.length === 0)
|
|
@@ -119,16 +110,71 @@ function renderTopFields(obj) {
|
|
|
119
110
|
return lines.join("\n") + "\n";
|
|
120
111
|
}
|
|
121
112
|
function renderMeta(obj) {
|
|
122
|
-
const lines =
|
|
113
|
+
const lines = renderHistoryGuide(obj._historyGuide);
|
|
114
|
+
const sourceNotes = (payload, prefix = "") => {
|
|
115
|
+
if (!isPlainObject(payload._source))
|
|
116
|
+
return;
|
|
117
|
+
const source = payload._source;
|
|
118
|
+
lines.push(`> ${prefix}数据来源:${escapeCell(formatPrimitive(source.api))};${escapeCell(formatPrimitive(source.meaning))}`);
|
|
119
|
+
lines.push(`> ${prefix}来源时间口径:${escapeCell(formatPrimitive(source.temporalNote))}`);
|
|
120
|
+
};
|
|
121
|
+
sourceNotes(obj);
|
|
122
|
+
if (isPlainObject(obj.sources)) {
|
|
123
|
+
for (const [scope, payload] of Object.entries(obj.sources)) {
|
|
124
|
+
if (isPlainObject(payload))
|
|
125
|
+
sourceNotes(payload, `${scope}:`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (isPlainObject(obj._fieldSemantics)) {
|
|
129
|
+
for (const key of Object.keys(obj._fieldSemantics).sort()) {
|
|
130
|
+
const note = obj._fieldSemantics[key];
|
|
131
|
+
if (typeof note === "string" && note)
|
|
132
|
+
lines.push(`> 字段口径:${escapeCell(note)}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (isPlainObject(obj._dateSemantics)) {
|
|
136
|
+
for (const key of ["change_time", "ftShareholding", "changeTime", "createTime", "historicalTime", "firstAppointment"]) {
|
|
137
|
+
const note = obj._dateSemantics[key];
|
|
138
|
+
if (typeof note === "string" && note)
|
|
139
|
+
lines.push(`> 时间口径:${escapeCell(note)}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
123
142
|
if (obj._summary) {
|
|
124
143
|
lines.push(`> 📋 **摘要**:${formatPrimitive(obj._summary)}`);
|
|
125
144
|
}
|
|
126
145
|
if (obj._tip) {
|
|
127
146
|
lines.push(`> 💡 **提示**:${formatPrimitive(obj._tip)}`);
|
|
128
147
|
}
|
|
129
|
-
if (obj._empty === true) {
|
|
148
|
+
if (obj._empty === true && !isPlainObject(obj._riskOverview)) {
|
|
130
149
|
lines.push(`> ⚪ **空结果**:未发现该维度记录`);
|
|
131
150
|
}
|
|
151
|
+
if (isPlainObject(obj._pagination)) {
|
|
152
|
+
const p = obj._pagination;
|
|
153
|
+
const parts = [];
|
|
154
|
+
if (p.page !== undefined)
|
|
155
|
+
parts.push(`第${p.page}页`);
|
|
156
|
+
if (p.requested_page_size !== undefined)
|
|
157
|
+
parts.push(`请求每页${p.requested_page_size}条`);
|
|
158
|
+
if (p.effective_page_size !== undefined)
|
|
159
|
+
parts.push(`实际每页${p.effective_page_size}条`);
|
|
160
|
+
if (p.max_page_size !== undefined)
|
|
161
|
+
parts.push(`上游每页上限${p.max_page_size}条`);
|
|
162
|
+
parts.push(`本次返回${p.returned}条`);
|
|
163
|
+
parts.push(p.total === undefined ? "全量总数未知" : `共${p.total}条`);
|
|
164
|
+
if (p.has_more === true) {
|
|
165
|
+
parts.push(`还有下一页;沿用原命令和主体/筛选参数,传 --pageNum ${p.next_page} --pageSize ${p.effective_page_size}`);
|
|
166
|
+
}
|
|
167
|
+
else if (p.has_more === false) {
|
|
168
|
+
parts.push("已到末页");
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
parts.push("尚不能确定是否还有下一页");
|
|
172
|
+
}
|
|
173
|
+
lines.push(`> 分页:${parts.join(";")}。`);
|
|
174
|
+
}
|
|
175
|
+
else if (obj._truncated === true) {
|
|
176
|
+
lines.push("> 当前返回不是全量;请根据工具分页参数继续查询。");
|
|
177
|
+
}
|
|
132
178
|
if (obj._warnings && isPlainObject(obj._warnings) && Object.keys(obj._warnings).length > 0) {
|
|
133
179
|
lines.push(`> ⚠️ **警告**:`);
|
|
134
180
|
for (const [path, msg] of Object.entries(obj._warnings)) {
|
|
@@ -137,6 +183,55 @@ function renderMeta(obj) {
|
|
|
137
183
|
}
|
|
138
184
|
return lines.length > 0 ? lines.join("\n") + "\n" : "";
|
|
139
185
|
}
|
|
186
|
+
function renderRiskOverview(obj) {
|
|
187
|
+
const parts = [];
|
|
188
|
+
const meta = obj._riskOverview;
|
|
189
|
+
if (isPlainObject(meta)) {
|
|
190
|
+
// State and counts come from Shared Core; never derive them from local rows.
|
|
191
|
+
parts.push(`> 数据状态:${formatPrimitive(meta.dataStatus)};摘要及诊断行数:${formatPrimitive(meta.displayRowCount)};风险事件总数:未确认。`);
|
|
192
|
+
parts.push(`> 计数口径:${formatPrimitive(meta.countMeaning)}`);
|
|
193
|
+
if (meta.reportedTotal !== undefined) {
|
|
194
|
+
parts.push(`> 上游报告总数:${formatPrimitive(meta.reportedTotal)}(保留原始口径,未经事件去重核实)。`);
|
|
195
|
+
}
|
|
196
|
+
if (Array.isArray(meta.reportedCounts) && meta.reportedCounts.length > 0) {
|
|
197
|
+
parts.push(renderItemsTable(meta.reportedCounts, "上游计数来源"));
|
|
198
|
+
}
|
|
199
|
+
if (Array.isArray(meta.issues)) {
|
|
200
|
+
parts.push(...meta.issues.map((issue) => `> 数据说明:${escapeCell(formatPrimitive(issue))}`));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const sections = [
|
|
204
|
+
["toolRisks", "可用专项工具获取详情"],
|
|
205
|
+
["detailRisks", "需通过风险 ID 获取详情"],
|
|
206
|
+
["relationRiskNotes", "周边风险提示"],
|
|
207
|
+
["warningRisks", "预警提醒"],
|
|
208
|
+
["unclassifiedRisks", "未分类风险及诊断信息"],
|
|
209
|
+
];
|
|
210
|
+
const sectionKeys = new Set(sections.map(([key]) => key));
|
|
211
|
+
const otherFields = Object.fromEntries(Object.entries(obj).filter(([key]) => !META_KEYS.has(key) && !sectionKeys.has(key) && key !== "name" && !(key === "total" && isPlainObject(meta))));
|
|
212
|
+
if (Object.keys(otherFields).length > 0)
|
|
213
|
+
parts.push(renderTopFields(otherFields));
|
|
214
|
+
for (const [key, title] of sections) {
|
|
215
|
+
const rows = obj[key];
|
|
216
|
+
if (!Array.isArray(rows) || rows.length === 0)
|
|
217
|
+
continue;
|
|
218
|
+
if (key === "detailRisks") {
|
|
219
|
+
parts.push("使用 get_risk_detail,将 riskId 作为 id 参数、同一行 type 作为 type 参数。");
|
|
220
|
+
}
|
|
221
|
+
parts.push(renderItemsTable(rows.map((row) => {
|
|
222
|
+
if (!isPlainObject(row) || !Array.isArray(row.examples))
|
|
223
|
+
return row;
|
|
224
|
+
const { examples, ...rest } = row;
|
|
225
|
+
return rest;
|
|
226
|
+
}), title));
|
|
227
|
+
for (const row of rows) {
|
|
228
|
+
if (isPlainObject(row) && Array.isArray(row.examples) && row.examples.length > 0) {
|
|
229
|
+
parts.push(renderItemsTable(row.examples, `${title} / ${formatPrimitive(row.riskType)}:关联主体与原始风险示例`));
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return parts.join("\n\n");
|
|
234
|
+
}
|
|
140
235
|
function renderNestedObjects(obj) {
|
|
141
236
|
const lines = [];
|
|
142
237
|
for (const [k, v] of Object.entries(obj)) {
|
|
@@ -181,6 +276,15 @@ export function jsonToMarkdown(value, toolName) {
|
|
|
181
276
|
return "```\n" + formatPrimitive(value) + "\n```\n";
|
|
182
277
|
}
|
|
183
278
|
const obj = value;
|
|
279
|
+
const historical = splitHistoricalPersonnel(obj, toolName);
|
|
280
|
+
if (historical)
|
|
281
|
+
return jsonToMarkdown(historical.rest, toolName) + "\n" + historical.markdown;
|
|
282
|
+
const changes = splitChangeRecords(obj, toolName);
|
|
283
|
+
if (changes) {
|
|
284
|
+
// The recursive call receives a copy without the extracted list. Keep raw
|
|
285
|
+
// code blocks verbatim: the generic newline compaction must not touch them.
|
|
286
|
+
return jsonToMarkdown(changes.rest, toolName) + "\n" + renderChangeRecords(changes.records, changes.title);
|
|
287
|
+
}
|
|
184
288
|
const parts = [];
|
|
185
289
|
// 标题
|
|
186
290
|
if (toolName) {
|
|
@@ -193,6 +297,10 @@ export function jsonToMarkdown(value, toolName) {
|
|
|
193
297
|
const meta = renderMeta(obj);
|
|
194
298
|
if (meta)
|
|
195
299
|
parts.push(meta);
|
|
300
|
+
if (toolName === "get_risk_overview" || isPlainObject(obj._riskOverview)) {
|
|
301
|
+
parts.push(renderRiskOverview(obj));
|
|
302
|
+
return parts.join("\n").replace(/\n{3,}/g, "\n\n");
|
|
303
|
+
}
|
|
196
304
|
// 顶层字段
|
|
197
305
|
const topFields = renderTopFields(obj);
|
|
198
306
|
parts.push(topFields);
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Presentation only: all personnel extraction and statuses come from Shared Core.
|
|
2
|
+
import { changeRecordsForDisplay } from "./history.js";
|
|
3
|
+
const object = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
4
|
+
const text = (v) => typeof v === "string" ? v : JSON.stringify(v) ?? "null";
|
|
5
|
+
const cell = (v) => (v === undefined || v === "" ? "-" : text(v)).replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
|
|
6
|
+
function block(value) {
|
|
7
|
+
const raw = text(value);
|
|
8
|
+
const longest = Math.max(0, ...(raw.match(/`+/g) ?? []).map((s) => s.length));
|
|
9
|
+
const fence = "`".repeat(Math.max(3, longest + 1));
|
|
10
|
+
return `${fence}text\n${raw}\n${fence}\n`;
|
|
11
|
+
}
|
|
12
|
+
export function splitChangeRecords(obj, toolName) {
|
|
13
|
+
if (toolName === "get_change_records" && Array.isArray(obj.items)) {
|
|
14
|
+
const rest = { ...obj };
|
|
15
|
+
delete rest.items;
|
|
16
|
+
return { rest, records: obj.items, title: "变更记录" };
|
|
17
|
+
}
|
|
18
|
+
if (toolName === "get_historical_registration" && object(obj.sources) && object(obj.sources.cb) && Array.isArray(obj.sources.cb.changeList)) {
|
|
19
|
+
const cb = { ...obj.sources.cb };
|
|
20
|
+
delete cb.changeList;
|
|
21
|
+
return { rest: { ...obj, sources: { ...obj.sources, cb } }, records: obj.sources.cb.changeList, title: "工商信息 / 变更记录" };
|
|
22
|
+
}
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
function renderSide(title, side) {
|
|
26
|
+
if (!object(side))
|
|
27
|
+
return "";
|
|
28
|
+
const out = [`#### ${title}\n`, `解析状态:${cell(side.status)}\n`];
|
|
29
|
+
const people = Array.isArray(side.people) ? side.people : [];
|
|
30
|
+
if (people.length) {
|
|
31
|
+
out.push("| 姓名 | 职务原值 | 法定代表人标志原值 | 显式变更标记 | 其他登记字段 |", "|---|---|---|---|---|");
|
|
32
|
+
for (const person of people) {
|
|
33
|
+
if (!object(person)) {
|
|
34
|
+
out.push(block(person));
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
const fields = Array.isArray(person.fields) ? person.fields : [];
|
|
38
|
+
const other = fields.filter((field) => !object(field) || !["姓名", "职务", "法定代表人标志"].includes(String(field.key)));
|
|
39
|
+
out.push(`| ${[person.name, person.position, person.legalRepresentativeFlag, person.marker, other].map(cell).join(" | ")} |`);
|
|
40
|
+
}
|
|
41
|
+
out.push("");
|
|
42
|
+
}
|
|
43
|
+
if (Array.isArray(side.warnings))
|
|
44
|
+
for (const warning of side.warnings)
|
|
45
|
+
out.push(`> 解析说明:${cell(warning)}\n`);
|
|
46
|
+
if (Array.isArray(side.unparsedFragments))
|
|
47
|
+
for (const fragment of side.unparsedFragments)
|
|
48
|
+
out.push("未解析片段:\n", block(fragment));
|
|
49
|
+
return out.join("\n");
|
|
50
|
+
}
|
|
51
|
+
export function renderChangeRecords(records, title) {
|
|
52
|
+
const out = [`## ${title}(${records.length} 条)\n`, "> 本次返回记录按变更日期倒序展示;同日维持来源顺序,日期未知置后,不代表全量历程。\n"];
|
|
53
|
+
changeRecordsForDisplay(records).forEach((value, index) => {
|
|
54
|
+
if (!object(value)) {
|
|
55
|
+
out.push(block(value));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
out.push(`### 第 ${index + 1} 条 · ${cell(value.changeTime)} · ${cell(value.changeItem)}\n`);
|
|
59
|
+
const parsed = object(value._parsed) && value._parsed.version === 1 ? value._parsed : undefined;
|
|
60
|
+
for (const [key, v] of Object.entries(value)) {
|
|
61
|
+
if (["changeTime", "changeItem", "contentBefore", "contentAfter"].includes(key) || (key === "_parsed" && parsed))
|
|
62
|
+
continue;
|
|
63
|
+
out.push(`${key === "createTime" ? "记录创建日期" : key}:${cell(v)}\n`);
|
|
64
|
+
}
|
|
65
|
+
if (parsed) {
|
|
66
|
+
out.push(`> 解析版本:${cell(parsed.version)};原文完整性:${cell(parsed.sourceCompleteness)}(未确认)。解析结果不是完整任职快照,标志原值未转换。\n`);
|
|
67
|
+
out.push(renderSide("变更前人员", parsed.before), renderSide("变更后人员", parsed.after));
|
|
68
|
+
}
|
|
69
|
+
for (const [key, name] of [["contentBefore", "变更前原文"], ["contentAfter", "变更后原文"]]) {
|
|
70
|
+
out.push(`#### ${name}\n`, key in value ? block(value[key]) : "_(原字段未返回)_\n");
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
return out.join("\n");
|
|
74
|
+
}
|