gangtise-openapi-cli 0.47.0 → 0.49.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
@@ -6,18 +6,19 @@
6
6
 
7
7
  README 仅列最近 5 个版本摘要:
8
8
 
9
+ - **v0.49.0 — 2026-10-28**:① **新增 `gangtise update`**:一条命令更新到最新版(从 npmjs.org `npm install -g`),并替换 `~/.claude` / `~/.codex` / `~/.openclaw/workspace` / `~/.hermes` 下版本较旧的 Agent Skill 副本;`--check` 只查不装,`--no-skill` 不动 skill 副本。② **新版本提示**:在终端里运行任何命令时,若已有新版本,结束后 stderr 提示一行(同一新版本约一天一次);版本信息在后台刷新、命令不等网络,脚本 / Agent / CI 中不提示,`GANGTISE_NO_UPDATE_CHECK=1` 关闭。③ **管理层情绪的 `publishTime` 现为北京时间**,与 `--start-time` / `--end-time` 同一口径;按 v0.48.0 说明「先加 8 小时」处理过的脚本要去掉这一步。④ **路演 / 调研 / 策略会 / 论坛列表的翻页去重改按各自的 ID**(`roadshowId` / `siteVisitId` / `strategyMeetingId` / `forumId`):跨页重复的行会被去掉、结果标为不完整(退出码 3),提示缩短时间范围重拉;`response-schema.md` 同步更正这四个列表的字段名。⑤ **公募基金**:`fund holder-structure` 的 `holderCount` 现为整数,按 v0.44.0 说明「先去千分位逗号再转数字」的处理要去掉(对整数做字符串替换会报错);`fund etf-pcf-components` 的接口只返回成分名称,CLI 按名称匹配补上 `componentCode`(带市场后缀,如 `600519.SH` / `00941.HK`;用该行收盘价核对,A+H 同名两行分别匹配;现金行等匹配不到的为 `null`,stderr 列出),`--no-component-code` 关闭。⑥ `ai security-clue` 的 `--query-mode` / `--gts-code` 补充帮助说明。
10
+
11
+ - **v0.48.0 — 2026-10-24**:① **新增管理层情绪三个命令**:`ai management-sentiment-indicator`(每场业绩会的正面回应程度 / 回复乐观度 / 回复确定度评分与按问题类型拆分)、`ai management-sentiment-conclusion`(情绪结论与较上一场的问答对照),各 5 积分/条、自动翻页;`ai management-sentiment-detail` 按列表返回的 `source.sourceId` 取逐条问答评分与依据,10 积分/次。⚠️ 返回的 `publishTime` 比北京时间早 8 小时(时间筛选按北京时间),按时刻展示或按日期归档前先加 8 小时。② **新增 `bond default-list` 违约债券**(0.4 积分/次):按违约日期区间(不超过 1 年)/ `--security` / `--issuer` 三选一;`--issuer` 一次一个、用发行人全称,简称只返回最匹配的一家主体,核对 `issuerName`。③ 文档:EDE 基金指标(分类、复权净值、区间收益与风险指标、同类排名),区间指标的参数须显式传。④ 依赖 `undici` 升级到 7.30.1(含上游安全修复)。
12
+
9
13
  - **v0.47.0 — 2026-10-17**:① **`insight opinion list` 新增 `--category`(观点类别:业绩点评 / 异动点评 / 调研访谈更新 / 交易台 / 行业分析)与 `--broker-tag`(头部券商 / 新财富团队)**,返回行带 `categoryList` / `brokerTagList`。② **`--llm-tag` 改为 28 项投研业务标签**(强烈推荐、超预期、涨价、龙头等,取值见 `--help`);⚠️ **存量脚本要改**:业绩点评、头部券商、新财富团队改用 `--category earningsReview` / `--broker-tag topBroker` / `--broker-tag newFortune`,传给 `--llm-tag` 报 `100005`。同一选项传多个值取并集。③ `--with-content` 不支持 `--category` / `--broker-tag`(本地报错、不发请求),其 `--llm-tag` 仍只收旧的四个值;要按新标签筛又要正文,用 `list` + `detail`。④ **`bond daily-quote` / `bond valuation` 首行比起始日晚超过 14 天时 stderr 提示**(上市较晚或数据起点较晚;多只同查只看整批最早的首行,逐只起点按 `securityCode` 核对)。⑤ 文档:股东结构类 EDE 指标(`shr_*`),大股东系列须传 `rank`、户均持股系列须传 `shareType`。
10
14
 
11
15
  - **v0.46.0 — 2026-10-14**:① **`reference securities-search --category bond`**:按代码或简称搜债券,代码用于 `bond` 命令组。⚠️ **可转债可能出现在任何 `--category` 的结果里**(按简称查股票时常排第一),**只取返回行 `category` 等于所查类别的那一行**;债券代码传给 `fund` 命令会返回空结果、不报错。② **场外基金可按名称搜到**(`--category fund` 同时返回 `.OF` 与场内代码)。③ **计费单价按平台最新价目更新**:一页通等 AI 生成 10/次、业绩点评 / 观点 PK 20/次、热点话题 10/篇、个股看点 1/条、路演四件套 10/条、独立观点列表 1/条、纪要 / 外资研报 / 我的会议下载 30/篇、公告下载 A 股 1 / 港美股 2、`edb-data` 30/指标;额度保护随之调整。④ **数据范围**:`SKILL.md` 按官方权限表逐命令列出正式 / 试用两档,规划查询区间按表来。⑤ **报错口径**:EDE 漏传必填的 `fiscalYear` 报 `100001`;`main-business` / `valuation-analysis` 的 `--field` 写错名字报 `100005`;`screener` 计费按筛选前范围保守估算。完整列表见 CHANGELOG。
12
16
 
13
17
  - **v0.45.0 — 2026-10-07**:① **`vault drive-copy` 可复制整个文件夹**:`--folder-id` + `--target-parent-id`,连同子文件夹与文件复制到另一空间,返回 `{folderId, newFolderId}`;复制文件的写法不变,两组参数二选一。② **下载文件名**:响应同时给出 UTF-8 文件名与普通文件名时用 UTF-8 的那个。③ `GANGTISE_BASE_URL` 指向非本机地址却用 `http://` 时 stderr 提示一次(凭证会明文传输)。④ 文档:`bond daily-quote` / `bond valuation` 的数据可能比账号窗口短,**按年份取长序列时核对首行日期**;`bond announcement` 按日期查询时有代码的行都带后缀;基金只知道名称时怎么换代码;分钟 K 的可查历史是滚动的短窗口。
14
18
 
