gangtise-openapi-cli 0.15.1 → 0.16.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.
package/README.md CHANGED
@@ -4,6 +4,20 @@
4
4
 
5
5
  ## Changelog
6
6
 
7
+ ### v0.16.0 — 2026-06-12
8
+
9
+ **新增接口(参考数据 · 常量查询,均免积分)**
10
+ - `reference constant-category` — 查询常量分类:全量导出常量分类及各分类适用于哪些接口的哪些参数(7 个分类:中信/申万/Gangtise 行业、国内城市、A股/港股公告分类、区域)
11
+ - `reference constant-list --category <code>` — 查询常量值:按分类导出全量常量(`constantId` / `constantName`,树形分类含 `children` 嵌套)
12
+ - `reference concept-search --keyword <kw>` — 查询题材 ID:按名称/拼音/分组名搜索,返回 `conceptId`(供 `alternative concept-info / concept-securities`、`ai theme-tracking` 使用)
13
+ - `reference sector-search --keyword <kw>` — 查询板块 ID:返回 `sectorId` + `hierarchy` 层级路径
14
+ - `reference sector-constituents --sector-id <id>` — 查询板块成分股:返回该板块全量成分股(`gtsCode` / `gtsName`);注意 sectorId 必须来自 sector-search,题材 conceptId 查不到成分
15
+
16
+ **接口变更(Breaking)**
17
+ - 移除已被新 API 覆盖的 6 个本地 lookup 子命令及静态数据:`lookup research-area / industry / region / announcement-category / theme-id / industry-code list`,请改用 `reference constant-list` / `reference concept-search` / `reference sector-constituents`(申万行业代码 `821xxx.SWI` 全量:`sector-constituents --sector-id 2000000014`,即申万一级行业指数板块)
18
+ - `lookup` 仅保留 2 个 API 未覆盖的本地表:`broker-org` / `meeting-org`
19
+ - 路演/调研/策略会/论坛 list 新增 `--location <id>` 按城市过滤(domesticCity 常量 ID;实测 2026-06-12 服务端过滤暂未生效)
20
+
7
21
  ### v0.15.0 — 2026-05-29
8
22
 
9
23
  **新增接口**
@@ -135,7 +149,7 @@ npm version patch --no-git-tag-version
135
149
  npm run prepare
136
150
  VERSION=$(node -p "require('./package.json').version")
137
151
  git commit -am "chore: release v$VERSION"
138
- git tag "v$VERSION"
152
+ git tag -a "v$VERSION" -m "v$VERSION" # 必须 annotated:--follow-tags 不推 lightweight tag
139
153
  git push --follow-tags
140
154
  ```
141
155
 
@@ -235,7 +249,7 @@ cp -r gangtise-openapi ~/.hermes/skills/gangtise-openapi
235
249
  | 模块 | 子命令 | 说明 |
236
250
  |------|--------|------|
237
251
  | **Auth** | `login` / `status` | 认证登录、状态查询 |
238
- | **Lookup** | `research-area list` / `broker-org list` / `meeting-org list` / `industry list` / `industry-code list` / `region list` / `announcement-category list` / `theme-id list` | 枚举速查(内置,无需额外文档) |
252
+ | **Lookup** | `broker-org list` / `meeting-org list` | 本地枚举表(API 未覆盖的部分;行业/区域/公告分类/题材/申万行业代码改用 Reference 接口) |
239
253
  | **Insight** | `opinion list` | 内资机构观点 |
240
254
  | | `summary list` / `download` | 纪要(含下载,支持 `--file-type` 选原始/HTML) |
241
255
  | | `roadshow list` | 路演 |
@@ -249,6 +263,11 @@ cp -r gangtise-openapi ~/.hermes/skills/gangtise-openapi
249
263
  | | `foreign-opinion list` | 外资机构观点 |
250
264
  | | `independent-opinion list` / `download` | 外资独立分析师观点(含原文/翻译HTML下载) |
251
265
  | **Reference** | `securities-search` | GTS Code 搜索(按名称/代码/拼音匹配) |
266
+ | | `constant-category` | 常量分类列表(含各分类适用的接口与参数) |
267
+ | | `constant-list` | 按分类导出常量值全量列表(行业/城市/公告分类/区域等) |
268
+ | | `concept-search` | 题材 ID 搜索(名称/拼音/分组名匹配) |
269
+ | | `sector-search` | 板块 ID 搜索(返回层级路径) |
270
+ | | `sector-constituents` | 板块成分股查询 |
252
271
  | **Quote** | `day-kline` / `day-kline-hk` / `day-kline-us` | A股/港股/美股历史日K线 |
253
272
  | | `index-day-kline` | 沪深京指数日K线 |
254
273
  | | `minute-kline` | A股分钟K线 |
@@ -302,13 +321,14 @@ cp -r gangtise-openapi ~/.hermes/skills/gangtise-openapi
302
321
  先查枚举/参数:
303
322
 
304
323
  ```bash
