@v587d/capital-generation 2.11.0 → 2.11.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/CHANGELOG.md +10 -0
- package/README.md +2 -2
- package/chart-ui/package.json +1 -1
- package/config/package.json +1 -1
- package/lib/sources/fuyao-rest.js +1 -1
- package/lib/web-retriever/local-fetch.d.ts +1 -1
- package/lib/web-retriever/local-fetch.js +1 -1
- package/lib/web-retriever/sources.d.ts +4 -0
- package/lib/web-retriever/sources.js +21 -7
- package/lib/web-retriever/wind-client.d.ts +1 -1
- package/lib/web-retriever/wind-client.js +1 -1
- package/package.json +1 -1
- package/watchlist/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,16 @@
|
|
|
7
7
|
- **第三位(patch)**:不破坏既有工具 / 配置 / 会话的新增与修复(如 2.1.1、2.1.2)。
|
|
8
8
|
- **第二位(minor)**:有需要用户知晓的行为变更,且段内附迁移说明(如 2.1.0 的图表呈现通道重做、2.2.0 的数据源与检索来源扩容、2.4.0 的**必须换宿主版本**、2.9.0 的选择器收敛与质检前置——本插件自己的工具与数据契约没变,所以只抬第二位)。
|
|
9
9
|
|
|
10
|
+
## [2.11.1] - 2026-10-26
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **财联社快讯在 50 条时必然解析失败**:具名来源沿用了 HTML 回退的 10 万字符上限,
|
|
15
|
+
`cls_telegraph limit=50` 的真实响应 106,106 字符被截断后报 `Unterminated string`。
|
|
16
|
+
具名来源改用独立上限(262,144 字符 / 1 MiB),截断时点名体积原因而不是 JSON 语法错误。
|
|
17
|
+
- **5003 被当成可重试失败**:`financial_indicators` 描述补明该码是报告期尚未入库的数据缺口
|
|
18
|
+
(实测 2026-3 全市场均如此),不要 `force_refresh` 重试。
|
|
19
|
+
|
|
10
20
|
## [2.11.0] - 2026-10-26
|
|
11
21
|
|
|
12
22
|
精选 Skills 收敛为「运行时只带 Markdown」:取数方式从 70 颗正文里移出,改由角色系统提示的
|
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<p align="center">
|
|
19
19
|
<a href="https://awesome-dsh-plugin.com"><img src="https://awesome-dsh-plugin.com/badge.svg" alt="awesome · DSH plugin"></a>
|
|
20
20
|
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-green" alt="License"></a>
|
|
21
|
-
<a href="https://github.com/v587d/capital-generation/releases"><img src="https://img.shields.io/badge/version-2.11.
|
|
21
|
+
<a href="https://github.com/v587d/capital-generation/releases"><img src="https://img.shields.io/badge/version-2.11.1-9cf" alt="Version"></a>
|
|
22
22
|
<a href="https://www.npmjs.com/package/@v587d/capital-generation"><img src="https://img.shields.io/npm/v/%40v587d%2Fcapital-generation" alt="npm version"></a>
|
|
23
23
|
<a href="https://github.com/deepseek-ai/deepseek-harness"><img src="https://img.shields.io/badge/powered_by-dsh-4D6BFE?style=flat-square&logo=deepseek&logoColor=white" alt="powered-by-dsh"></a>
|
|
24
24
|
</p>
|
|
@@ -202,7 +202,7 @@ https://github.com/v587d/capital-generation
|
|
|
202
202
|
- **安装后找不到模式?** 确认插件出现在「插件」→ 已安装列表;Web / CLI profile 安装后要重启该 profile,并在新会话选择模式。选择器里应看到自动模式、行情专家、资讯专员;分析师和绘图专员是自动模式的内部角色,不会出现在这里。
|
|
203
203
|
- **配置在哪?** 打开「插件」→ 已安装的 **Capital Generation**,页面顶部就是「数据源 / 精选 Skills / 包含的组件」三格,五把密钥都在第一格;不在 DSH 的普通「设置」页面,也不用点进任何一行组件。保存密钥后新建会话再试。
|
|
204
204
|
- **升级到 2.9.0 后,分析师和绘图专员去哪了?** 这一版把它们收进自动模式内部,不再出现在选择器与预设管理里,避免误开一个没有取数入口的空会话。旧会话仍能按原身份恢复;新任务直接用自动模式提出即可。
|
|
205
|
-
- **桌面端升级后仍是旧版?** 桌面端安装时会固定所解析的版本,不会自动升级。卸载后重新安装,再到插件详情查看版本号;如需指定版本,可在地址末尾加 `#v2.11.
|
|
205
|
+
- **桌面端升级后仍是旧版?** 桌面端安装时会固定所解析的版本,不会自动升级。卸载后重新安装,再到插件详情查看版本号;如需指定版本,可在地址末尾加 `#v2.11.1` 或 `@2.11.1`。
|
|
206
206
|
- **刚发布的新版本装不到?** 桌面端取包时会跳过**刚发布不久**的版本(宿主侧包管理器的发布冷却策略,实测会静默装成上一个版本),国内镜像的同步也可能滞后一段时间。急着用最新代码,改用仓库地址安装最直接。
|
|
207
207
|
- **网页抓取失败?** 配置卡片中的「允许启动本地提取网页内容」默认开启:AnySearch 提取失败时可尝试本机直连。关闭后,本机直连的具名来源查询也不可用;请检查该开关和网络连接。
|
|
208
208
|
- **Wind 那部分会消耗积分?** 会。公告与新闻检索、宏观 / 行业 / 汇率指标、按日期区间取的历史 K 线都按次消耗你在 Wind 侧的额度;额度与条款以 Wind 为准,插件不做用量封顶。没配 `WIND_API_KEY` 时这几类不可用,其余功能照常。
|
package/chart-ui/package.json
CHANGED
package/config/package.json
CHANGED
|
@@ -999,7 +999,7 @@ function endpointDefinitions() {
|
|
|
999
999
|
name: 'get_a_share_financials_indicators',
|
|
1000
1000
|
capability: 'financial_indicators',
|
|
1001
1001
|
summary: '单期五类财务指标(成长/盈利/偿债/营运/现金流)',
|
|
1002
|
-
description: '获取单只标的指定报告期的五类财务指标(growth/profitability/solvency/operation/cash-flow)。thscode 不接受逗号;report 格式 yyyy-1..yyyy-4(1 一季报/2 中报/3 三季报/4 年报)。返回 data.abilities[].indicators[],每项含 index_id 与 value:value 是数据源原始数值**字符串**(保留精度与尾随小数位,不转 number),缺失为 null;百分比类指标按百分数原值表达(如 89.12000000 表示 89.12%),周转率按次、已获利息倍数按倍,展示时按 index_id
|
|
1002
|
+
description: '获取单只标的指定报告期的五类财务指标(growth/profitability/solvency/operation/cash-flow)。thscode 不接受逗号;report 格式 yyyy-1..yyyy-4(1 一季报/2 中报/3 三季报/4 年报)。返回 data.abilities[].indicators[],每项含 index_id 与 value:value 是数据源原始数值**字符串**(保留精度与尾随小数位,不转 number),缺失为 null;百分比类指标按百分数原值表达(如 89.12000000 表示 89.12%),周转率按次、已获利息倍数按倍,展示时按 index_id 识别单位。⚠️ 上游返回 code=5003 表示该报告期数据尚未入库(财报披露前常见,实测 2026-3 全市场均为 5003),属数据缺口而非参数错误:**不要 force_refresh 重试**,改用更早报告期或等披露后再取。',
|
|
1003
1003
|
path: '/api/a-share/financials/indicators',
|
|
1004
1004
|
inputSchema: objectSchema({
|
|
1005
1005
|
thscode: { type: 'string', description: '单只标的完整代码(如 300033.SZ),不接受逗号' },
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Known limit: DNS resolves per request but the IP is not pinned to the connection,
|
|
4
4
|
* so a rebinding window remains (same trade-off as dsh-search-first).
|
|
5
5
|
*/
|
|
6
|
-
export declare const LOCAL_FETCH_CLIENT_VERSION = "2.11.
|
|
6
|
+
export declare const LOCAL_FETCH_CLIENT_VERSION = "2.11.1";
|
|
7
7
|
export declare const LOCAL_FETCH_MAX_URL_LENGTH = 2048;
|
|
8
8
|
export type LocalFetchErrorCode = 'ABORTED' | 'TIMEOUT' | 'INVALID_URL' | 'BLOCKED_URL' | 'DNS' | 'REDIRECT' | 'HTTP' | 'TOO_LARGE' | 'UNSUPPORTED_CONTENT_TYPE';
|
|
9
9
|
export declare class LocalFetchError extends Error {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* so a rebinding window remains (same trade-off as dsh-search-first).
|
|
5
5
|
*/
|
|
6
6
|
import { htmlToMarkdown, stripInvisibleText } from './html-markdown.js';
|
|
7
|
-
export const LOCAL_FETCH_CLIENT_VERSION = '2.11.
|
|
7
|
+
export const LOCAL_FETCH_CLIENT_VERSION = '2.11.1';
|
|
8
8
|
export const LOCAL_FETCH_MAX_URL_LENGTH = 2048;
|
|
9
9
|
export class LocalFetchError extends Error {
|
|
10
10
|
code;
|
|
@@ -30,6 +30,10 @@ export declare class SourceError extends Error {
|
|
|
30
30
|
readonly code: string;
|
|
31
31
|
constructor(source: SourceId, code: string, message: string);
|
|
32
32
|
}
|
|
33
|
+
/** Named sources return API payloads, not web pages: the 100k-char HTML cap cuts a 50-item
|
|
34
|
+
* CLS telegraph body (measured 106,106 chars) into invalid JSON. Size for JSON, not HTML. */
|
|
35
|
+
export declare const SOURCE_MAX_BYTES = 1048576;
|
|
36
|
+
export declare const SOURCE_MAX_CHARS = 262144;
|
|
33
37
|
type Requester = ReturnType<typeof createHttpRequester>;
|
|
34
38
|
/** Built per source-tool call; lazy transport, configured timeout. */
|
|
35
39
|
export declare function createSourceTransport(options: LocalFetchOptions): Requester;
|
|
@@ -21,13 +21,23 @@ export class SourceError extends Error {
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
const REQUEST_TIMEOUT_MS = 20_000;
|
|
24
|
+
/** Named sources return API payloads, not web pages: the 100k-char HTML cap cuts a 50-item
|
|
25
|
+
* CLS telegraph body (measured 106,106 chars) into invalid JSON. Size for JSON, not HTML. */
|
|
26
|
+
export const SOURCE_MAX_BYTES = 1_048_576;
|
|
27
|
+
export const SOURCE_MAX_CHARS = 262_144;
|
|
24
28
|
function createHash(algorithm) {
|
|
25
29
|
const processLike = globalThis.process;
|
|
26
30
|
return processLike?.getBuiltinModule?.('node:crypto')?.createHash?.(algorithm);
|
|
27
31
|
}
|
|
28
32
|
/** Built per source-tool call; lazy transport, configured timeout. */
|
|
29
33
|
export function createSourceTransport(options) {
|
|
30
|
-
return createHttpRequester({
|
|
34
|
+
return createHttpRequester({
|
|
35
|
+
...options,
|
|
36
|
+
timeoutMs: options.timeoutMs || REQUEST_TIMEOUT_MS,
|
|
37
|
+
// Never lower the JSON ceiling to the HTML setting: a truncated body cannot be parsed.
|
|
38
|
+
maxBytes: Math.max(options.maxBytes, SOURCE_MAX_BYTES),
|
|
39
|
+
maxContentChars: Math.max(options.maxContentChars, SOURCE_MAX_CHARS),
|
|
40
|
+
});
|
|
31
41
|
}
|
|
32
42
|
/**
|
|
33
43
|
* Eastmoney shares the other sources' egress gate; throttling stays process-wide.
|
|
@@ -89,7 +99,11 @@ function asRecord(value) {
|
|
|
89
99
|
? value
|
|
90
100
|
: undefined;
|
|
91
101
|
}
|
|
92
|
-
function parseJson(text, source, what) {
|
|
102
|
+
function parseJson(text, source, what, truncated = false) {
|
|
103
|
+
// A truncated body always fails JSON.parse with a syntax error; name the real cause instead.
|
|
104
|
+
if (truncated) {
|
|
105
|
+
throw new SourceError(source, 'INVALID_RESPONSE', `${what} 超过本机响应体上限被截断,无法解析;请降低 limit 或缩小查询范围`);
|
|
106
|
+
}
|
|
93
107
|
try {
|
|
94
108
|
return JSON.parse(text);
|
|
95
109
|
}
|
|
@@ -137,7 +151,7 @@ export async function clsTelegraph(transport, input) {
|
|
|
137
151
|
};
|
|
138
152
|
const query = Object.keys(params).sort().map((key) => `${key}=${params[key]}`).join('&');
|
|
139
153
|
const outcome = await transport.request({ url: `${CLS_URL}?${query}&sign=${clsSign(params)}`, headers: { referer: CLS_REFERER } }, input.signal);
|
|
140
|
-
const body = asRecord(parseJson(outcome.text, 'cls', '财联社响应'));
|
|
154
|
+
const body = asRecord(parseJson(outcome.text, 'cls', '财联社响应', outcome.truncated));
|
|
141
155
|
if (!body)
|
|
142
156
|
throw new SourceError('cls', 'INVALID_RESPONSE', '财联社响应不是对象');
|
|
143
157
|
if (body.errno !== 0) {
|
|
@@ -179,7 +193,7 @@ export async function wscnLives(transport, input) {
|
|
|
179
193
|
if (input.cursor !== undefined)
|
|
180
194
|
params.set('cursor', input.cursor);
|
|
181
195
|
const outcome = await transport.request({ url: `${WSCN_URL}?${params.toString()}` }, input.signal);
|
|
182
|
-
const body = asRecord(parseJson(outcome.text, 'wscn', '华尔街见闻响应'));
|
|
196
|
+
const body = asRecord(parseJson(outcome.text, 'wscn', '华尔街见闻响应', outcome.truncated));
|
|
183
197
|
if (!body)
|
|
184
198
|
throw new SourceError('wscn', 'INVALID_RESPONSE', '华尔街见闻响应不是对象');
|
|
185
199
|
if (body.code !== 20000) {
|
|
@@ -225,7 +239,7 @@ function formBody(fields) {
|
|
|
225
239
|
*/
|
|
226
240
|
export async function cninfoIrm(transport, input) {
|
|
227
241
|
const keyword = await transport.request({ url: IRM_KEYWORD_URL, method: 'POST', body: formBody({ keyWord: input.code }) }, input.signal);
|
|
228
|
-
const keywordBody = asRecord(parseJson(keyword.text, 'cninfo_irm', '互动易代码查询响应'));
|
|
242
|
+
const keywordBody = asRecord(parseJson(keyword.text, 'cninfo_irm', '互动易代码查询响应', keyword.truncated));
|
|
229
243
|
const matches = Array.isArray(keywordBody?.data) ? keywordBody.data : [];
|
|
230
244
|
const first = asRecord(matches[0]);
|
|
231
245
|
const orgId = typeof first?.secid === 'string' ? first.secid : undefined;
|
|
@@ -243,7 +257,7 @@ export async function cninfoIrm(transport, input) {
|
|
|
243
257
|
endDay: '',
|
|
244
258
|
});
|
|
245
259
|
const page = await transport.request({ url: `${IRM_QUESTION_URL}?${params.toString()}`, method: 'POST', body: '' }, input.signal);
|
|
246
|
-
const pageBody = asRecord(parseJson(page.text, 'cninfo_irm', '互动易问答响应'));
|
|
260
|
+
const pageBody = asRecord(parseJson(page.text, 'cninfo_irm', '互动易问答响应', page.truncated));
|
|
247
261
|
const rows = requireRows(pageBody?.rows, 'cninfo_irm', '互动易 rows');
|
|
248
262
|
const total = typeof pageBody?.total === 'number' ? pageBody.total : undefined;
|
|
249
263
|
const items = [];
|
|
@@ -311,7 +325,7 @@ async function sseCompanyPage(transport, page, signal) {
|
|
|
311
325
|
if (cached !== undefined)
|
|
312
326
|
return cached;
|
|
313
327
|
const outcome = await transport.request({ url: `${SSE_BASE}/allcompany.do`, method: 'POST', body: formBody({ code: '0', order: '2', areaId: '0', page: String(page) }), headers: { referer: SSE_REFERER } }, signal);
|
|
314
|
-
const body = asRecord(parseJson(outcome.text, 'sseinfo', '上证e互动公司列表响应'));
|
|
328
|
+
const body = asRecord(parseJson(outcome.text, 'sseinfo', '上证e互动公司列表响应', outcome.truncated));
|
|
315
329
|
const content = typeof body?.content === 'string' ? body.content : undefined;
|
|
316
330
|
if (content === undefined) {
|
|
317
331
|
throw new SourceError('sseinfo', 'INVALID_RESPONSE', `上证e互动公司列表第 ${page} 页没有 content 字符串(页面结构可能已变)`);
|
|
@@ -35,7 +35,7 @@ export interface WindCallResult {
|
|
|
35
35
|
code?: WindErrorCode;
|
|
36
36
|
}
|
|
37
37
|
/** Must match `package.json` version; a test pins the two together. */
|
|
38
|
-
export declare const WIND_CLIENT_VERSION = "2.11.
|
|
38
|
+
export declare const WIND_CLIENT_VERSION = "2.11.1";
|
|
39
39
|
export interface WindClient {
|
|
40
40
|
callTool(toolName: string, args: Record<string, unknown>, signal?: AbortSignal): Promise<WindCallResult>;
|
|
41
41
|
}
|
|
@@ -19,7 +19,7 @@ export const WIND_SERVER_ENDPOINTS = {
|
|
|
19
19
|
analytics_data: 'https://mcp.wind.com.cn/vserver_analytics_data/mcp/',
|
|
20
20
|
};
|
|
21
21
|
/** Must match `package.json` version; a test pins the two together. */
|
|
22
|
-
export const WIND_CLIENT_VERSION = '2.11.
|
|
22
|
+
export const WIND_CLIENT_VERSION = '2.11.1';
|
|
23
23
|
function errorText(error) {
|
|
24
24
|
return error instanceof Error ? error.message : String(error);
|
|
25
25
|
}
|
package/package.json
CHANGED
package/watchlist/package.json
CHANGED