15
- - **v0.44.0 — 2026-09-30**:**新增 `fund` 公募基金 18 个命令,一律 0.4 积分/次**(按次,与返回行数、基金只数无关):`basic-info` / `nav`(日频净值)/ `fee-rate` / `manager-info`(按姓名精确匹配,同名全返回)/ `manager-history` / `asset-size` / `holder-structure` / `top10-holders` / `asset-allocation` / `stock-portfolio` / `industry-allocation` / `bond-portfolio` / `bond-type-allocation` / `fund-portfolio` / `fund-type-allocation` / `etf-pcf-header` / `etf-pcf-components` / `etf-share-change`。`--security` 用带大写后缀的基金代码(场外 `.OF`,场内 `.SH` / `.SZ`),**代码不存在、不带后缀或后缀小写都返回空结果、不报错**。不分页:单次上限 10000 行,超出整批报 `100006`;日期两端都不传取账号可回溯窗口内的全部,起点早于窗口整批报 `110003`。**整族超时 / 5xx 不自动重发**(避免重复扣费)。取数前注意:各命令单位不同(元 / 万元 / 万份 / 万股);`holder-structure` 的 `holderCount` 是带千分位的字符串;持仓明细只返回证券简称、不返回代码;`fund-type-allocation` 季报期只含重仓基金。逐条见 `gangtise-openapi/references/commands/fund.md`。**另有几处行为变化**:只收一个值的参数重复传直接报错;代码类列表也按顿号 / 分号 / 空格分隔并去重;显式 `--size` 与 `fundamental earning-forecast` 的日期区间估算超过 1000 积分同样要 `--yes`;jsonl / csv 文件一律以换行结尾,csv 不再给 `-3.5%` 这类值加 `'`;全市场分片早于账号窗口的部分跳过而不中止;接口返回下载链接时不带 `--output` 也会下载;A 股公告与 `ai knowledge-batch` 只写日期的 `--end-time` 按当日 23:59:59 换算;`--indicator-param` 里的日期值与 `--date` 同样校验;单次请求另有总时长上限(`GANGTISE_TIMEOUT_MS` 的 2 倍,下载与上传 60 倍);环境变量设成空字符串按未设置处理。完整列表见 CHANGELOG。
16
-
17
- - **v0.43.1 — 2026-09-27**:① **EDE `fiscalYear` 漏传不报错**:预测类(`frcst_*`)与分红类(`div_cash_yr` 等)漏传时按服务端自定的默认年度取数,数看着正常但未必是你要的年度——**一律显式传 `fiscalYear`**;其余必填参数缺失仍报 `100001`。② **翻页去重**:同一 ID 的任一已出现版本再次出现都按重复去掉(计入 `duplicateRows`),不区分字段顺序。③ **观点 `detail`**:某一批夹有空元素或格式异常的元素时,这一批已返回的正文照常输出;空元素的 ID 列在 `missingIds`,其余没取到的列在 `unfetchedIds`(`unfetchedError` 带 traceId)。④ 文档:`finc_roe_avg_avg` 示例补 `reportDate`;债券三个评级命令的计费说明统一。
18
-
19
19
  ### 历史里程碑
20
20
 
21
+ - **v0.44.0**:新增公募基金 `fund` 命令组 18 个命令(0.4 积分/次),额度保护覆盖显式 `--size`。
21
22
  - **v0.43.0**:按条计费列表的额度保护(省略 `--size` 时估算超过 1000 积分须 `--yes`);翻页结果的跨页重复、版本变化与 `total` 封顶检测。
22
23
  - **v0.42.0**:数值参数只收十进制写法(`0x10` / `1e3` 这类写法发请求前报错);接管道时保留退出码 `3` / `4`。
23
24
  - **v0.41.0**:新增债券 12 个命令与云盘管理 9 个命令;观点列表改走 v2(列表只含摘要 1 积分/条,正文按 ID 另取);新增会议线索与联网搜索。
@@ -48,13 +49,17 @@ npm install -g gangtise-openapi-cli
48
49
  gangtise --help
49
50
  ```
50
51
 
51
- 更新到最新版(在终端里运行 `gangtise --version` 会与线上版本比对并提示,比对结果一天内复用):
52
+ 更新到最新版:
52
53
 
53
54
  ```bash
54
- npm update -g gangtise-openapi-cli
55
+ gangtise update # 从 npmjs.org 安装最新版(npm install -g),并更新已安装的 Agent Skill 副本
56
+ gangtise update --check # 只查看有没有新版本,不安装
55
57
  ```
56
58
 
57
- > 更新后若使用 Agent Skill:包内 skill 已随包更新,但复制到 `~/.claude/skills/` 等目录的副本是快照,**需重新执行下方「安装」段的复制命令**才能让 AI 助手拿到新版 skill。
59
+ - `update` 命令从 v0.49.0 起提供,更早的版本先运行一次 `npm install -g gangtise-openapi-cli@latest`
60
+ - `gangtise update` 只更新通过 `npm install -g` 安装的 CLI;从仓库 clone 运行的,按原方式 `git pull` 后重新构建。npm 报权限错误(`EACCES`)时,按提示用 `sudo npm install -g …` 安装后,再运行一次 `gangtise update` 更新 Agent Skill 副本
61
+ - Agent Skill:`~/.claude/skills/`、`~/.codex/skills/`、`~/.openclaw/workspace/skills/`、`~/.hermes/skills/` 下版本低于新版的 `gangtise-openapi` 副本会被替换(**对副本做过的本地修改会被覆盖**);版本相同或更新的副本、符号链接、以及其他目录下的副本不动。加 `--no-skill` 则不碰 skill 副本
62
+ - 在终端里运行任何命令时,若已有新版本,命令结束后 stderr 提示一行(同一个新版本大约一天提示一次)。版本信息在后台刷新,命令本身不等待网络;输出不是终端时(脚本、Agent、CI)不提示。设 `GANGTISE_NO_UPDATE_CHECK=1` 可关闭提示与后台查询
58
63
 
59
64
  本地开发:
60
65
 
@@ -80,6 +85,7 @@ export GANGTISE_PAGE_CONCURRENCY=5 # 翻页/分片并发数(默认 5,上
80
85
  export GANGTISE_VERBOSE=1 # 打印每个请求的耗时与字节数
81
86
  export GANGTISE_TIMEOUT_MS=30000 # 请求超时,整数毫秒(默认 30s;上限 1 小时;低于 1 秒或写法非法时用默认值;未按所写数值生效时在 stderr 提示)。按「多久没收到数据」计时,另有单次请求总时长上限:普通请求为它的 2 倍,下载与上传为 60 倍。one-pager 等生成类命令至少 120 秒、上传类至少 300 秒,设得更短不生效
82
87
  export GANGTISE_TOKEN_CACHE_PATH=... # 覆盖 token 缓存路径(默认 ~/.config/gangtise/token.json)
88
+ export GANGTISE_NO_UPDATE_CHECK=1 # 关闭新版本提示与后台版本查询
83
89
  ```
84
90
 