305
- gangtise lookup research-area list
306
- gangtise lookup broker-org list
307
- gangtise lookup meeting-org list
308
- gangtise lookup industry list
309
- gangtise lookup region list # 外资研报区域
310
- gangtise lookup announcement-category list # 公告分类
311
- gangtise lookup industry-code list # 申万行业代码(用于 security-clue --gts-code)
324
+ gangtise reference constant-category # 有哪些常量分类、各用于哪些参数
325
+ gangtise reference constant-list --category citicIndustry # 中信行业(--industry / --research-area)
326
+ gangtise reference constant-list --category swIndustry # 申万行业
327
+ gangtise reference constant-list --category regionCategory # 外资研报区域
328
+ gangtise reference constant-list --category aShareAnnouncementCategory # A股公告分类(树形)
329
+ gangtise reference sector-constituents --sector-id 2000000014 # 申万行业代码 821xxx.SWI 全量(security-clue --gts-code 用)
330
+ gangtise lookup broker-org list # 券商机构(本地表)
331
+ gangtise lookup meeting-org list # 会议机构(本地表)
312
332
  ```
313
333
 
314
334
  再调用业务命令:
@@ -436,6 +456,19 @@ gangtise reference securities-search --keyword "贵州茅台" --category stock
436
456
  gangtise reference securities-search --keyword "600519" --category stock
437
457
  gangtise reference securities-search --keyword gzmt --top 5
438
458
  gangtise reference securities-search --keyword "银行" --category stock --category index
459
+
460
+ # 常量查询:先看分类,再按分类导出全量常量值
461
+ gangtise reference constant-category --format json
462
+ gangtise reference constant-list --category citicIndustry --format json
463
+ gangtise reference constant-list --category aShareAnnouncementCategory --format json # 树形,含 children
464
+
465
+ # 题材 ID 搜索(供 concept-info / concept-securities / theme-tracking 使用)
466
+ gangtise reference concept-search --keyword 机器人 --top 3 --format json
467
+ gangtise reference concept-search --keyword jqr # 拼音首字母
468
+
469
+ # 板块:先搜板块 ID,再查成分股(sectorId 必须来自 sector-search)
470
+ gangtise reference sector-search --keyword 半导体设备 --format json
471
+ gangtise reference sector-constituents --sector-id 1000001005 --format json
439
472
  ```
440
473
 
441
474
  ### Quote
@@ -593,7 +626,7 @@ gangtise alternative edb-data \
593
626
  --output ./indicator.csv
594
627
 
595
628
  # 题材指数:先查 conceptId(与 theme-id 共用 ID 体系),再拉画像 / 成分股
596
- gangtise lookup theme-id list | grep 机器人 # → 121000130
629
+ gangtise reference concept-search --keyword 机器人 --format json # → 121000130
597
630
  gangtise alternative concept-info --concept-id 121000130 --format json
598
631
  # 题材成分股(题材深度 F8,按分组返回,标记重点个股)
599
632
  gangtise alternative concept-securities --concept-id 121000130 --format json
package/dist/src/cli.js CHANGED
@@ -108,21 +108,15 @@ program
108
108
  const cache = await readTokenCache(config.tokenCachePath);
109
109
  await printData({ hasEnvToken: Boolean(config.token), hasCachedToken: Boolean(cache?.accessToken), cache }, parseOutputFormat(options.format));
110
110
  }));
