gangtise-openapi-cli 0.14.4 → 0.15.1
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 +48 -0
- package/dist/src/cli.js +54 -74
- package/dist/src/core/endpoints.js +14 -0
- package/dist/src/core/errors.js +7 -0
- package/dist/src/version.js +1 -1
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -4,6 +4,30 @@
|
|
|
4
4
|
|
|
5
5
|
## Changelog
|
|
6
6
|
|
|
7
|
+
### v0.15.0 — 2026-05-29
|
|
8
|
+
|
|
9
|
+
**新增接口**
|
|
10
|
+
- `alternative concept-info` — 题材指数基本信息:返回题材整体画像(定义 / 投资逻辑 / 行业空间 / 竞争格局 / 催化事件)。按 `--concept-id` 查询,仅返回最新截面数据,不支持历史回溯
|
|
11
|
+
- `alternative concept-securities` — 题材指数成分股(题材深度 F8):按分组结构返回当前成分股,每只含是否重点个股 `isKey` 与纳入理由 `inclusionReason`。按 `--concept-id` 查询
|
|
12
|
+
|
|
13
|
+
**接口变更**
|
|
14
|
+
- `quote index-day-kline` 返回字段新增 `securityName`(指数名称,如"上证指数")
|
|
15
|
+
|
|
16
|
+
> `--concept-id` 与主题跟踪 `ai theme-tracking --theme-id` 共用同一套题材 ID 体系,可用 `gangtise lookup theme-id list` 按名称查询(如 机器人 → `121000130`)。
|
|
17
|
+
|
|
18
|
+
### v0.14.4 — 2026-05-29
|
|
19
|
+
|
|
20
|
+
**Bug fix(全市场 K 线分片容错)**
|
|
21
|
+
- `quote day-kline --security all` 等全市场查询的日期分片改为容错:部分分片失败时返回已成功分片的数据并标记 `partial: true` + `failedShards`(失败的日期区间),同时向 stderr 告警;只有全部分片失败才抛错。此前为 fail-fast,单片失败会让整次查询失败,或在异常路径上被误判为空结果。
|
|
22
|
+
|
|
23
|
+
### v0.14.3 — 2026-05-29
|
|
24
|
+
|
|
25
|
+
**性能 / 健壮性**
|
|
26
|
+
- 标题缓存按端点封顶(5000 条/端点)并清理过期项,修复 `title-cache.json` 无上限增长(曾达 ~58MB)拖慢启动的问题
|
|
27
|
+
- 下载接口遇鉴权失效(`8000014` / `8000015`)自动刷新 token 并重试一次(此前仅普通 JSON 调用具备 token 自愈)
|
|
28
|
+
- CLI handler 抽出 `emit` / `withClient` 公共封装去除重复样板;CSV 转义逻辑去重;翻页与 K 线分片统一走 `GANGTISE_PAGE_CONCURRENCY` 并发控制
|
|
29
|
+
- 补齐多个 core 模块的单元测试
|
|
30
|
+
|
|
7
31
|
### v0.14.2 — 2026-05-22
|
|
8
32
|
|
|
9
33
|
**Bug fix(A 股 / HK 全市场 K 线同源问题)**
|
|
@@ -101,6 +125,22 @@ cd gangtise-openapi-cli
|
|
|
101
125
|
npm install
|
|
102
126
|
npm run dev -- --help
|
|
103
127
|
```
|
|
128
|
+
|
|
129
|
+
## 发布
|
|
130
|
+
|
|
131
|
+
npm 发版通过 GitHub Actions Trusted Publishing 完成,不需要 `NPM_TOKEN`。npm 包设置里的 Trusted Publisher 需要匹配本仓库和 workflow 文件名 `publish.yml`。
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
npm version patch --no-git-tag-version
|
|
135
|
+
npm run prepare
|
|
136
|
+
VERSION=$(node -p "require('./package.json').version")
|
|
137
|
+
git commit -am "chore: release v$VERSION"
|
|
138
|
+
git tag "v$VERSION"
|
|
139
|
+
git push --follow-tags
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
推送 `v*` tag 后,`.github/workflows/publish.yml` 会在 GitHub-hosted runner 上使用 OIDC 发布到 `https://registry.npmjs.org/`。也可以从 GitHub Actions 页面手动运行该 workflow。
|
|
143
|
+
|
|
104
144
|
## 版本更新
|
|
105
145
|
|
|
106
146
|
查看当前版本(自动与线上版本比对):
|
|
@@ -240,6 +280,8 @@ cp -r gangtise-openapi ~/.hermes/skills/gangtise-openapi
|
|
|
240
280
|
| | `stock-pool-list` / `stock-pool-stocks` | 自选股股票池列表与证券明细 |
|
|
241
281
|
| **Alternative** | `edb-search` | 行业指标搜索(按关键词匹配,返回 indicatorId 等元信息) |
|
|
242
282
|
| | `edb-data` | 行业指标时序数据(批量拉取,最多10个指标) |
|
|
283
|
+
| | `concept-info` | 题材指数基本信息(投资逻辑/行业空间/竞争格局/催化事件) |
|
|
284
|
+
| | `concept-securities` | 题材指数成分股(题材深度F8,按分组,标记重点个股) |
|
|
243
285
|
| **Raw** | `call` | 原始接口调用(可访问任意 endpoint) |
|
|
244
286
|
|
|
245
287
|
## 命令概览
|
|
@@ -549,6 +591,12 @@ gangtise alternative edb-data \
|
|
|
549
591
|
--end-date 2024-12-31 \
|
|
550
592
|
--format csv \
|
|
551
593
|
--output ./indicator.csv
|
|
594
|
+
|
|
595
|
+
# 题材指数:先查 conceptId(与 theme-id 共用 ID 体系),再拉画像 / 成分股
|
|
596
|
+
gangtise lookup theme-id list | grep 机器人 # → 121000130
|
|
597
|
+
gangtise alternative concept-info --concept-id 121000130 --format json
|
|
598
|
+
# 题材成分股(题材深度 F8,按分组返回,标记重点个股)
|
|
599
|
+
gangtise alternative concept-securities --concept-id 121000130 --format json
|
|
552
600
|
```
|
|
553
601
|
|
|
554
602
|
### Raw
|
package/dist/src/cli.js
CHANGED
|
@@ -46,6 +46,35 @@ async function runDownload(client, endpointKey, query, options) {
|
|
|
46
46
|
const resolved = options.resolveOutputPath ? await options.resolveOutputPath(result) : undefined;
|
|
47
47
|
await saveDownloadResult(result, options.fallbackName, resolved);
|
|
48
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Register a download subcommand. All download commands share one shape: a
|
|
51
|
+
* required id option, optionally --file-type / --content-type, then --output.
|
|
52
|
+
* `idField` doubles as the commander option key and the query/title-cache
|
|
53
|
+
* field, so it must stay the camelCase twin of `idOption`.
|
|
54
|
+
*/
|
|
55
|
+
function addDownloadCommand(parent, spec) {
|
|
56
|
+
const cmd = parent.command(spec.name ?? "download").requiredOption(`${spec.idOption} <id>`);
|
|
57
|
+
if (spec.fileType?.required)
|
|
58
|
+
cmd.requiredOption("--file-type <number>", spec.fileType.description);
|
|
59
|
+
else if (spec.fileType)
|
|
60
|
+
cmd.option("--file-type <number>", spec.fileType.description, spec.fileType.default);
|
|
61
|
+
if (spec.contentTypeDescription)
|
|
62
|
+
cmd.requiredOption("--content-type <type>", spec.contentTypeDescription);
|
|
63
|
+
cmd.option("--output <path>").action((options) => withClient(async (client) => {
|
|
64
|
+
const id = options[spec.idField];
|
|
65
|
+
const qp = { [spec.idField]: id };
|
|
66
|
+
if (spec.fileType && options.fileType)
|
|
67
|
+
qp.fileType = parseNumberOption(options.fileType, "--file-type", { integer: true, min: 1 });
|
|
68
|
+
if (spec.contentTypeDescription)
|
|
69
|
+
qp.contentType = options.contentType;
|
|
70
|
+
const titleList = spec.titleListEndpoint;
|
|
71
|
+
await runDownload(client, spec.endpointKey, qp, {
|
|
72
|
+
output: options.output,
|
|
73
|
+
fallbackName: `${spec.fallbackPrefix}-${id}`,
|
|
74
|
+
resolveOutputPath: titleList ? (result) => resolveTitle(client, result, titleList, spec.idField, id) : undefined,
|
|
75
|
+
});
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
49
78
|
function addTimeFilters(command) {
|
|
50
79
|
return command
|
|
51
80
|
.option("--from <number>", "Starting offset", "0")
|
|
@@ -80,15 +109,20 @@ program
|
|
|
80
109
|
await printData({ hasEnvToken: Boolean(config.token), hasCachedToken: Boolean(cache?.accessToken), cache }, parseOutputFormat(options.format));
|
|
81
110
|
}));
|
|
82
111
|
const lookup = new Command("lookup").description("Lookup helper APIs");
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
.addCommand(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
112
|
+
const addLookupList = (name, endpointKey, description) => {
|
|
113
|
+
const cmd = new Command(name);
|
|
114
|
+
if (description)
|
|
115
|
+
cmd.description(description);
|
|
116
|
+
lookup.addCommand(cmd.addCommand(new Command("list").option("--format <format>", "Output format", "table").action((options) => emit(options, (client) => client.call(endpointKey)))));
|
|
117
|
+
};
|
|
118
|
+
addLookupList("research-area", "lookup.research-areas.list");
|
|
119
|
+
addLookupList("broker-org", "lookup.broker-orgs.list");
|
|
120
|
+
addLookupList("meeting-org", "lookup.meeting-orgs.list");
|
|
121
|
+
addLookupList("industry", "lookup.industries.list");
|
|
122
|
+
addLookupList("region", "lookup.regions.list", "Foreign report region codes");
|
|
123
|
+
addLookupList("announcement-category", "lookup.announcement-categories.list", "Announcement category codes");
|
|
124
|
+
addLookupList("industry-code", "lookup.industry-codes.list", "Shenwan industry codes for security-clue --gts-code");
|
|
125
|
+
addLookupList("theme-id", "lookup.theme-ids.list", "Theme IDs for theme-tracking --theme-id");
|
|
92
126
|
program.addCommand(lookup);
|
|
93
127
|
const insight = new Command("insight").description("Insight APIs");
|
|
94
128
|
const opinion = new Command("opinion");
|
|
@@ -115,16 +149,7 @@ addTimeFilters(summary.command("list").option("--search-type <number>", "Search
|
|
|
115
149
|
researchAreaList: maybeArray(options.researchArea), securityList: maybeArray(options.security), institutionList: maybeArray(options.institution),
|
|
116
150
|
categoryList: maybeArray(options.category), marketList: maybeArray(options.market), participantRoleList: maybeArray(options.participantRole),
|
|
117
151
|
}), { endpointKey: "insight.summary.list", idField: "summaryId" }));
|
|
118
|
-
summary.
|
|
119
|
-
const qp = { summaryId: options.summaryId };
|
|
120
|
-
if (options.fileType)
|
|
121
|
-
qp.fileType = parseNumberOption(options.fileType, "--file-type", { integer: true, min: 1 });
|
|
122
|
-
await runDownload(client, "insight.summary.download", qp, {
|
|
123
|
-
output: options.output,
|
|
124
|
-
fallbackName: `summary-${options.summaryId}`,
|
|
125
|
-
resolveOutputPath: (result) => resolveTitle(client, result, "insight.summary.list", "summaryId", options.summaryId),
|
|
126
|
-
});
|
|
127
|
-
}));
|
|
152
|
+
addDownloadCommand(summary, { endpointKey: "insight.summary.download", idOption: "--summary-id", idField: "summaryId", fallbackPrefix: "summary", fileType: { description: "File type: 1=original(default) 2=HTML; only affects meeting platform summaries" }, titleListEndpoint: "insight.summary.list" });
|
|
128
153
|
const addScheduleList = (command, endpointKey) => addTimeFilters(command.command("list").option("--research-area <id>", "Research area", collectList, []).option("--institution <id>", "Institution ID", collectList, []).option("--security <code>", "Security code", collectList, []).option("--category <name>", "Category", collectList, []).option("--market <name>", "Market", collectList, []).option("--participant-role <name>", "Participant role", collectList, []).option("--broker-type <name>", "Broker type", collectList, []).option("--object <type>", "Object type: company/industry", collectList, []).option("--permission <number>", "Permission", collectNumberList, []).option("--format <format>", "Output format", "table").option("--output <path>", "Output path")).action((options) => emit(options, (client) => client.call(endpointKey, {
|
|
129
154
|
from: parseFrom(options.from), size: parseSize(options.size), startTime: options.startTime, endTime: options.endTime, keyword: options.keyword,
|
|
130
155
|
researchAreaList: maybeArray(options.researchArea), institutionList: maybeArray(options.institution), securityList: maybeArray(options.security),
|
|
@@ -143,13 +168,7 @@ addTimeFilters(research.command("list").option("--search-type <number>", "Search
|
|
|
143
168
|
ratingChangeList: maybeArray(options.ratingChange), minReportPages: parseOptionalNumberOption(options.minPages, "--min-pages", { integer: true, min: 0 }),
|
|
144
169
|
maxReportPages: parseOptionalNumberOption(options.maxPages, "--max-pages", { integer: true, min: 0 }), sourceList: maybeArray(options.source),
|
|
145
170
|
}), { endpointKey: "insight.research.list", idField: "reportId" }));
|
|
146
|
-
research.
|
|
147
|
-
await runDownload(client, "insight.research.download", { reportId: options.reportId, fileType: parseNumberOption(options.fileType, "--file-type", { integer: true, min: 1 }) }, {
|
|
148
|
-
output: options.output,
|
|
149
|
-
fallbackName: `research-${options.reportId}`,
|
|
150
|
-
resolveOutputPath: (result) => resolveTitle(client, result, "insight.research.list", "reportId", options.reportId),
|
|
151
|
-
});
|
|
152
|
-
}));
|
|
171
|
+
addDownloadCommand(research, { endpointKey: "insight.research.download", idOption: "--report-id", idField: "reportId", fallbackPrefix: "research", fileType: { description: "File type: 1=PDF 2=Markdown", default: "1" }, titleListEndpoint: "insight.research.list" });
|
|
153
172
|
addTimeFilters(foreignReport.command("list").option("--search-type <number>", "Search type: 1=title 2=fulltext", "1").option("--rank-type <number>", "Rank type: 1=composite 2=time desc", "1").option("--security <code>", "Security code", collectList, []).option("--region <id>", "Region ID", collectList, []).option("--category <name>", "Report category", collectList, []).option("--industry <id>", "Industry ID", collectList, []).option("--broker <id>", "Broker ID", collectList, []).option("--llm-tag <tag>", "Semantic tag", collectList, []).option("--rating <name>", "Rating", collectList, []).option("--rating-change <name>", "Rating change", collectList, []).option("--min-pages <number>", "Min report pages").option("--max-pages <number>", "Max report pages").option("--format <format>", "Output format", "table").option("--output <path>", "Output path")).action((options) => emit(options, (client) => client.call("insight.foreign-report.list", {
|
|
154
173
|
from: parseFrom(options.from), size: parseSize(options.size), startTime: options.startTime, endTime: options.endTime, keyword: options.keyword,
|
|
155
174
|
searchType: parseNumberOption(options.searchType, "--search-type", { integer: true, min: 1 }), rankType: parseNumberOption(options.rankType, "--rank-type", { integer: true, min: 1 }),
|
|
@@ -158,26 +177,14 @@ addTimeFilters(foreignReport.command("list").option("--search-type <number>", "S
|
|
|
158
177
|
ratingList: maybeArray(options.rating), ratingChangeList: maybeArray(options.ratingChange),
|
|
159
178
|
minReportPages: parseOptionalNumberOption(options.minPages, "--min-pages", { integer: true, min: 0 }), maxReportPages: parseOptionalNumberOption(options.maxPages, "--max-pages", { integer: true, min: 0 }),
|
|
160
179
|
}), { endpointKey: "insight.foreign-report.list", idField: "reportId" }));
|
|
161
|
-
foreignReport.
|
|
162
|
-
await runDownload(client, "insight.foreign-report.download", { reportId: options.reportId, fileType: parseNumberOption(options.fileType, "--file-type", { integer: true, min: 1 }) }, {
|
|
163
|
-
output: options.output,
|
|
164
|
-
fallbackName: `foreign-report-${options.reportId}`,
|
|
165
|
-
resolveOutputPath: (result) => resolveTitle(client, result, "insight.foreign-report.list", "reportId", options.reportId),
|
|
166
|
-
});
|
|
167
|
-
}));
|
|
180
|
+
addDownloadCommand(foreignReport, { endpointKey: "insight.foreign-report.download", idOption: "--report-id", idField: "reportId", fallbackPrefix: "foreign-report", fileType: { description: "File type: 1=PDF 2=Markdown 3=CN-PDF 4=CN-Markdown", default: "1" }, titleListEndpoint: "insight.foreign-report.list" });
|
|
168
181
|
addTimeFilters(announcement.command("list").option("--search-type <number>", "Search type: 1=title 2=fulltext", "1").option("--rank-type <number>", "Rank type: 1=composite 2=time desc", "1").option("--security <code>", "Security code", collectList, []).option("--announcement-type <type>", "Announcement type", collectList, []).option("--category <id>", "Category ID", collectList, []).option("--format <format>", "Output format", "table").option("--output <path>", "Output path")).action((options) => emit(options, (client) => client.call("insight.announcement.list", {
|
|
169
182
|
from: parseFrom(options.from), size: parseSize(options.size),
|
|
170
183
|
startTime: parseTimestamp13(options.startTime, "--start-time"), endTime: parseTimestamp13(options.endTime, "--end-time"),
|
|
171
184
|
searchType: parseNumberOption(options.searchType, "--search-type", { integer: true, min: 1 }), rankType: parseNumberOption(options.rankType, "--rank-type", { integer: true, min: 1 }), keyword: options.keyword,
|
|
172
185
|
securityList: maybeArray(options.security), announcementTypeList: maybeArray(options.announcementType), categoryList: maybeArray(options.category),
|
|
173
186
|
}), { endpointKey: "insight.announcement.list", idField: "announcementId" }));
|
|
174
|
-
announcement.
|
|
175
|
-
await runDownload(client, "insight.announcement.download", { announcementId: options.announcementId, fileType: parseNumberOption(options.fileType, "--file-type", { integer: true, min: 1 }) }, {
|
|
176
|
-
output: options.output,
|
|
177
|
-
fallbackName: `announcement-${options.announcementId}`,
|
|
178
|
-
resolveOutputPath: (result) => resolveTitle(client, result, "insight.announcement.list", "announcementId", options.announcementId),
|
|
179
|
-
});
|
|
180
|
-
}));
|
|
187
|
+
addDownloadCommand(announcement, { endpointKey: "insight.announcement.download", idOption: "--announcement-id", idField: "announcementId", fallbackPrefix: "announcement", fileType: { description: "File type: 1=PDF 2=Markdown", default: "1" }, titleListEndpoint: "insight.announcement.list" });
|
|
181
188
|
addTimeFilters(announcementHk.command("list").option("--search-type <number>", "Search type: 1=title 2=fulltext", "1").option("--rank-type <number>", "Rank type: 1=composite 2=time desc", "1").option("--security <code>", "Security code (e.g. 01913.HK)", collectList, []).option("--category <id>", "Category ID", collectList, []).option("--format <format>", "Output format", "table").option("--output <path>", "Output path")).action((options) => emit(options, (client) => client.call("insight.announcement-hk.list", {
|
|
182
189
|
from: parseFrom(options.from), size: parseSize(options.size),
|
|
183
190
|
startTime: options.startTime, endTime: options.endTime,
|
|
@@ -186,13 +193,7 @@ addTimeFilters(announcementHk.command("list").option("--search-type <number>", "
|
|
|
186
193
|
keyword: options.keyword,
|
|
187
194
|
securityList: maybeArray(options.security), categoryList: maybeArray(options.category),
|
|
188
195
|
}), { endpointKey: "insight.announcement-hk.list", idField: "announcementId" }));
|
|
189
|
-
announcementHk.
|
|
190
|
-
await runDownload(client, "insight.announcement-hk.download", { announcementId: options.announcementId }, {
|
|
191
|
-
output: options.output,
|
|
192
|
-
fallbackName: `announcement-hk-${options.announcementId}`,
|
|
193
|
-
resolveOutputPath: (result) => resolveTitle(client, result, "insight.announcement-hk.list", "announcementId", options.announcementId),
|
|
194
|
-
});
|
|
195
|
-
}));
|
|
196
|
+
addDownloadCommand(announcementHk, { endpointKey: "insight.announcement-hk.download", idOption: "--announcement-id", idField: "announcementId", fallbackPrefix: "announcement-hk", titleListEndpoint: "insight.announcement-hk.list" });
|
|
196
197
|
addTimeFilters(foreignOpinion.command("list").option("--rank-type <number>", "Rank type: 1=composite 2=time desc", "1").option("--security <code>", "Security code (e.g. UBER.N)", collectList, []).option("--region <code>", "Region code", collectList, []).option("--industry <id>", "Industry ID", collectList, []).option("--broker <id>", "Broker ID", collectList, []).option("--rating <name>", "Rating", collectList, []).option("--rating-change <name>", "Rating change", collectList, []).option("--format <format>", "Output format", "table").option("--output <path>", "Output path")).action((options) => emit(options, (client) => client.call("insight.foreign-opinion.list", {
|
|
197
198
|
from: parseFrom(options.from), size: parseSize(options.size),
|
|
198
199
|
startTime: options.startTime, endTime: options.endTime,
|
|
@@ -210,12 +211,7 @@ addTimeFilters(independentOpinion.command("list").option("--rank-type <number>",
|
|
|
210
211
|
industryList: maybeArray(options.industry), securityList: maybeArray(options.security),
|
|
211
212
|
ratingList: maybeArray(options.rating), ratingChangeList: maybeArray(options.ratingChange),
|
|
212
213
|
})));
|
|
213
|
-
independentOpinion.
|
|
214
|
-
await runDownload(client, "insight.independent-opinion.download", { independentOpinionId: options.independentOpinionId, fileType: parseNumberOption(options.fileType, "--file-type", { integer: true, min: 1 }) }, {
|
|
215
|
-
output: options.output,
|
|
216
|
-
fallbackName: `independent-opinion-${options.independentOpinionId}`,
|
|
217
|
-
});
|
|
218
|
-
}));
|
|
214
|
+
addDownloadCommand(independentOpinion, { endpointKey: "insight.independent-opinion.download", idOption: "--independent-opinion-id", idField: "independentOpinionId", fallbackPrefix: "independent-opinion", fileType: { description: "File type: 1=original HTML 2=CN-translated HTML", required: true } });
|
|
219
215
|
insight.addCommand(opinion);
|
|
220
216
|
insight.addCommand(summary);
|
|
221
217
|
insight.addCommand(roadshow);
|
|
@@ -362,29 +358,11 @@ reference.command("securities-search").requiredOption("--keyword <text>", "Searc
|
|
|
362
358
|
program.addCommand(reference);
|
|
363
359
|
const vault = new Command("vault").description("Vault APIs");
|
|
364
360
|
vault.command("drive-list").option("--from <number>", "Starting offset", "0").option("--size <number>", "Total rows to return; omit to fetch all").option("--start-time <datetime>").option("--end-time <datetime>").option("--keyword <text>").option("--file-type <number>", "File type", collectNumberList, []).option("--space-type <number>", "Space type", collectNumberList, []).option("--format <format>", "Output format", "table").option("--output <path>").action((options) => emit(options, (client) => client.call("vault.drive.list", { from: parseFrom(options.from), size: parseSize(options.size), startTime: options.startTime, endTime: options.endTime, keyword: options.keyword, fileTypeList: options.fileType.length ? options.fileType : undefined, spaceTypeList: options.spaceType.length ? options.spaceType : undefined }), { endpointKey: "vault.drive.list", idField: "fileId" }));
|
|
365
|
-
vault.
|
|
366
|
-
await runDownload(client, "vault.drive.download", { fileId: options.fileId }, {
|
|
367
|
-
output: options.output,
|
|
368
|
-
fallbackName: `file-${options.fileId}`,
|
|
369
|
-
resolveOutputPath: (result) => resolveTitle(client, result, "vault.drive.list", "fileId", options.fileId),
|
|
370
|
-
});
|
|
371
|
-
}));
|
|
361
|
+
addDownloadCommand(vault, { endpointKey: "vault.drive.download", name: "drive-download", idOption: "--file-id", idField: "fileId", fallbackPrefix: "file", titleListEndpoint: "vault.drive.list" });
|
|
372
362
|
vault.command("record-list").option("--from <number>", "Starting offset", "0").option("--size <number>", "Total rows to return; omit to fetch all").option("--start-time <datetime>").option("--end-time <datetime>").option("--keyword <text>").option("--category <name>", "Recording type: upload/link/mobile/gtNote/pc/share", collectList, []).option("--space-type <number>", "Space type: 1=my records / 2=tenant records", collectNumberList, []).option("--format <format>", "Output format", "table").option("--output <path>").action((options) => emit(options, (client) => client.call("vault.record.list", { from: parseFrom(options.from), size: parseSize(options.size), startTime: options.startTime, endTime: options.endTime, keyword: options.keyword, categoryList: maybeArray(options.category), spaceTypeList: options.spaceType.length ? options.spaceType : undefined }), { endpointKey: "vault.record.list", idField: "recordId" }));
|
|
373
|
-
vault.
|
|
374
|
-
await runDownload(client, "vault.record.download", { recordId: options.recordId, contentType: options.contentType }, {
|
|
375
|
-
output: options.output,
|
|
376
|
-
fallbackName: `record-${options.recordId}`,
|
|
377
|
-
resolveOutputPath: (result) => resolveTitle(client, result, "vault.record.list", "recordId", options.recordId),
|
|
378
|
-
});
|
|
379
|
-
}));
|
|
363
|
+
addDownloadCommand(vault, { endpointKey: "vault.record.download", name: "record-download", idOption: "--record-id", idField: "recordId", fallbackPrefix: "record", contentTypeDescription: "Content type: original/asr/summary", titleListEndpoint: "vault.record.list" });
|
|
380
364
|
vault.command("my-conference-list").option("--from <number>", "Starting offset", "0").option("--size <number>", "Total rows to return; omit to fetch all").option("--start-time <datetime>").option("--end-time <datetime>").option("--keyword <text>").option("--research-area <id>", "Research area ID", collectList, []).option("--security <code>", "Security code", collectList, []).option("--institution <id>", "Institution ID", collectList, []).option("--category <name>", "Conference category: earningsCall/strategyMeeting/fundRoadshow/shareholdersMeeting/maMeeting/specialMeeting/companyAnalysis/industryAnalysis/other", collectList, []).option("--format <format>", "Output format", "table").option("--output <path>").action((options) => emit(options, (client) => client.call("vault.my-conference.list", { from: parseFrom(options.from), size: parseSize(options.size), startTime: options.startTime, endTime: options.endTime, keyword: options.keyword, researchAreaList: maybeArray(options.researchArea), securityList: maybeArray(options.security), institutionList: maybeArray(options.institution), categoryList: maybeArray(options.category) }), { endpointKey: "vault.my-conference.list", idField: "conferenceId" }));
|
|
381
|
-
vault.
|
|
382
|
-
await runDownload(client, "vault.my-conference.download", { conferenceId: options.conferenceId, contentType: options.contentType }, {
|
|
383
|
-
output: options.output,
|
|
384
|
-
fallbackName: `conference-${options.conferenceId}`,
|
|
385
|
-
resolveOutputPath: (result) => resolveTitle(client, result, "vault.my-conference.list", "conferenceId", options.conferenceId),
|
|
386
|
-
});
|
|
387
|
-
}));
|
|
365
|
+
addDownloadCommand(vault, { endpointKey: "vault.my-conference.download", name: "my-conference-download", idOption: "--conference-id", idField: "conferenceId", fallbackPrefix: "conference", contentTypeDescription: "Content type: asr/summary", titleListEndpoint: "vault.my-conference.list" });
|
|
388
366
|
vault.command("wechat-message-list").option("--from <number>", "Starting offset", "0").option("--size <number>", "Total rows to return; omit to fetch all").option("--start-time <datetime>").option("--end-time <datetime>").option("--keyword <text>").option("--security <code>", "Security code (e.g. 000001.SZ)", collectList, []).option("--wechat-group-id <id>", "WeChat group ID", collectList, []).option("--industry <id>", "Industry ID", collectList, []).option("--category <name>", "Message type: text/image/documents/url", collectList, []).option("--tag <name>", "Tag: roadShow/research/strategyMeeting/meetingSummary/industryComment/companyComment/earningsReview", collectList, []).option("--format <format>", "Output format", "table").option("--output <path>").action((options) => emit(options, (client) => client.call("vault.wechat-message.list", buildWechatMessageListBody(options))));
|
|
389
367
|
vault.command("wechat-chatroom-list").option("--from <number>", "Starting offset", "0").option("--size <number>", "Rows to return", "20").option("--room-name <name>", "WeChat group name; repeat or comma-separate for multiple names", collectList, []).option("--format <format>", "Output format", "table").option("--output <path>").action((options) => emit(options, (client) => client.call("vault.wechat-chatroom.list", buildWechatChatroomListBody(options))));
|
|
390
368
|
vault.command("stock-pool-list").option("--format <format>", "Output format", "table").option("--output <path>").action((options) => emit(options, (client) => client.call("vault.stock-pool.list", {})));
|
|
@@ -412,6 +390,8 @@ alternative.command("edb-data").option("--indicator-id <id>", "Indicator ID (rep
|
|
|
412
390
|
}
|
|
413
391
|
await printData(data, parseOutputFormat(options.format), options.output);
|
|
414
392
|
}));
|
|
393
|
+
alternative.command("concept-info").requiredOption("--concept-id <id>", "Concept (theme index) ID, e.g. 121000130 机器人; discover via 'gangtise lookup theme-id list'").option("--format <format>", "Output format", "json").option("--output <path>").action((options) => emit(options, (client) => client.call("alternative.concept-info", { conceptId: options.conceptId })));
|
|
394
|
+
alternative.command("concept-securities").requiredOption("--concept-id <id>", "Concept (theme index) ID, e.g. 121000130 机器人; discover via 'gangtise lookup theme-id list'").option("--format <format>", "Output format", "json").option("--output <path>").action((options) => emit(options, (client) => client.call("alternative.concept-securities", { conceptId: options.conceptId })));
|
|
415
395
|
program.addCommand(alternative);
|
|
416
396
|
program.command("raw").description("Raw API calls").addCommand(new Command("call").argument("<endpointKey>").option("--body <json>").option("--query <key=value>", "Query string pair", collectKeyValue, {}).option("--format <format>", "Output format", "json").option("--output <path>").action(async (endpointKey, options) => {
|
|
417
397
|
const endpoint = ENDPOINTS[endpointKey];
|
|
@@ -537,4 +537,18 @@ export const ENDPOINTS = {
|
|
|
537
537
|
kind: "json",
|
|
538
538
|
description: "Get industry indicator time-series data by indicator ID list",
|
|
539
539
|
},
|
|
540
|
+
"alternative.concept-info": {
|
|
541
|
+
key: "alternative.concept-info",
|
|
542
|
+
method: "POST",
|
|
543
|
+
path: "/application/open-alternative/concept/info",
|
|
544
|
+
kind: "json",
|
|
545
|
+
description: "Query latest concept (theme index) profile by conceptId",
|
|
546
|
+
},
|
|
547
|
+
"alternative.concept-securities": {
|
|
548
|
+
key: "alternative.concept-securities",
|
|
549
|
+
method: "POST",
|
|
550
|
+
path: "/application/open-alternative/concept/securities",
|
|
551
|
+
kind: "json",
|
|
552
|
+
description: "Query concept (theme index) constituent securities, grouped",
|
|
553
|
+
},
|
|
540
554
|
};
|
package/dist/src/core/errors.js
CHANGED
|
@@ -21,6 +21,13 @@ const ERROR_HINTS = {
|
|
|
21
21
|
"8000016": "开发账号状态异常。",
|
|
22
22
|
"8000018": "开发账号已到期。",
|
|
23
23
|
"903301": "今日调用次数已达上限。",
|
|
24
|
+
"410110": "异步内容生成中,稍后用对应 *-check 命令查询。",
|
|
25
|
+
"410111": "异步内容生成失败(终态),请更换参数后重新提交。",
|
|
26
|
+
"410004": "数据未找到,请检查查询条件。",
|
|
27
|
+
"430004": "下载失败(官方未文档化错误码),请确认 reportId 有效或更换 --file-type 重试。",
|
|
28
|
+
"430007": "行情查询超出限制,请缩短日期范围。",
|
|
29
|
+
"433007": "数据源不匹配,请检查 resourceType 与 sourceId 组合。",
|
|
30
|
+
"10011401": "白名单未开通,请联系管理员。",
|
|
24
31
|
};
|
|
25
32
|
export class ApiError extends CliError {
|
|
26
33
|
code;
|
package/dist/src/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Auto-generated — DO NOT EDIT
|
|
2
|
-
export const CLI_VERSION = "0.
|
|
2
|
+
export const CLI_VERSION = "0.15.1";
|
package/package.json
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gangtise-openapi-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.1",
|
|
4
4
|
"description": "CLI for Gangtise OpenAPI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
|
-
"url": "git+
|
|
8
|
+
"url": "git+https://github.com/gangtiser/gangtise-openapi-cli.git"
|
|
9
9
|
},
|
|
10
10
|
"bugs": {
|
|
11
11
|
"url": "https://github.com/gangtiser/gangtise-openapi-cli/issues"
|
|
12
12
|
},
|
|
13
13
|
"homepage": "https://github.com/gangtiser/gangtise-openapi-cli#readme",
|
|
14
14
|
"type": "module",
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"registry": "https://registry.npmjs.org/"
|
|
17
|
+
},
|
|
15
18
|
"bin": {
|
|
16
19
|
"gangtise": "dist/src/cli.js"
|
|
17
20
|
},
|