85
91
  环境变量设成空字符串(如 `GANGTISE_BASE_URL=`)按未设置处理,取默认值。`GANGTISE_BASE_URL` 指向非本机地址却用 `http://` 时,CLI 在 stderr 提示一次:登录与请求里的 AK / SK / token 会明文传输。
@@ -103,9 +109,9 @@ gangtise-openapi/
103
109
  ├── SKILL.md # 主 skill 文件(必备规则、速查表、按需引用 references)
104
110
  └── references/
105
111
  ├── commands/ # 按命令组拆分的详细参数文档(agent 按需 Read)
106
- │ ├── ai.md # AI 能力(知识库 / 个股线索与看点 / 一页通等生成类 / 业绩点评与观点 PK 异步任务 / 热点 / 管理层讨论)
112
+ │ ├── ai.md # AI 能力(知识库 / 个股线索与看点 / 一页通等生成类 / 业绩点评与观点 PK 异步任务 / 热点 / 管理层讨论 / 管理层情绪)
107
113
  │ ├── alternative.md # 行业指标数据库 EDB(search / data)+ 题材指数画像与成分股
108
- │ ├── bond.md # 债券(基本资料 / 发行人 / 行情与估值 / 兑付 / 公告 / 发行 / 评级 / 行权)
114
+ │ ├── bond.md # 债券(基本资料 / 发行人 / 行情与估值 / 兑付 / 公告 / 发行 / 评级 / 行权 / 违约)
109
115
  │ ├── fund.md # 公募基金(基本信息 / 净值 / 费率 / 经理 / 规模与持有人 / 资产配置 / 持仓与分布 / ETF 申赎与份额)
110
116
  │ ├── fundamental.md # 财务数据(A股/港股/美股三大报表 / 主营 / 估值 / 盈利预测 / 股东)
111
117
  │ ├── indicator.md # 证券级数据指标 EDE(search / 截面 / 时序 / 条件选股)
@@ -137,7 +143,7 @@ install_skill ~/.hermes/skills # Hermes
137
143
 
138
144
  > 从仓库 clone 开发时,把 `$SKILL_SRC` 换成仓库内 `gangtise-openapi` 目录的绝对路径即可。
139
145
 
140
- > **版本更新**:`SKILL.md` 头部的 `version` 字段与 CLI 版本一致。`npm update -g gangtise-openapi-cli` 之后重新执行上面这段,再看已安装副本的 `version` 是否与 `gangtise --version` 相同。对已安装副本做过的本地修改会被覆盖。
146
+ > **版本更新**:`SKILL.md` 头部的 `version` 字段与 CLI 版本一致。`gangtise update` 会一并替换上面四个目录里版本较旧的副本;装在其他目录的副本,更新 CLI 后重新执行上面这段,再看已安装副本的 `version` 是否与 `gangtise --version` 相同。对已安装副本做过的本地修改会被覆盖。
141
147
 
142
148
  安装后,可以用自然语言触发,例如:
143
149
  - "帮我查今天所有的研报"
@@ -204,6 +210,7 @@ install_skill ~/.hermes/skills # Hermes
204
210
  | | `issuer-rating-change` | 发债主体评级变动历史(`--security` 或 `--issuer` 二选一) |
205
211
  | | `issuance-plan` | 利率债发行计划(按发行日期区间) |
206
212
  | | `exercise-notice` | 含权债行权提示(行权安排与行权结果) |
213
+ | | `default-list` | 违约债券(按违约日期区间 / 债券代码 / 单个发行人三选一) |
207
214
  | **Fund** | `basic-info` | 公募基金基本信息(分类、管理人与托管人、成立与存续、申赎规则、业绩基准、风险等级、跟踪指数) |
208
215
  | | `nav` | 基金日频净值(单位 / 累计 / 复权净值、复权因子;货币基金七日年化与万份收益) |
209
216
  | | `fee-rate` | 基金费率(申购 / 赎回 / 管理 / 托管 / 销售服务,按条件分档) |
@@ -227,6 +234,8 @@ install_skill ~/.hermes/skills # Hermes
227
234
  | | `research-outline` | 研究提纲 |
228
235
  | | `management-discuss-announcement` | 管理层讨论-财报 |
229
236
  | | `management-discuss-earnings-call` | 管理层讨论-业绩会 |
237
+ | | `management-sentiment-indicator` / `management-sentiment-conclusion` | 管理层情绪指标 / 结论(每场业绩会一条;5 积分/条) |
238
+ | | `management-sentiment-detail` | 管理层情绪明细(一场会议的逐条问答评分与依据;10 积分/次) |
230
239
  | | `viewpoint-debate` / `viewpoint-debate-check` | 观点PK(异步) |
231
240
  | **Vault** | `drive-list` / `drive-download` | 云盘文件列表与下载 |
232
241
  | | `drive-folder-list` | 云盘目录浏览(某文件夹下的直接子文件夹与文件) |
@@ -269,6 +278,7 @@ install_skill ~/.hermes/skills # Hermes
269
278
  - `gangtise reference ...`
270
279
  - `gangtise tool ...`
271
280
  - `gangtise raw call ...` / `gangtise raw list`
281
+ - `gangtise update`(更新 CLI 与 Agent Skill 副本)
272
282
 
273
283
  ## 推荐工作流
274
284
 
@@ -306,7 +316,7 @@ gangtise ai knowledge-batch --query 比亚迪 --query 最近热门概念
306
316
  - **流式下载**:二进制响应(PDF 等)直接 `pipeline` 到磁盘,不经过内存缓冲;不带 `--output` 时先写入临时文件,定好文件名后再改名。50MB PDF 内存占用近乎为零。
307
317
  - **流式输出**:`--format jsonl` 或 `csv` 加 `--output <file>` 时,翻页 / 全市场分片 / 多证券分批请求的行**按到达顺序逐批写盘**,取数阶段不持有整份结果,内存不随行数增长;csv 先落临时行文件、收尾时按列并集写表头再转成 csv(磁盘两遍、内存不变)。jsonl / csv 无论写文件还是输出到终端都按批写出,每行以换行结尾。任一只 / 页 / 片失败,命令退出时后台已无取数与写盘,不留 `.part`。