111
- const lookup = new Command("lookup").description("Lookup helper APIs");
111
+ const lookup = new Command("lookup").description("Local lookup tables (IDs not covered by 'reference constant-list')");
112
112
  const addLookupList = (name, endpointKey, description) => {
113
113
  const cmd = new Command(name);
114
114
  if (description)
115
115
  cmd.description(description);
116
116
  lookup.addCommand(cmd.addCommand(new Command("list").option("--format <format>", "Output format", "table").action((options) => emit(options, (client) => client.call(endpointKey)))));
117
117
  };
118
- addLookupList("research-area", "lookup.research-areas.list");
119
118
  addLookupList("broker-org", "lookup.broker-orgs.list");
120
119
  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");
126
120
  program.addCommand(lookup);
127
121
  const insight = new Command("insight").description("Insight APIs");
128
122
  const opinion = new Command("opinion");
@@ -150,11 +144,12 @@ addTimeFilters(summary.command("list").option("--search-type <number>", "Search
150
144
  categoryList: maybeArray(options.category), marketList: maybeArray(options.market), participantRoleList: maybeArray(options.participantRole),
151
145
  }), { endpointKey: "insight.summary.list", idField: "summaryId" }));
152
146
  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" });
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, {
147
+ 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("--location <id>", "Location ID (domesticCity constant, via 'reference constant-list')", collectList, []).option("--format <format>", "Output format", "table").option("--output <path>", "Output path")).action((options) => emit(options, (client) => client.call(endpointKey, {
154
148
  from: parseFrom(options.from), size: parseSize(options.size), startTime: options.startTime, endTime: options.endTime, keyword: options.keyword,
155
149
  researchAreaList: maybeArray(options.researchArea), institutionList: maybeArray(options.institution), securityList: maybeArray(options.security),
156
150
  categoryList: maybeArray(options.category), marketList: maybeArray(options.market), participantRoleList: maybeArray(options.participantRole),
157
151
  brokerTypeList: maybeArray(options.brokerType), objectList: maybeArray(options.object), permission: options.permission.length ? options.permission : undefined,
152
+ locationList: maybeArray(options.location),
158
153
  })));
159
154
  addScheduleList(roadshow, "insight.roadshow.list");
160
155
  addScheduleList(siteVisit, "insight.site-visit.list");
@@ -302,7 +297,7 @@ ai.command("earnings-review").requiredOption("--security-code <code>").requiredO
302
297
  }
303
298
  }));
304
299
  ai.command("earnings-review-check").requiredOption("--data-id <id>", "dataId from earnings-review").option("--format <format>", "Output format", "json").option("--output <path>").action((options) => withClient((client) => checkAsyncContent(client, "ai.earnings-review.get-content", options.dataId, parseOutputFormat(options.format), options.output)));
305
- ai.command("theme-tracking").requiredOption("--theme-id <id>", "Theme ID (use lookup theme-id list)").requiredOption("--date <date>", "Date (yyyy-MM-dd)").option("--type <name>", "Report type: morning/night", collectList, []).option("--format <format>", "Output format", "json").option("--output <path>").action((options) => emit(options, (client) => {
300
+ ai.command("theme-tracking").requiredOption("--theme-id <id>", "Theme ID (use 'reference concept-search')").requiredOption("--date <date>", "Date (yyyy-MM-dd)").option("--type <name>", "Report type: morning/night", collectList, []).option("--format <format>", "Output format", "json").option("--output <path>").action((options) => emit(options, (client) => {
306
301
  const typeList = options.type.length ? options.type : undefined;
307
302
  return client.call("ai.theme-tracking", { themeId: options.themeId, date: options.date, type: typeList });
308
303
  }));
@@ -355,6 +350,17 @@ reference.command("securities-search").requiredOption("--keyword <text>", "Searc
355
350
  category: options.category.length ? options.category : undefined,
356
351
  top: parseNumberOption(options.top, "--top", { integer: true, min: 1 }),
357
352
  })));