308
318
  - **导出元信息**:`csv` / `jsonl` 落盘时旁边生成 `<文件>.meta.json`——命令行、数据行数、列名、`complete`(写元信息时的判定:退出 3 即 `false`)、`total` / `partial` / `failedPages` / `failedShards` / `truncatedShards` / `droppedColumns` / `missingFields` / `duplicateRows` / `changedRows` / `totalCapped` 等全部完整性标记(以及不影响完整性判定的 `outOfWindowShards`)、抓取时间与时区、CLI 版本,以及数据文件的字节数 `bytes` 与内容哈希 `sha256`。命令行与结果里的 key / secret / token 类字段写成 `[redacted]`。元信息在数据文件发布之后才就位,导出失败不会留下新数据配旧元信息。**转交或归档前建议核一次**:`shasum -a 256 <文件>` 与元信息里的 `sha256` 相等,才说明这份元信息描述的就是旁边这份数据——两个进程同时导出到**同一个 `--output`** 时,最终文件一定是其中某一次的完整产物,但旁边的元信息有可能来自另一次,核哈希就能发现。**输的那一次自己也会报**:收尾时核对 `--output` 是否仍是本次写出的文件(仍是同一个文件就不必回读,否则回读比对哈希),不符就在 stderr 说明「这个文件不是本次命令的产物」并**退出码 4**。这两种格式的文件本身只有数据行,转交之后靠它核验是否完整;`json` 自带标记,不生成。
309
- - **自动重试**:5xx / 429 / `ECONNREFUSED` / `ECONNRESET` / `ETIMEDOUT` / `ENOTFOUND` / `EAI_AGAIN` / `UND_ERR_*`(undici 连接/超时类)/ `999999` 系统错误自动指数退避重试 2 次。**不重放的端点例外**(贵档:one-pager 等生成/提交类 + `tool file-parse` 提交 + 30/篇 的 summary / foreign-report / my-conference 下载 + 单价未公布但保守同档的 pamirs-summary 下载 + 题材 `concept-info` / `concept-securities`(含 `--full`);`bond` 全部 12 个命令(多数按次,三个评级命令按条 / 按债券 / 按发行人)、`fund` 全部 18 个命令(按次)与按次计费的 `tool web-search`;按条计费的 `insight highlight list`、观点 `detail` 与 `list --with-content`,以及单次最多 6000 只、按只计费的 `ai stock-summary` 与行数随日期区间增长的 `fundamental earning-forecast`;另加不计分但不可重复执行的 `vault stock-pool-create` 与云盘的上传 / 新建文件夹 / 复制 / 两个删除,共 64 个):5xx/超时不重放,仅连接失败、429 与 token 自愈重试。**两类端点各有各的理由**:贵档是**重放会重复扣分**——服务端可能已经执行并计费,重发按次计费的再扣一次,重发按篇/按条计费的会把已交付的行再计一次;不计分的那几个是**重放会造成副作用或把成功报成失败**——股票池名不允许重复,重发一个其实已经建成的请求,回来的是 `230006 股票池名称重复`;云盘允许同名,重发上传 / 新建 / 复制会多出一份;重发一个其实已经删掉的删除,回来的是「文件不存在」或 `130002`。**`indicator`(EDE)端点对 `999999` 不重试**——重放一次已计费的查询没有意义(EDE 无数据不用此码,而是保留行列的占位单元格 `null`;代码或参数名写错报 `100003`、缺必填参数报 `100001`)。**终态码 `999011`(凭证无效)/ `140002`(终态失败:异步生成失败、参数错误或业务处理失败)在任何 HTTP 状态下都不重试**——凭证错不会因重试而变,`140002` 立即重发得到的还是同一结果。
319
+ - **自动重试**:5xx / 429 / `ECONNREFUSED` / `ECONNRESET` / `ETIMEDOUT` / `ENOTFOUND` / `EAI_AGAIN` / `UND_ERR_*`(undici 连接/超时类)/ `999999` 系统错误自动指数退避重试 2 次。**不重放的端点例外**(贵档:one-pager 等生成/提交类 + `tool file-parse` 提交 + 30/篇 的 summary / foreign-report / my-conference 下载 + 单价未公布但保守同档的 pamirs-summary 下载 + 题材 `concept-info` / `concept-securities`(含 `--full`)+ `ai management-sentiment-detail`;`bond` 全部 13 个命令(多数按次,三个评级命令按条 / 按债券 / 按发行人)、`fund` 全部 18 个命令(按次)与按次计费的 `tool web-search`;按条计费的 `insight highlight list`、观点 `detail` 与 `list --with-content`,以及单次最多 6000 只、按只计费的 `ai stock-summary` 与行数随日期区间增长的 `fundamental earning-forecast`;另加不计分但不可重复执行的 `vault stock-pool-create` 与云盘的上传 / 新建文件夹 / 复制 / 两个删除,共 66 个):5xx/超时不重放,仅连接失败、429 与 token 自愈重试。**两类端点各有各的理由**:贵档是**重放会重复扣分**——服务端可能已经执行并计费,重发按次计费的再扣一次,重发按篇/按条计费的会把已交付的行再计一次;不计分的那几个是**重放会造成副作用或把成功报成失败**——股票池名不允许重复,重发一个其实已经建成的请求,回来的是 `230006 股票池名称重复`;云盘允许同名,重发上传 / 新建 / 复制会多出一份;重发一个其实已经删掉的删除,回来的是「文件不存在」或 `130002`。**`indicator`(EDE)端点对 `999999` 不重试**——重放一次已计费的查询没有意义(EDE 无数据不用此码,而是保留行列的占位单元格 `null`;代码或参数名写错报 `100003`、缺必填参数报 `100001`)。**终态码 `999011`(凭证无效)/ `140002`(终态失败:异步生成失败、参数错误或业务处理失败)在任何 HTTP 状态下都不重试**——凭证错不会因重试而变,`140002` 立即重发得到的还是同一结果。
310
320
 
311
321
  <!-- no-replay-endpoints
312
322
  上面那句点名的「不重放」端点,完整清单如下(endpoint key,与 `gangtise raw list` 一致):
@@ -316,6 +326,7 @@ ai.investment-logic
316
326
  ai.knowledge-batch
317
327
  ai.management-discuss-announcement
318
328
  ai.management-discuss-earnings-call
329
+ ai.management-sentiment-detail
319
330
  ai.one-pager
320
331
  ai.peer-comparison
321
332
  ai.research-outline
@@ -330,6 +341,7 @@ bond.announcement
330
341
  bond.basic-info
331
342
  bond.cash-flow
332
343
  bond.daily-quote
344
+ bond.default-list
333
345
  bond.exercise-notice
334
346
  bond.issuance-detail
335
347
  bond.issuance-plan
@@ -410,10 +422,12 @@ vault.stock-pool.create
410
422
  - `vault wechat-message-list`
411
423
  - `vault wechat-chatroom-list`
412
424
  - `ai hot-topic`
425
+ - `ai management-sentiment-indicator`
426
+ - `ai management-sentiment-conclusion`
413
427
 
414
428
  规则:
415
429
  - **省略 `--size` 一律拉全量**(无论是否传时间范围),CLI 自动翻页查完
416
- - **按条计费的列表有额度保护**:省略 `--size` 时,CLI 先拿到 `total`,按 `total × 单价` 估算全量要花的积分,超过 1000 积分就报错、退出码 1,不再往下拉,报错写明估算值。整页不超过 50 积分的列表用第一页拿 `total`;整页更贵的(路演 / 调研 / 策略会 / 论坛、会议线索、个股线索、热点话题、`--with-content`)先只取 1 条:结果最多 1 条时它就是全部结果;否则放行后从头翻页,这 1 条会多计一次费(知道要多少条就直接传 `--size N`,不走这一步)。确认要全量就加 `--yes`,只要一部分就传 `--size N`——显式 `--size` 按 `min(--size, total) × 单价` 估算仍超过 1000 积分时同样报错(`--size 100000` 不能当「全量」绕过),要 `--yes`。`fundamental earning-forecast` 不分页,按日期区间估算(工作日数 × 3 条 × 0.5),同一条线。免费列表不受影响
430
+ - **按条计费的列表有额度保护**:省略 `--size` 时,CLI 先拿到 `total`,按 `total × 单价` 估算全量要花的积分,超过 1000 积分就报错、退出码 1,不再往下拉,报错写明估算值。整页不超过 50 积分的列表用第一页拿 `total`;整页更贵的(路演 / 调研 / 策略会 / 论坛、会议线索、个股线索、热点话题、管理层情绪指标 / 结论、`--with-content`)先只取 1 条:结果最多 1 条时它就是全部结果;否则放行后从头翻页,这 1 条会多计一次费(知道要多少条就直接传 `--size N`,不走这一步)。确认要全量就加 `--yes`,只要一部分就传 `--size N`——显式 `--size` 按 `min(--size, total) × 单价` 估算仍超过 1000 积分时同样报错(`--size 100000` 不能当「全量」绕过),要 `--yes`。`fundamental earning-forecast` 不分页,按日期区间估算(工作日数 × 3 条 × 0.5),同一条线。免费列表不受影响
417
431
  - 数据量未知时,可先 `--size 1` 从 stderr 的 `Total: N` 探明量级,再决定是否全量
418
432
  - 如果显式传了 `--size`,则按指定值翻页,直到达到 `size` 或数据取完
419
433
  - `--from` 必须是非负整数,`--size` 必须是正整数;非法数字会在本地直接报 `ValidationError`,不会继续请求 API
@@ -690,6 +704,10 @@ gangtise bond issuance-plan --start-date 2026-09-01 --end-date 2026-09-30
690
704
  # 公告:按债券码 或 按公告日期区间,二选一(同传报 100003)
691
705
  gangtise bond announcement --security 019742.SH --page-size 50
692
706
  gangtise bond announcement --start-date 2026-09-18 --end-date 2026-09-19 --page-no 2 --page-size 50