353
+ reference.command("constant-category").description("List constant categories and which API params accept them").option("--format <format>", "Output format", "table").option("--output <path>").action((options) => emit(options, (client) => client.call("reference.constant-category")));
354
+ reference.command("constant-list").requiredOption("--category <code>", "Category code from 'reference constant-category' (e.g. citicIndustry/swIndustry/regionCategory)").option("--format <format>", "Output format", "table").option("--output <path>").action((options) => emit(options, (client) => client.call("reference.constant-list", { category: options.category })));
355
+ reference.command("concept-search").requiredOption("--keyword <text>", "Search keyword (name/pinyin/group name)").option("--top <number>", "Max results (default: 10, max: 10)", "10").option("--format <format>", "Output format", "table").option("--output <path>").action((options) => emit(options, (client) => client.call("reference.concept-search", {
356
+ keyword: options.keyword,
357
+ top: parseNumberOption(options.top, "--top", { integer: true, min: 1 }),
358
+ })));
359
+ reference.command("sector-search").option("--keyword <text>", "Search keyword (name/pinyin)").option("--top <number>", "Max results (default: 10, max: 10)", "10").option("--format <format>", "Output format", "table").option("--output <path>").action((options) => emit(options, (client) => client.call("reference.sector-search", {
360
+ keyword: options.keyword,
361
+ top: parseNumberOption(options.top, "--top", { integer: true, min: 1 }),
362
+ })));
363
+ reference.command("sector-constituents").requiredOption("--sector-id <id>", "Sector ID from 'reference sector-search'").option("--format <format>", "Output format", "table").option("--output <path>").action((options) => emit(options, (client) => client.call("reference.sector-constituents", { sectorId: options.sectorId })));
358
364
  program.addCommand(reference);
359
365
  const vault = new Command("vault").description("Vault APIs");
360
366
  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" }));
@@ -390,8 +396,8 @@ alternative.command("edb-data").option("--indicator-id <id>", "Indicator ID (rep
390
396
  }
391
397
  await printData(data, parseOutputFormat(options.format), options.output);
392
398
  }));
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 })));
399
+ alternative.command("concept-info").requiredOption("--concept-id <id>", "Concept (theme index) ID, e.g. 121000130 机器人; discover via 'gangtise reference concept-search'").option("--format <format>", "Output format", "json").option("--output <path>").action((options) => emit(options, (client) => client.call("alternative.concept-info", { conceptId: options.conceptId })));
400
+ alternative.command("concept-securities").requiredOption("--concept-id <id>", "Concept (theme index) ID, e.g. 121000130 机器人; discover via 'gangtise reference concept-search'").option("--format <format>", "Output format", "json").option("--output <path>").action((options) => emit(options, (client) => client.call("alternative.concept-securities", { conceptId: options.conceptId })));
395
401
  program.addCommand(alternative);