707
+
708
+ # 违约债券:违约日期区间(不超过 1 年)/ --security / --issuer 三选一;--issuer 一次一个、用发行人全称
709
+ gangtise bond default-list --start-date 2026-09-01 --end-date 2026-10-20
710
+ gangtise bond default-list --issuer 上海世茂股份有限公司 --field securityName --field defaultAmount
693
711
  ```
694
712
 
695
713
  > **`bond announcement` 需要手动翻页**:它是本系列唯一分页的接口,且响应**不返回 `total`**,因此不走 CLI 的自动翻页——`--page-no` 从 1 开始逐页递增,翻过末页返回空数组(退出码 0)即停;条件下一条都没有时第 1 页就返回 `130001`(退出码 1)。按日期区间查询时,部分行的 `securityCode` 为空(不对应已上市证券的公告等),这类行无法接着查其他债券命令。其余债券命令一次返回全部匹配行。
@@ -724,7 +742,7 @@ gangtise fund etf-share-change --security 510300.SH --start-date 2026-09-01 --en
724
742
 
725
743
  > **基金命令一律按次计费 0.4 积分**(与返回行数、基金只数无关,多只基金合并成一次调用最省),超时 / 5xx 不自动重放。不分页:单次上限 10000 行,超出整批报 `100006`,减少只数或缩短区间分批查。**代码不存在、不带后缀或后缀小写都返回空结果、不报错**——拿到空先核对代码。日期两端都不传 = 取账号可回溯窗口内的全部;起点早于窗口整批报 `110003`。
726
744
 
727
- > **取数前注意**:各命令单位不同(`nav` / `asset-allocation` 为元,`asset-size` 为万份 / 万元,持仓明细为万股 / 万张 / 万元);`holder-structure` 的 `holderCount` 是带千分位的字符串;持仓明细只返回证券简称、不返回代码;`fund-type-allocation` 季报期只含重仓基金(`positionType: top`),中报 / 年报为全部持基(`all`)。逐条见 `gangtise-openapi/references/commands/fund.md`。
745
+ > **取数前注意**:各命令单位不同(`nav` / `asset-allocation` 为元,`asset-size` 为万份 / 万元,持仓明细为万股 / 万张 / 万元);持仓明细只返回证券简称、不返回代码;ETF 申赎成分的 `componentCode` 由 CLI 按名称匹配补上(匹配不到为 `null`,`--no-component-code` 关闭);`fund-type-allocation` 季报期只含重仓基金(`positionType: top`),中报 / 年报为全部持基(`all`)。逐条见 `gangtise-openapi/references/commands/fund.md`。
728
746
 
729
747
  ### AI
730
748
 
@@ -751,6 +769,11 @@ gangtise ai management-discuss-announcement --report-date 2025-12-31 --security-
751
769
  gangtise ai management-discuss-announcement --report-date 2025-12-31 --security-code 000001.SZ --dimension all
752
770
  # 管理层讨论-业绩会
753
771
  gangtise ai management-discuss-earnings-call --report-date 2025-06-30 --security-code 000001.SZ --dimension financialPerformance
772
+ # 管理层情绪:指标与结论按条计费(5 积分/条),记得带 --size;publishTime 与时间筛选都是北京时间
773
+ gangtise ai management-sentiment-indicator --security 300285.SZ --size 5
774
+ gangtise ai management-sentiment-conclusion --security 300285.SZ --start-time 2026-08-01 --end-time 2026-08-31 --size 5
775
+ # 逐条问答的评分与依据:--source-id 取上面返回的 source.sourceId(10 积分/次)
776
+ gangtise ai management-sentiment-detail --source-id 554030
754
777
  # 观点PK(异步,返回 dataId)
755
778
  gangtise ai viewpoint-debate --viewpoint "飞天茅台的批价低点是1500元"
756
779
  # 等待生成完成后查询结果
package/dist/src/cli.js CHANGED
@@ -4,19 +4,21 @@ import { ACCUMULATING_PARSERS, unknownChoiceMessage } from "./core/args.js";
4
4
  import { ApiError, ValidationError } from "./core/errors.js";
5
5
  import { removeStagingFiles } from "./core/output.js";
6
6
  import { isVerbose, setVerbose } from "./core/transport.js";
7
- import { checkForUpdate } from "./core/updateCheck.js";
7
+ import { checkForUpdate, noticeUpdate } from "./core/updateCheck.js";
8
8
  import { CLI_VERSION } from "./version.js";
9
9
  import { COMMAND_GROUPS } from "./commands/groups.js";
10
10
  import { decidedExitCode, markFailed } from "./core/exitStatus.js";
11
11
  const program = new Command();
12
+ let ranCommand;
12
13
  program
13
14
  .name("gangtise")
14
15
  .description("Gangtise OpenAPI CLI")
15
16
  .version(CLI_VERSION)
16
17
  .option("--verbose", "Print per-request timings to stderr (also: GANGTISE_VERBOSE=1)")
17
- .hook("preAction", (thisCommand) => {
18
+ .hook("preAction", (thisCommand, actionCommand) => {
18
19
  if (thisCommand.opts().verbose)
19
20
  setVerbose(true);
21
+ ranCommand = actionCommand;
20
22
  });
21
23
  for (const group of COMMAND_GROUPS) {
22
24
  program.addCommand(group);
@@ -100,7 +102,7 @@ async function main() {
100
102
  const firstArg = process.argv[2];
101
103
  if (firstArg === "--version" || firstArg === "-V") {
102
104
  process.stdout.write(`${CLI_VERSION}\n`);
103
- if (process.stdout.isTTY) {
105
+ if (process.stdout.isTTY && !process.env.GANGTISE_NO_UPDATE_CHECK) {
104
106
  await Promise.race([checkForUpdate(CLI_VERSION), new Promise((resolve) => setTimeout(resolve, 2500))]);
105
107
  process.exit(0);
106
108
  }
@@ -125,5 +127,9 @@ async function main() {
125
127
  process.stderr.write("Unknown error\n");
126
128
  markFailed();
127
129
  }
130
+ finally {
131
+ if (process.stderr.isTTY && !process.env.CI && !process.env.GANGTISE_NO_UPDATE_CHECK && ranCommand?.name() !== "update")
132
+ noticeUpdate(CLI_VERSION);
133
+ }
128
134
  }
129
135
  void main();
@@ -6,7 +6,7 @@ import { ValidationError } from "../core/errors.js";
6
6
  import { markFailed } from "../core/exitStatus.js";
7
7
  import { parseOutputFormat } from "../core/output.js";
8
8
  import { printData } from "../core/printer.js";
9
- import { emit, withClient, runDownload, multiChoice, field, required, date, list, choiceList, flag, format, output, from, size, requestBody, query } from "./shared.js";
9
+ import { emit, withClient, runDownload, multiChoice, field, required, date, list, choiceList, flag, format, output, from, size, startTime, endTime, requestBody, query } from "./shared.js";
10
10
  import { checkMarketKeywords } from "./quote.js";
11
11
  export const ai = new Command("ai").description("AI APIs");
12
12
  function addGenerationTask(name, spec) {
@@ -64,8 +64,8 @@ query(ai, "security-clue", {
64
64
  from(), size(),
65
65
  field(new Option("--start-time <datetime>", "Start time").argParser(datetimeArg("--start-time")).makeOptionMandatory(), (v) => ({ startTime: v })),
66
66
  field(new Option("--end-time <datetime>", "End time").argParser(datetimeArg("--end-time")).makeOptionMandatory(), (v) => ({ endTime: v })),
67
- field(new Option("--query-mode <mode>").choices(["bySecurity", "byIndustry"]).makeOptionMandatory(), (v) => ({ queryMode: v })),
68
- list("--gts-code <code>", "GTS code", "gtsCodeList"),
67
+ field(new Option("--query-mode <mode>", "bySecurity: --gts-code takes stock codes; byIndustry: Shenwan industry codes").choices(["bySecurity", "byIndustry"]).makeOptionMandatory(), (v) => ({ queryMode: v })),
68
+ list("--gts-code <code>", "Stock code (e.g. 600519.SH) or Shenwan industry code (e.g. 821035.SWI), per --query-mode; repeatable", "gtsCodeList"),
69
69
  choiceList("--source <name>", "Source: researchReport/conference/announcement/view (repeat)", "source", ["researchReport", "conference", "announcement", "view"]),
70
70
  format(), output(),
71
71
  ],
@@ -123,6 +123,30 @@ query(ai, "management-discuss-earnings-call", {
123
123
  format("json"), output(),
124
124
  ],
125
125
  });
126
+ for (const [name, endpoint, description] of [
127
+ ["management-sentiment-indicator", "ai.management-sentiment-indicator", "Management sentiment scores per earnings call: responsiveness / polarity / conviction (0-5), overall and by question type (5 credits/row)"],
128
+ ["management-sentiment-conclusion", "ai.management-sentiment-conclusion", "Management sentiment conclusions per earnings call, with the changes against the previous call (5 credits/row)"],
129
+ ]) {
130
+ query(ai, name, {
131
+ description,
132
+ endpoint,
133
+ fields: [
134
+ from(), size("Total rows to return; omit to fetch all (max page 100). Billed per row — bound this on wide ranges"),
135
+ startTime("Publish time from (yyyy-MM-dd or yyyy-MM-dd HH:mm:ss)"), endTime("Publish time to (yyyy-MM-dd or yyyy-MM-dd HH:mm:ss; a bare date covers the whole day)"),
136
+ list("--security <code>", "Security code, e.g. 300285.SZ (repeatable); omit for all. One invalid code rejects the whole call (120001)", "securityList"),
137
+ format("json"), output(),
138
+ ],
139
+ });
140
+ }
141
+ query(ai, "management-sentiment-detail", {
142
+ description: "Per-Q&A sentiment scores and the quotes behind them, for one earnings call (10 credits/call)",
143
+ endpoint: "ai.management-sentiment-detail",
144
+ fields: [
145
+ required("--source-id <id>", "source.sourceId from management-sentiment-indicator / -conclusion", "sourceId"),
146
+ field(new Option("--source-type <type>", "source.sourceType from the same row").default("conference"), (v) => ({ sourceType: v })),
147
+ format("json"), output(),
148
+ ],
149
+ });
126
150
  addGenerationTask("viewpoint-debate", {
127
151
  submit: "ai.viewpoint-debate.get-id",
128
152
  content: "ai.viewpoint-debate.get-content",
@@ -84,3 +84,23 @@ bondSecurityOption(bond.command("announcement").description("Bond announcements,
84
84
  fieldList: maybeArray(options.field),
85
85
  });
86
86
  }));
87
+ bond.command("default-list").description("Defaulted bonds: by default-date range (at most 1 year), by bond codes, or by one issuer — pick one")
88
+ .option("--start-date <date>", "Default date range start (yyyy-MM-dd); needs --end-date", dateArg("--start-date"))
89
+ .option("--end-date <date>", "Default date range end (yyyy-MM-dd), at most one year after --start-date", dateArg("--end-date"))
90
+ .option("--security <code>", "Bond code, e.g. 169687.SH / 220205.IB (repeatable, no per-call limit)", collectList, [])
91
+ .option("--issuer <name>", "One issuer, by full name. A short name is matched to a single closest issuer, which may not be the one meant — check issuerName in the result", collectNames, [])
92
+ .option("--field <field>", "Field to return (repeatable); omit for all. An unsupported name rejects the whole call with 100005", collectList, [])
93
+ .option("--format <format>", "Output format", "table").option("--output <path>")
94
+ .action((options) => {
95
+ const byDate = Boolean(options.startDate || options.endDate);
96
+ const modes = [byDate, options.security.length > 0, options.issuer.length > 0].filter(Boolean).length;
97
+ if (modes > 1)
98
+ throw new ValidationError("bond default-list: --start-date/--end-date, --security and --issuer are mutually exclusive — pick one");
99
+ if (modes === 0)
100
+ throw new ValidationError("bond default-list: pass --start-date with --end-date (default date range), --security (bond codes) or --issuer (one issuer)");
101
+ if (byDate && !(options.startDate && options.endDate))
102
+ throw new ValidationError("bond default-list: --start-date and --end-date go together");
103
+ if (options.issuer.length > 1)
104
+ throw new ValidationError(`bond default-list: ${options.issuer.length} issuers given — the endpoint takes one per call. Run one call per issuer.`);
105
+ return emit(options, (client) => client.call("bond.default-list", { startDate: options.startDate, endDate: options.endDate, securityList: maybeArray(options.security), issuerNameList: maybeArray(options.issuer), fieldList: maybeArray(options.field) }));
106
+ });
@@ -1,6 +1,7 @@
1
1
  import { Command } from "commander";
2
2
  import { collectNames } from "../core/args.js";
3
- import { date, format, list, output, query, requiredList, value } from "./shared.js";
3
+ import { addComponentCodes, clientLookup } from "../core/etfComponents.js";
4
+ import { date, flag, format, list, output, query, requiredList, value } from "./shared.js";
4
5
  export const fund = new Command("fund").description("Fund APIs: profiles, NAV, fees, managers, scale, holders, holdings and allocation, ETF creation/redemption");
5
6
  const code = () => requiredList("--security <code>", "Fund code, e.g. 005827.OF / 159967.SZ / 510300.SH (repeatable, comma-separated accepted). From a fund name: 'reference securities-search --category fund', keeping only rows whose category is fund", "fundCodeList");
6
7
  const range = (what) => [
@@ -89,9 +90,10 @@ query(fund, "etf-pcf-header", {
89
90
  fields: [code(), format(), output()],
90
91
  });
91
92
  query(fund, "etf-pcf-components", {
92
- description: "ETF creation/redemption component list for the latest trade date: quantities, weights, cash substitution",
93
+ description: "ETF creation/redemption component list for the latest trade date: quantities, weights, cash substitution. componentCode is matched by the CLI from each component name",
93
94
  endpoint: "fund.etf-pcf-components",
94
- fields: [code(), format(), output()],
95
+ fields: [code(), format(), output(), flag("--no-component-code", "Skip matching componentCode: no download of the securities list, no name searches, no quote request")],
96
+ shape: (data, client, options) => options.componentCode === false ? Promise.resolve(data) : addComponentCodes(data, clientLookup(client)),
95
97
  });
96
98
  query(fund, "etf-share-change", {
97
99
  description: "ETF shares and scale per trade date, with the change vs the previous one",
@@ -10,5 +10,6 @@ import { quote } from "./quote.js";
10
10
  import { raw } from "./raw.js";
11
11
  import { reference } from "./reference.js";
12
12
  import { tool } from "./tool.js";
13
+ import { update } from "./update.js";
13
14
  import { vault } from "./vault.js";
14
- export const COMMAND_GROUPS = [auth, lookup, insight, quote, fundamental, bond, fund, reference, vault, ai, alternative, indicator, tool, raw];
15
+ export const COMMAND_GROUPS = [auth, lookup, insight, quote, fundamental, bond, fund, reference, vault, ai, alternative, indicator, tool, raw, update];
@@ -28,7 +28,7 @@ function flagDropped(rows, data, requestedSecurities, requestedIndicators) {
28
28
  parts.push(`indicators ${indicators.join(", ")}`);
29
29
  if (securities.length > 0)
30
30
  parts.push(`securities ${securities.join(", ")}`);
31
- process.stderr.write(`[gangtise] warning: the response omits ${parts.join(" and ")} entirely — no row/column at all, not a null one. A code the server merely has no data for still comes back as null, so this normally means the code itself was not recognised: check it for typos and, for securities, for the wrong market suffix (US tickers are .O/.N, not .US). Result marked partial (exit 3).\n`);
31
+ process.stderr.write(`[gangtise] warning: the response omits ${parts.join(" and ")} entirely — no row/column at all, not a null one. A code the server merely has no data for still comes back as null, so this normally means the code itself was not recognised: check it for typos and, for securities, for the wrong market suffix (US tickers are .O/.N/.A, not .US). Result marked partial (exit 3).\n`);
32
32
  }
33
33
  function requireIndicatorScope(indicators, securities) {
34
34
  const missing = [indicators.length === 0 ? "--indicator" : "", securities.length === 0 ? "--security" : ""].filter(Boolean);
@@ -221,6 +221,7 @@ export function query(parent, name, spec) {
221
221
  return command.action(async (options) => {
222
222
  const body = requestBody(fields, options);
223
223
  const endpoint = typeof spec.endpoint === "string" ? spec.endpoint : spec.endpoint(options);
224
- return emit(options, (client) => client.call(endpoint, body), spec.cache);
224
+ const shape = spec.shape;
225
+ return emit(options, shape ? async (client) => shape(await client.call(endpoint, body), client, options) : (client) => client.call(endpoint, body), spec.cache);
225
226
  });
226
227
  }
@@ -0,0 +1,30 @@
1
+ import os from "node:os";
2
+ import { Command } from "commander";
3
+ import { isVersionNewer } from "../core/args.js";
4
+ import { installVersion, refreshSkillCopies } from "../core/selfUpdate.js";
5
+ import { latestVersion, PACKAGE_NAME, REGISTRY } from "../core/updateCheck.js";
6
+ import { CLI_VERSION, PACKAGE_ROOT } from "../version.js";
7
+ export const update = new Command("update")
8
+ .description("Update to the latest published version (npm install -g), then refresh older copies of the Agent Skill installed for Claude Code / Codex / OpenClaw / Hermes")
9
+ .option("--check", "Only report whether a newer version is published; install nothing")
10
+ .option("--no-skill", "Leave the Agent Skill copies as they are")
11
+ .action(async (options) => {
12
+ const latest = await latestVersion(undefined, 15_000, Date.now(), true);
13
+ if (!latest)
14
+ throw new Error(`Could not reach ${REGISTRY} to look up the latest version. Check the network and retry, or install directly: npm install -g ${PACKAGE_NAME}@latest --registry=${REGISTRY}`);
15
+ const outdated = isVersionNewer(latest, CLI_VERSION);
16
+ if (options.check) {
17
+ process.stdout.write(outdated ? `Update available: ${CLI_VERSION} → ${latest}. Run: gangtise update\n` : `gangtise ${CLI_VERSION} is the latest version.\n`);
18
+ return;
19
+ }
20
+ if (outdated) {
21
+ installVersion(latest, PACKAGE_ROOT);
22
+ process.stdout.write(`Updated gangtise ${CLI_VERSION} → ${latest}.\n`);
23
+ }
24
+ else {
25
+ process.stdout.write(`gangtise ${CLI_VERSION} is the latest version.\n`);
26
+ }
27
+ if (options.skill)
28
+ for (const line of refreshSkillCopies(PACKAGE_ROOT, os.homedir()))
29
+ process.stdout.write(`${line}\n`);
30
+ });
@@ -272,7 +272,9 @@ export class GangtiseClient {
272
272
  sink.setFieldList(firstPage.fieldList);
273
273
  const collected = [];
274
274
  let count = 0;
275
- const rowId = endpoint.rowId;
275
+ const rowIdFields = endpoint.rowId === undefined ? [] : [endpoint.rowId].flat();
276
+ const rowId = rowIdFields.length > 0 ? rowIdFields.join(" + ") : undefined;
277
+ const rowIdPaths = rowIdFields.map((path) => path.split("."));
276
278
  const seen = rowId ? new Map() : undefined;
277
279
  let duplicateRows = 0;
278
280
  let changedRows = 0;
@@ -282,11 +284,11 @@ export class GangtiseClient {
282
284
  return rows;
283
285
  const onThisPage = new Map();
284
286
  return rows.filter((row) => {
285
- const id = row && typeof row === "object" ? row[rowId] : undefined;
286
- if (id === undefined || id === null)
287
+ const ids = rowIdPaths.map((path) => path.reduce((v, k) => v && typeof v === "object" ? v[k] : undefined, row));
288
+ if (ids.some((id) => id === undefined || id === null))
287
289
  return true;
288
290
  const digest = createHash("sha1").update(stableStringify(row)).digest("base64");
289
- const key = String(id);
291
+ const key = ids.length === 1 ? String(ids[0]) : JSON.stringify(ids.map(String));
290
292
  const pageVersions = onThisPage.get(key);
291
293
  if (pageVersions && !pageVersions.has(digest))
292
294
  idIsRowKey = false;
@@ -114,7 +114,7 @@ const ENDPOINT_DEFS = {
114
114
  description: "List roadshows",
115
115
  billing: { per: "row", price: 10 },
116
116
  pagination: { enabled: true, maxPageSize: 50 },
117
- rowId: "id",
117
+ rowId: "roadshowId",
118
118
  rowIdUnverified: true,
119
119
  },
120
120
  "insight.site-visit.list": {
@@ -124,7 +124,7 @@ const ENDPOINT_DEFS = {
124
124
  description: "List site visits",
125
125
  billing: { per: "row", price: 10 },
126
126
  pagination: { enabled: true, maxPageSize: 50 },
127
- rowId: "id",
127
+ rowId: "siteVisitId",
128
128
  rowIdUnverified: true,
129
129
  },
130
130
  "insight.strategy.list": {
@@ -134,7 +134,7 @@ const ENDPOINT_DEFS = {
134
134
  description: "List strategy meetings",
135
135
  billing: { per: "row", price: 10 },
136
136
  pagination: { enabled: true, maxPageSize: 50 },
137
- rowId: "id",
137
+ rowId: "strategyMeetingId",
138
138
  rowIdUnverified: true,
139
139
  },
140
140
  "insight.forum.list": {
@@ -144,7 +144,7 @@ const ENDPOINT_DEFS = {
144
144
  description: "List forums",
145
145
  billing: { per: "row", price: 10 },
146
146
  pagination: { enabled: true, maxPageSize: 50 },
147
- rowId: "id",
147
+ rowId: "forumId",
148
148
  rowIdUnverified: true,
149
149
  },
150
150
  "insight.performance-calendar.list": {
@@ -645,6 +645,15 @@ const ENDPOINT_DEFS = {
645
645
  retry: "no-replay",
646
646
  expects: "list",
647
647
  },
648
+ "bond.default-list": {
649
+ method: "POST",
650
+ path: "/application/open-fundamental/bond/default-list",
651
+ kind: "json",
652
+ description: "Query bond default records (by default-date range, bond codes or one issuer)",
653
+ billing: { per: "call", price: 0.4 },
654
+ retry: "no-replay",
655
+ expects: "list",
656
+ },
648
657
  "fund.basic-info": {
649
658
  method: "POST",
650
659
  path: "/application/open-fundamental/fund/basic-info",
@@ -924,6 +933,37 @@ const ENDPOINT_DEFS = {
924
933
  timeoutMs: 120_000,
925
934
  retry: "no-replay",
926
935
  },
936
+ "ai.management-sentiment-indicator": {
937
+ method: "POST",
938
+ path: "/application/open-ai/management-sentiment/company/indicator",
939
+ kind: "json",
940
+ description: "Management sentiment scores per earnings call (responsiveness / polarity / conviction)",
941
+ billing: { per: "row", price: 5 },
942
+ pagination: { enabled: true, maxPageSize: 100 },
943
+ rowId: ["securityCode", "source.sourceType", "source.sourceId"],
944
+ rowIdUnverified: true,
945
+ expects: "list",
946
+ },
947
+ "ai.management-sentiment-conclusion": {
948
+ method: "POST",
949
+ path: "/application/open-ai/management-sentiment/company/conclusion",
950
+ kind: "json",
951
+ description: "Management sentiment conclusions per earnings call, with changes against the previous call",
952
+ billing: { per: "row", price: 5 },
953
+ pagination: { enabled: true, maxPageSize: 100 },
954
+ rowId: ["securityCode", "source.sourceType", "source.sourceId"],
955
+ rowIdUnverified: true,
956
+ expects: "list",
957
+ },
958
+ "ai.management-sentiment-detail": {
959
+ method: "POST",
960
+ path: "/application/open-ai/management-sentiment/company/detail",
961
+ kind: "json",
962
+ description: "Per-Q&A management sentiment scores and their supporting quotes, for one earnings call",
963
+ billing: { per: "call", price: 10 },
964
+ retry: "no-replay",
965
+ expects: "list",
966
+ },
927
967
  "ai.viewpoint-debate.get-id": {
928
968
  method: "POST",
929
969
  path: "/application/open-ai/agent/viewpoint-debate-getid",