396
402
  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) => {
397
403
  const endpoint = ENDPOINTS[endpointKey];
@@ -101,14 +101,8 @@ export class GangtiseClient {
101
101
  }
102
102
  async readLocalLookup(endpoint) {
103
103
  const keyMapping = {
104
- "lookup.research-areas.list": "research-areas",
105
104
  "lookup.broker-orgs.list": "broker-orgs",
106
105
  "lookup.meeting-orgs.list": "meeting-orgs",
107
- "lookup.industries.list": "industries",
108
- "lookup.regions.list": "regions",
109
- "lookup.announcement-categories.list": "announcement-categories",
110
- "lookup.industry-codes.list": "industry-codes",
111
- "lookup.theme-ids.list": "theme-ids",
112
106
  };
113
107
  const lookupKey = keyMapping[endpoint.key];
114
108
  if (lookupKey) {
@@ -8,13 +8,6 @@ export const ENDPOINTS = {
8
8
  description: "Get access token",
9
9
  },
10
10
  // ─── lookup (served from local data, not HTTP) ───
11
- "lookup.research-areas.list": {
12
- key: "lookup.research-areas.list",
13
- method: "GET",
14
- path: "/guide/research-area-local",
15
- kind: "json",
16
- description: "List research areas from local docs",
17
- },
18
11
  "lookup.broker-orgs.list": {
19
12
  key: "lookup.broker-orgs.list",
20
13
  method: "GET",
@@ -29,41 +22,6 @@ export const ENDPOINTS = {
29
22
  kind: "json",
30
23
  description: "List meeting orgs from local docs",
31
24
  },
32
- "lookup.industries.list": {
33
- key: "lookup.industries.list",
34
- method: "GET",
35
- path: "/guide/industries-local",
36
- kind: "json",
37
- description: "List industries from local docs",
38
- },
39
- "lookup.regions.list": {
40
- key: "lookup.regions.list",
41
- method: "GET",
42
- path: "/guide/regions-local",
43
- kind: "json",
44
- description: "List regions from local docs",
45
- },
46
- "lookup.announcement-categories.list": {
47
- key: "lookup.announcement-categories.list",
48
- method: "GET",
49
- path: "/guide/announcement-categories-local",
50
- kind: "json",
51
- description: "List announcement categories from local docs",
52
- },
53
- "lookup.industry-codes.list": {
54
- key: "lookup.industry-codes.list",
55
- method: "GET",
56
- path: "/guide/industry-codes-local",
57
- kind: "json",
58
- description: "List Shenwan industry codes from local docs",
59
- },
60
- "lookup.theme-ids.list": {
61
- key: "lookup.theme-ids.list",
62
- method: "GET",
63
- path: "/guide/theme-ids-local",
64
- kind: "json",
65
- description: "List theme IDs from local docs",
66
- },
67
25
  // ─── insight ───
68
26
  "insight.opinion.list": {
69
27
  key: "insight.opinion.list",
@@ -211,6 +169,41 @@ export const ENDPOINTS = {
211
169
  kind: "json",
212
170
  description: "Search GTS codes (securities)",
213
171
  },
172
+ "reference.constant-category": {
173
+ key: "reference.constant-category",
174
+ method: "GET",
175
+ path: "/application/open-reference/constants/category",
176
+ kind: "json",
177
+ description: "List constant categories and their API usage scopes",
178
+ },
179
+ "reference.constant-list": {
180
+ key: "reference.constant-list",
181
+ method: "POST",
182
+ path: "/application/open-reference/constants/getList",
183
+ kind: "json",
184
+ description: "List all constant values of a category",
185
+ },
186
+ "reference.concept-search": {
187
+ key: "reference.concept-search",
188
+ method: "POST",
189
+ path: "/application/open-reference/concepts/search",
190
+ kind: "json",
191
+ description: "Search concept (theme) IDs by keyword",
192
+ },
193
+ "reference.sector-search": {
194
+ key: "reference.sector-search",
195
+ method: "POST",
196
+ path: "/application/open-reference/sectors/search",
197
+ kind: "json",
198
+ description: "Search sector IDs by keyword",
199
+ },
200
+ "reference.sector-constituents": {
201
+ key: "reference.sector-constituents",
202
+ method: "POST",
203
+ path: "/application/open-reference/sectors/constituents",
204
+ kind: "json",
205
+ description: "List constituent securities of a sector",
206
+ },
214
207
  // ─── quote ───
215
208
  "quote.day-kline": {
216
209
  key: "quote.day-kline",
@@ -1,13 +1,7 @@
1
1
  const cache = new Map();
2
2
  const loaders = {
3
- "research-areas": () => import("./research-areas.js"),
4
3
  "broker-orgs": () => import("./broker-orgs.js"),
5
4
  "meeting-orgs": () => import("./meeting-orgs.js"),
6
- "industries": () => import("./industries.js"),
7
- "regions": () => import("./regions.js"),
8
- "announcement-categories": () => import("./announcement-categories.js"),
9
- "industry-codes": () => import("./industry-codes.js"),
10
- "theme-ids": () => import("./theme-ids.js"),
11
5
  };
12
6
  export async function getLookupData(key) {
13
7
  if (cache.has(key))
@@ -29,5 +29,10 @@ export function normalizeRows(value) {
29
29
  const hasMeta = Object.keys(meta).length > 0;
30
30
  return hasMeta ? { ...meta, list: chatRoomList } : chatRoomList;
31
31
  }
32
+ if (Array.isArray(record.constants)) {
33
+ const { constants, ...meta } = record;
34
+ const hasMeta = Object.keys(meta).length > 0;
35
+ return hasMeta ? { ...meta, list: constants } : constants;
36
+ }
32
37
  return value;
33
38
  }
@@ -1,2 +1,2 @@
1
1
  // Auto-generated — DO NOT EDIT
2
- export const CLI_VERSION = "0.15.1";
2
+ export const CLI_VERSION = "0.16.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gangtise-openapi-cli",
3
- "version": "0.15.1",
3
+ "version": "0.16.0",
4
4
  "description": "CLI for Gangtise OpenAPI",
5
5
  "license": "MIT",
6
6
  "repository": {