@v587d/capital-generation 0.0.0-stage → 2.6.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 +814 -0
- package/LICENSE +21 -0
- package/README.md +118 -2
- package/assets/cg-icon.png +0 -0
- package/capital-config/client.js +28 -0
- package/capital-config/client.src.cjs +388 -0
- package/capital-config/index.js +113 -0
- package/capital-config/package.json +36 -0
- package/capital-watchlist/client.js +182 -0
- package/capital-watchlist/client.src.cjs +1229 -0
- package/capital-watchlist/index.js +772 -0
- package/capital-watchlist/package.json +37 -0
- package/chart-ui/README.md +112 -0
- package/chart-ui/client.js +16 -0
- package/chart-ui/client.src.cjs +126 -0
- package/chart-ui/index.js +205 -0
- package/chart-ui/package.json +30 -0
- package/cordis.patch.yml +72 -0
- package/lib/agents/bash-guard.d.ts +55 -0
- package/lib/agents/bash-guard.js +162 -0
- package/lib/agents/root-tool-policy.d.ts +164 -0
- package/lib/agents/root-tool-policy.js +309 -0
- package/lib/chart/artifact-ref.d.ts +65 -0
- package/lib/chart/artifact-ref.js +107 -0
- package/lib/chart/downsample.d.ts +8 -0
- package/lib/chart/downsample.js +53 -0
- package/lib/chart/errors.d.ts +16 -0
- package/lib/chart/errors.js +15 -0
- package/lib/chart/events.d.ts +120 -0
- package/lib/chart/events.js +471 -0
- package/lib/chart/html.d.ts +21 -0
- package/lib/chart/html.js +154 -0
- package/lib/chart/runtime.d.ts +20 -0
- package/lib/chart/runtime.js +270 -0
- package/lib/chart/series.d.ts +102 -0
- package/lib/chart/series.js +424 -0
- package/lib/chart/source-token.d.ts +64 -0
- package/lib/chart/source-token.js +217 -0
- package/lib/chart/spec.d.ts +58 -0
- package/lib/chart/spec.js +265 -0
- package/lib/chart/tool.d.ts +86 -0
- package/lib/chart/tool.js +288 -0
- package/lib/chart/vendor/LICENSE +201 -0
- package/lib/chart/vendor/VERSION.json +8 -0
- package/lib/chart/vendor/lightweight-charts.standalone.production.js +7 -0
- package/lib/data-collector/args.d.ts +38 -0
- package/lib/data-collector/args.js +114 -0
- package/lib/data-collector/dataset-tools.d.ts +3 -0
- package/lib/data-collector/dataset-tools.js +421 -0
- package/lib/data-collector/describe.d.ts +99 -0
- package/lib/data-collector/describe.js +256 -0
- package/lib/data-collector/hub.d.ts +187 -0
- package/lib/data-collector/hub.js +469 -0
- package/lib/data-collector/query-time.d.ts +58 -0
- package/lib/data-collector/query-time.js +101 -0
- package/lib/data-collector/query.d.ts +57 -0
- package/lib/data-collector/query.js +473 -0
- package/lib/data-collector/store.d.ts +499 -0
- package/lib/data-collector/store.js +1486 -0
- package/lib/data-collector/time-axis.d.ts +169 -0
- package/lib/data-collector/time-axis.js +419 -0
- package/lib/data-collector/tools.d.ts +32 -0
- package/lib/data-collector/tools.js +216 -0
- package/lib/index.d.ts +275 -0
- package/lib/index.js +458 -0
- package/lib/net/eastmoney-client.d.ts +104 -0
- package/lib/net/eastmoney-client.js +118 -0
- package/lib/net/throttle.d.ts +16 -0
- package/lib/net/throttle.js +34 -0
- package/lib/ocr/client.d.ts +82 -0
- package/lib/ocr/client.js +281 -0
- package/lib/ocr/markdown.d.ts +75 -0
- package/lib/ocr/markdown.js +209 -0
- package/lib/ocr/store.d.ts +119 -0
- package/lib/ocr/store.js +226 -0
- package/lib/sources/eastmoney-http.d.ts +2 -0
- package/lib/sources/eastmoney-http.js +1253 -0
- package/lib/sources/fuyao-core.d.ts +52 -0
- package/lib/sources/fuyao-core.js +96 -0
- package/lib/sources/fuyao-rest.d.ts +14 -0
- package/lib/sources/fuyao-rest.js +2057 -0
- package/lib/sources/security-code.d.ts +27 -0
- package/lib/sources/security-code.js +104 -0
- package/lib/sources/security-identity.d.ts +24 -0
- package/lib/sources/security-identity.js +89 -0
- package/lib/sources/tencent-http.d.ts +2 -0
- package/lib/sources/tencent-http.js +1053 -0
- package/lib/sources/wind-mcp.d.ts +8 -0
- package/lib/sources/wind-mcp.js +593 -0
- package/lib/time/tools.d.ts +74 -0
- package/lib/time/tools.js +464 -0
- package/lib/tool-exec.d.ts +29 -0
- package/lib/tool-exec.js +17 -0
- package/lib/web-retriever/engines.d.ts +61 -0
- package/lib/web-retriever/engines.js +202 -0
- package/lib/web-retriever/html-markdown.d.ts +50 -0
- package/lib/web-retriever/html-markdown.js +321 -0
- package/lib/web-retriever/local-fetch.d.ts +121 -0
- package/lib/web-retriever/local-fetch.js +562 -0
- package/lib/web-retriever/retriever.d.ts +58 -0
- package/lib/web-retriever/retriever.js +94 -0
- package/lib/web-retriever/sources.d.ts +235 -0
- package/lib/web-retriever/sources.js +961 -0
- package/lib/web-retriever/tools.d.ts +50 -0
- package/lib/web-retriever/tools.js +1029 -0
- package/lib/web-retriever/wind-client.d.ts +69 -0
- package/lib/web-retriever/wind-client.js +311 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +84 -4
- package/preset/capital-generation/README.md +161 -0
- package/preset/capital-generation/agent.patch.yml +509 -0
- package/preset/capital-generation/skills/capital-chart-protocol/SKILL.md +137 -0
- package/preset/capital-generation/skills/capital-data-protocol/SKILL.md +416 -0
- package/preset/capital-generation/skills/capital-orchestration/SKILL.md +174 -0
- package/preset/capital-generation/skills/capital-visualization-protocol/SKILL.md +296 -0
- package/preset/capital-generation/skills/capital-web-protocol/SKILL.md +240 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { AnySearchClient, AnySearchErrorCode, SearchResult } from './engines.js';
|
|
2
|
+
import type { LocalFetchErrorCode } from './local-fetch.js';
|
|
3
|
+
/** 最终产出这份正文的来源;`ok:false` 时恒为 `'anysearch'`(主路)。 */
|
|
4
|
+
export type FetchVia = 'anysearch' | 'local-http';
|
|
5
|
+
/** 触发回退的那次 AnySearch 失败;永远只描述 AnySearch 侧。 */
|
|
6
|
+
export interface FetchFallbackNote {
|
|
7
|
+
from: 'anysearch';
|
|
8
|
+
code: AnySearchErrorCode;
|
|
9
|
+
error: string;
|
|
10
|
+
}
|
|
11
|
+
/** 本地抓取自己失败的原因;只在"回退被尝试且本地也失败"时出现。 */
|
|
12
|
+
export interface FetchLocalErrorNote {
|
|
13
|
+
code: LocalFetchErrorCode;
|
|
14
|
+
error: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* `web_retriever_fetch` 的回执。
|
|
18
|
+
*
|
|
19
|
+
* - `via`:最终产出这份正文的来源;`ok:false` 时恒为 `'anysearch'`(主路)。
|
|
20
|
+
* - `fallback`:触发回退的那次 AnySearch 失败,永远只描述 AnySearch 侧
|
|
21
|
+
* (回退成功与"回退尝试后本地也失败"两种情形都会出现)。
|
|
22
|
+
* - `local_error`:本地抓取自己失败的原因,只在"回退被尝试且本地也失败"时出现。
|
|
23
|
+
* `fallback.error` 是 AnySearch 的错、`local_error.error` 是本地的错,两者不重复。
|
|
24
|
+
* - `truncated`:正文被截断过(AnySearch 侧截断、本地字节截断、本地字符截断、转换省略,任一为真)。
|
|
25
|
+
*/
|
|
26
|
+
export interface FetchResult {
|
|
27
|
+
url: string;
|
|
28
|
+
ok: boolean;
|
|
29
|
+
via: FetchVia;
|
|
30
|
+
title?: string;
|
|
31
|
+
content?: string;
|
|
32
|
+
truncated?: boolean;
|
|
33
|
+
fallback?: FetchFallbackNote;
|
|
34
|
+
error?: string;
|
|
35
|
+
code?: AnySearchErrorCode;
|
|
36
|
+
local_error?: FetchLocalErrorNote;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 本地抓取器的结构类型。构造函数不传 `localFetch` 即"不启用回退",
|
|
40
|
+
* 由 `src/index.ts` 按配置决定是否传入。
|
|
41
|
+
*/
|
|
42
|
+
export interface LocalFetcherLike {
|
|
43
|
+
fetch(url: string, signal?: AbortSignal): Promise<{
|
|
44
|
+
url: string;
|
|
45
|
+
status: number;
|
|
46
|
+
title?: string;
|
|
47
|
+
markdown: string;
|
|
48
|
+
truncated: boolean;
|
|
49
|
+
}>;
|
|
50
|
+
}
|
|
51
|
+
/** 无状态的 AnySearch 适配器:不缓存、不排队、不维护回合或材料索引。 */
|
|
52
|
+
export declare class WebRetriever {
|
|
53
|
+
private readonly client;
|
|
54
|
+
private readonly localFetch?;
|
|
55
|
+
constructor(client: AnySearchClient, localFetch?: LocalFetcherLike | undefined);
|
|
56
|
+
search(query: string, maxResults?: number, signal?: AbortSignal): Promise<SearchResult>;
|
|
57
|
+
fetch(url: string, signal?: AbortSignal): Promise<FetchResult>;
|
|
58
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { AnySearchError, normalizeFetchResponse, normalizeSearchResponse, shouldFallbackToLocalFetch, } from './engines.js';
|
|
2
|
+
import { LocalFetchError } from './local-fetch.js';
|
|
3
|
+
function errorText(error) {
|
|
4
|
+
return error instanceof Error ? error.message : String(error);
|
|
5
|
+
}
|
|
6
|
+
/** 调用方取消必须原样抛出,不能被降级成"失败信封"。 */
|
|
7
|
+
function abortError(signal) {
|
|
8
|
+
return signal.reason ?? new Error('request aborted');
|
|
9
|
+
}
|
|
10
|
+
/** 无状态的 AnySearch 适配器:不缓存、不排队、不维护回合或材料索引。 */
|
|
11
|
+
export class WebRetriever {
|
|
12
|
+
client;
|
|
13
|
+
localFetch;
|
|
14
|
+
constructor(client, localFetch) {
|
|
15
|
+
this.client = client;
|
|
16
|
+
this.localFetch = localFetch;
|
|
17
|
+
}
|
|
18
|
+
async search(query, maxResults, signal) {
|
|
19
|
+
const raw = await this.client.search({
|
|
20
|
+
query,
|
|
21
|
+
...(maxResults === undefined ? {} : { max_results: maxResults }),
|
|
22
|
+
}, signal);
|
|
23
|
+
return normalizeSearchResponse(query, raw);
|
|
24
|
+
}
|
|
25
|
+
async fetch(url, signal) {
|
|
26
|
+
let trigger;
|
|
27
|
+
try {
|
|
28
|
+
const raw = await this.client.extract({ url }, signal);
|
|
29
|
+
const normalized = normalizeFetchResponse(url, raw);
|
|
30
|
+
if (normalized.ok) {
|
|
31
|
+
return {
|
|
32
|
+
url,
|
|
33
|
+
ok: true,
|
|
34
|
+
via: 'anysearch',
|
|
35
|
+
...(normalized.title ? { title: normalized.title } : {}),
|
|
36
|
+
...(normalized.content ? { content: normalized.content } : {}),
|
|
37
|
+
...(normalized.truncated ? { truncated: true } : {}),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
// "取到了但没有内容"与失败等价,按可回退失败处理。
|
|
41
|
+
trigger = {
|
|
42
|
+
from: 'anysearch',
|
|
43
|
+
code: 'UPSTREAM',
|
|
44
|
+
error: normalized.error ?? 'fetch returned no content',
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
// 调用方取消:原样抛出,不回退、不吞。
|
|
49
|
+
if (error instanceof AnySearchError && error.code === 'ABORTED')
|
|
50
|
+
throw error;
|
|
51
|
+
const code = error instanceof AnySearchError ? error.code : 'UPSTREAM';
|
|
52
|
+
const message = errorText(error);
|
|
53
|
+
// 非法 URL / 不支持的二进制:本机同样会拒,回退只是白跑一次。
|
|
54
|
+
if (!shouldFallbackToLocalFetch(code)) {
|
|
55
|
+
return { url, ok: false, via: 'anysearch', code, error: message };
|
|
56
|
+
}
|
|
57
|
+
trigger = { from: 'anysearch', code, error: message };
|
|
58
|
+
}
|
|
59
|
+
// 回退前的取消闸门(硬):调用方已经不要这个结果了,绝不再发一次网络请求。
|
|
60
|
+
if (signal?.aborted)
|
|
61
|
+
throw abortError(signal);
|
|
62
|
+
if (!this.localFetch) {
|
|
63
|
+
return { url, ok: false, via: 'anysearch', code: trigger.code, error: trigger.error };
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
const local = await this.localFetch.fetch(url, signal);
|
|
67
|
+
return {
|
|
68
|
+
url: local.url,
|
|
69
|
+
ok: true,
|
|
70
|
+
via: 'local-http',
|
|
71
|
+
...(local.title ? { title: local.title } : {}),
|
|
72
|
+
content: local.markdown,
|
|
73
|
+
...(local.truncated ? { truncated: true } : {}),
|
|
74
|
+
fallback: trigger,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
// 本地抓取侧的取消同样原样抛出。
|
|
79
|
+
if (error instanceof LocalFetchError && error.code === 'ABORTED')
|
|
80
|
+
throw error;
|
|
81
|
+
const localMessage = errorText(error);
|
|
82
|
+
const localCode = error instanceof LocalFetchError ? error.code : 'HTTP';
|
|
83
|
+
return {
|
|
84
|
+
url,
|
|
85
|
+
ok: false,
|
|
86
|
+
via: 'anysearch',
|
|
87
|
+
code: trigger.code,
|
|
88
|
+
error: `AnySearch 失败:${trigger.error};本地直抓也失败:${localMessage}`,
|
|
89
|
+
fallback: trigger,
|
|
90
|
+
local_error: { code: localCode, error: localMessage },
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 具名来源的结构化取数(provider + operation)。
|
|
3
|
+
*
|
|
4
|
+
* 与 `web_retriever_fetch` 的分工:fetch 是"一个 URL → 一篇正文"的**检索**工作面;
|
|
5
|
+
* 这里是"一个具名来源 + 业务参数 → 确定、有序、可翻页、同参可复现的结果集"的**查询**工作面。
|
|
6
|
+
* 判据不是"返回 JSON 还是 HTML"(`SearchHit` 本来就带 `content?`,形状无法区分),
|
|
7
|
+
* 而是结果集是否确定、是否可复现、以及"这次调用承诺了什么"——见
|
|
8
|
+
* `docs/design/web-retriever-source-expansion.md` §2.2。
|
|
9
|
+
*
|
|
10
|
+
* 四条来源各自是一次具体实现,**不做配方注册表 / 通用适配层**:只有这样,
|
|
11
|
+
* 每个来源的端点、签名、字段语义才能就地写清楚,不引入一层只为"统一"而存在的间接。
|
|
12
|
+
* 出口校验(URL 合法性 + DNS 公网 IP 闸门 + 重定向重校验 + 大小/超时上限)统一复用
|
|
13
|
+
* `createHttpRequester`,本文件不自建网络路径(AGENTS.md §9.7)。
|
|
14
|
+
*/
|
|
15
|
+
import { createHttpRequester } from './local-fetch.js';
|
|
16
|
+
import type { EastmoneyClient, EastmoneyTransport } from '../net/eastmoney-client.js';
|
|
17
|
+
import type { LocalFetchOptions } from './local-fetch.js';
|
|
18
|
+
/** 错误信封里的上游名,也是 `provider_tally` 的键。 */
|
|
19
|
+
export type SourceId = 'cls' | 'wscn' | 'cninfo_irm' | 'sseinfo' | 'eastmoney' | 'ths' | 'sina';
|
|
20
|
+
export declare const SOURCE_IDS: readonly SourceId[];
|
|
21
|
+
/** 单条正文裁剪上限:条目数由各工具的 limit 参数封顶,单条长度在这里封顶。 */
|
|
22
|
+
export declare const MAX_ITEM_CHARS = 1200;
|
|
23
|
+
/** 一条工具输出里的条目上限(与工具 schema 的 maximum 一致)。 */
|
|
24
|
+
export declare const MAX_ITEMS = 50;
|
|
25
|
+
export interface SourceItem {
|
|
26
|
+
[key: string]: string | number | null;
|
|
27
|
+
}
|
|
28
|
+
export interface SourceOutcome {
|
|
29
|
+
source: SourceId;
|
|
30
|
+
operation: string;
|
|
31
|
+
items: SourceItem[];
|
|
32
|
+
/** 上游给的可继续翻页游标;没有就不带这个键。 */
|
|
33
|
+
next_cursor?: string;
|
|
34
|
+
/** 如实记录上游口径 / 已知缺口,模型据此判断"空结果"是不是真事实。 */
|
|
35
|
+
note?: string;
|
|
36
|
+
}
|
|
37
|
+
export declare class SourceError extends Error {
|
|
38
|
+
readonly source: SourceId;
|
|
39
|
+
readonly code: string;
|
|
40
|
+
constructor(source: SourceId, code: string, message: string);
|
|
41
|
+
}
|
|
42
|
+
type Requester = ReturnType<typeof createHttpRequester>;
|
|
43
|
+
/** 每个来源工具调用一次;transport 惰性建、按配置缓存。 */
|
|
44
|
+
export declare function createSourceTransport(options: LocalFetchOptions): Requester;
|
|
45
|
+
/**
|
|
46
|
+
* 东财客户端传输层的**闸门适配**:三个 `eastmoney_*` 工具与其余六个来源共用同一份出口校验
|
|
47
|
+
* (URL 合法性 + DNS 公网 IP 闸门 + 手动重定向逐跳重校验 + 大小/超时上限),不再是第二份
|
|
48
|
+
* 裸 `fetch`(AGENTS.md §9.7)。节流器不在这里——仍由进程级共享的东财节流器提供
|
|
49
|
+
* (`sharedEastmoneyThrottle()`),本适配只替换"怎么发出去"。
|
|
50
|
+
*
|
|
51
|
+
* 带分类 `code` 的 `LocalFetchError` 原样穿过(`createEastmoneyClient` 见 code 即不包裹),
|
|
52
|
+
* 取消因此保持 `ABORTED` 语义、不被降级成网络失败(docs/dev/web-retriever.md §4.2)。
|
|
53
|
+
*/
|
|
54
|
+
export declare function createGatedEastmoneyTransport(requester: Requester): EastmoneyTransport;
|
|
55
|
+
/**
|
|
56
|
+
* 财联社 v1 接口的本地签名:`sign = md5(sha1(按 key 字典序拼接的 query 串))`。
|
|
57
|
+
*
|
|
58
|
+
* 旧 `nodeapi/telegraphList` 于 2026-05 下线,现接口强制校验 sign,但**纯本地计算、无需任何 key**
|
|
59
|
+
* (外部实践文献 5.2 节,2026-07 复活;2026-09-24 本仓按此式复现,`errno=0` 正常返回)。
|
|
60
|
+
* 签名必须由代码在请求前算出——这正是"模型只给参数、不给 URL"的原因。
|
|
61
|
+
*/
|
|
62
|
+
export declare function clsSign(params: Record<string, string>): string;
|
|
63
|
+
export declare function clsTelegraph(transport: Requester, input: {
|
|
64
|
+
limit: number;
|
|
65
|
+
signal?: AbortSignal;
|
|
66
|
+
}): Promise<SourceOutcome>;
|
|
67
|
+
export declare function wscnLives(transport: Requester, input: {
|
|
68
|
+
channel: string;
|
|
69
|
+
limit: number;
|
|
70
|
+
cursor?: string;
|
|
71
|
+
signal?: AbortSignal;
|
|
72
|
+
}): Promise<SourceOutcome>;
|
|
73
|
+
/**
|
|
74
|
+
* 两步取数:`keyWord=代码` → `secid`(即 orgId)→ 按 orgId 查问答。
|
|
75
|
+
*
|
|
76
|
+
* ⚠️ 实测坑(2026-09-24 本仓):第二步**必须 POST**(参数放 query 串)——
|
|
77
|
+
* 同一 URL 用 GET 会得到 **HTTP 405**。外部文献只写了"参数放 query string",
|
|
78
|
+
* 没写这一条,而少了 POST 就是 405。
|
|
79
|
+
* 覆盖范围:只覆盖深市;沪市实测返回 0 条(走 `sseinfo_qa`);北交所两个平台都没有。
|
|
80
|
+
*/
|
|
81
|
+
export declare function cninfoIrm(transport: Requester, input: {
|
|
82
|
+
code: string;
|
|
83
|
+
pageSize: number;
|
|
84
|
+
pageNum: number;
|
|
85
|
+
signal?: AbortSignal;
|
|
86
|
+
}): Promise<SourceOutcome>;
|
|
87
|
+
/**
|
|
88
|
+
* 公司列表页:每页 32 家、按代码升序。
|
|
89
|
+
*
|
|
90
|
+
* ⚠️ "uid 与代码必须来自**同一个 `<a>`**"在本函数里是**结构性保证**,不是靠正则窗口长度凑的:
|
|
91
|
+
* 先用 `/<a\b(?:(?!<\/a>)[\s\S])*?<\/a>/g` 切出每个 `<a>` 元素,再在**单个元素内**找 uid 与
|
|
92
|
+
* `company/<6位>.png`。逐元素解析的另一个好处是**跨条目错配在结构上不可能发生**。
|
|
93
|
+
*
|
|
94
|
+
* 为什么不能用"一次正则 + 限制间隔长度"(本函数第一版就是这么写的,被测试当场抓住):
|
|
95
|
+
* 真实页面在 `</a>` 与 `<img>` 之间有一个 `<br/>`,而 A 条目的 `<br/>` 之后紧跟 B 条目,
|
|
96
|
+
* 于是任何形如 `uid=…[\s\S]{0,N}?…png` 的写法在 **N 足够大**时都会跨到下一个条目上——
|
|
97
|
+
* 实测把 `600369` 配到了前一条的 `uid=65`。窗口小则匹配 0 条(外部文献的 `[^>]*>` 就是这个下场),
|
|
98
|
+
* 窗口大则错配;**只有按元素切分才是对的**。
|
|
99
|
+
*/
|
|
100
|
+
export declare function parseCompanyList(content: string): Array<{
|
|
101
|
+
code: string;
|
|
102
|
+
uid: string;
|
|
103
|
+
}>;
|
|
104
|
+
export declare function resetSseCachesForTest(): void;
|
|
105
|
+
/**
|
|
106
|
+
* 一次 uid 定位的**总**时限。`REQUEST_TIMEOUT_MS` 只管单个请求,而定位一次要串行 10–13 个
|
|
107
|
+
* 请求(最坏 13 × 20s ≈ 4 分钟),所以这段必须有自己的上限——工具层的超时由宿主决定,
|
|
108
|
+
* 不能拿"整轮对话还剩多少"来兜一个内部元数据查询。`sseinfoQa` 的 `uidLocateBudgetMs`
|
|
109
|
+
* 仅供测试注入缩短(与 `wind-client.ts` 的 `retryDelaysMs` 同一约定)。
|
|
110
|
+
*/
|
|
111
|
+
export declare const SSE_UID_LOCATE_TIMEOUT_MS = 60000;
|
|
112
|
+
/**
|
|
113
|
+
* 代码 → uid:公司列表分页升序,用**倍增 + 二分**定位。
|
|
114
|
+
*
|
|
115
|
+
* ⚠️ 缓存层次是本函数最容易写错的地方(2026-09-24 实测踩坑,症状很绕):
|
|
116
|
+
* - `ssePageCache` 缓存**页**,供二分复用,不参与"这个代码是谁"的判定;
|
|
117
|
+
* - `sseUidCache` 只缓存**已解析出的那个代码**,绝不在扫描页面时把整页代码灌进去。
|
|
118
|
+
*
|
|
119
|
+
* 为什么后者是硬约束:二分一次会扫过 ~10–13 页、每页 32 家。若把扫描到的**全部**代码都写进
|
|
120
|
+
* uid 缓存,一次解析就能塞进 200+ 条,把容量占满并**淘汰掉正在解析的那个代码**——表现为
|
|
121
|
+
* "单次解析成功、稍后再查同一个代码却报 NOT_FOUND",而且只在特定调用序列下复现。
|
|
122
|
+
* 因此 uid 只从"已确认命中"的那一页里取,取不到就按升序自检后的页范围继续二分。
|
|
123
|
+
*/
|
|
124
|
+
export declare function resolveSseCompanyUid(transport: Requester, code: string, signal?: AbortSignal): Promise<string>;
|
|
125
|
+
/**
|
|
126
|
+
* 解析问答列表页。以回复块 `class="m_feed_detail m_qa"` 为界切问题段与回复段——
|
|
127
|
+
* 不靠 id 区分("最新回复"与"最新提问"两种列表的标记不同)。
|
|
128
|
+
*/
|
|
129
|
+
export declare function parseSseFeed(text: string): SourceItem[];
|
|
130
|
+
/**
|
|
131
|
+
* 页面时间统一成「YYYY-MM-DD HH:MM」(北京时间)。
|
|
132
|
+
*
|
|
133
|
+
* 实测(2026-09-24 全量抽样 150 条)非绝对写法**只有三类**,必须全部支持:
|
|
134
|
+
* - 相对当天:`2分钟前` / `57分钟前` / `1小时前` / `2小时前`;
|
|
135
|
+
* - 相对昨天:`昨天 18:16`;
|
|
136
|
+
* - 绝对:`2026年09月24日 09:30`。
|
|
137
|
+
*
|
|
138
|
+
* 少支持任何一类,"最新提问"列表就会因某一条认不出而**整次失败**(本仓真机冒烟两次都栽在
|
|
139
|
+
* 这里:先是 `昨天 18:16`,再是 `2分钟前`)。相对写法按**北京时间**换算——host 本地时区不可依赖
|
|
140
|
+
* (本仓跑在 UTC 主机上),所以先取"北京当下"再回推。
|
|
141
|
+
*/
|
|
142
|
+
export declare function sseTime(text: string, now?: Date): string | undefined;
|
|
143
|
+
export declare function sseinfoQa(transport: Requester, input: {
|
|
144
|
+
code?: string;
|
|
145
|
+
kind: string;
|
|
146
|
+
page: number;
|
|
147
|
+
pageSize: number;
|
|
148
|
+
signal?: AbortSignal;
|
|
149
|
+
uidLocateBudgetMs?: number;
|
|
150
|
+
}): Promise<SourceOutcome>;
|
|
151
|
+
/**
|
|
152
|
+
* 取东财 7×24 全球快讯。媒体的独立备份来源(与 `cls_telegraph` / `wscn_lives` 互备):
|
|
153
|
+
* 三条不同源、不同风控面,一条不可用另一条仍在。
|
|
154
|
+
*
|
|
155
|
+
* 实测(2026-09-24):GET + JSON,`code` 为**字符串** `"1"` 表示成功;条目含 `title` /
|
|
156
|
+
* `showTime`(已是北京时间字符串)/ `summary`。服务端会把它聚合到的财联社内容一并返回,
|
|
157
|
+
* 所以同一事件可能与其他来源重复——**去重由调用方按标题判断**,本工具不猜。
|
|
158
|
+
*/
|
|
159
|
+
export declare function eastmoneyFastNews(client: EastmoneyClient, input: {
|
|
160
|
+
limit: number;
|
|
161
|
+
signal?: AbortSignal;
|
|
162
|
+
}): Promise<SourceOutcome>;
|
|
163
|
+
/**
|
|
164
|
+
* 取某只股票的东财新闻(JSONP 接口,正文在 `cmsArticleWebOld` 数组里)。
|
|
165
|
+
*
|
|
166
|
+
* ⚠️ 必须区分两种"没结果"(参考实践记为 #18,本仓 2026-09-24 实测为正常返回):
|
|
167
|
+
* 上游对部分 IP 间歇风控时只返回 `passportWeb`(股民资料)而**没有** `cmsArticleWebOld` 键——
|
|
168
|
+
* 这与"该股确实没有新闻"(键在、数组为空)是**两件事**。本函数按**键是否存在**判定,
|
|
169
|
+
* 缺键时报 `UPSTREAM`,绝不静默返回空表。
|
|
170
|
+
*/
|
|
171
|
+
export declare function eastmoneyStockNews(client: EastmoneyClient, input: {
|
|
172
|
+
code: string;
|
|
173
|
+
limit: number;
|
|
174
|
+
signal?: AbortSignal;
|
|
175
|
+
}): Promise<SourceOutcome>;
|
|
176
|
+
/**
|
|
177
|
+
* 取个股研报列表。
|
|
178
|
+
*
|
|
179
|
+
* ⚠️ **没有摘要可给**(2026-09-24 实测该接口 51 个字段):列表只有标题 / 机构 / 研究员 /
|
|
180
|
+
* 日期 / 评级。正文在那份 PDF 里——本机直连只处理文本(`application/pdf` 会被
|
|
181
|
+
* `web_retriever_fetch` 拒),要走 `ocr` 解析(§4.3)。所以本函数把**每条的 PDF 直链**交出去
|
|
182
|
+
* (`pdf_url`),通路才是完整的:没有它,模型手上没有任何稳定 `.pdf` URL,而人设又禁止编链接。
|
|
183
|
+
*
|
|
184
|
+
* 有用的派生信息:每篇研报自带分析师预测 EPS(`predictThisYearEps` / `predictNextYearEps` /
|
|
185
|
+
* `predictNextTwoYearEps`)与对应 PE,以及评级与评级变动(`emRatingName` / `ratingChange`)。
|
|
186
|
+
*
|
|
187
|
+
* ⚠️ 该接口只认**纯 6 位代码**:带 `SH` 前缀会返回 `hits=0`,看起来像"这只票没研报"。
|
|
188
|
+
*/
|
|
189
|
+
export declare function eastmoneyReports(client: EastmoneyClient, input: {
|
|
190
|
+
code: string;
|
|
191
|
+
pageSize: number;
|
|
192
|
+
pageNo: number;
|
|
193
|
+
signal?: AbortSignal;
|
|
194
|
+
}): Promise<SourceOutcome>;
|
|
195
|
+
/**
|
|
196
|
+
* 取新浪研报列表(研报第二来源,与 `eastmoney_reports` 互为备份)。
|
|
197
|
+
*
|
|
198
|
+
* ⚠️ 两条**与参考实践不同的实测结论**(2026-09-24,本仓复核):
|
|
199
|
+
* 1. 参考实践说"两次请求间隔 <1 秒会返回假的『没有找到相关内容』空页,必须 6 秒"。
|
|
200
|
+
* 本仓实测**连续 4 次无间隔全部成功**(分别为 40/39/39/38 条带序号行)——那个"限流"是误判。
|
|
201
|
+
* 2. 真正会让**个股**查询返回假空页的是**缺交易所前缀**:`688017` 纯 6 位 → 假空页;
|
|
202
|
+
* `sh688017` / `sz000001` → 正常。而 `600519` 纯 6 位恰好也能用(前缀可推断),
|
|
203
|
+
* 所以"纯 6 位能用"的样本会误导人。本函数**一律补前缀**。
|
|
204
|
+
* ⚠️ 空页 HTTP 200 且与"真的没有研报"长得一样,因此本函数按"带序号行数"与空页提示语双重判定。
|
|
205
|
+
*/
|
|
206
|
+
export declare function sinaReports(transport: Requester, input: {
|
|
207
|
+
code?: string;
|
|
208
|
+
page: number;
|
|
209
|
+
signal?: AbortSignal;
|
|
210
|
+
}): Promise<SourceOutcome>;
|
|
211
|
+
/**
|
|
212
|
+
* 新浪研报的 symbol 参数:一律带交易所前缀(实测缺前缀会让科创板返回假空页)。
|
|
213
|
+
*
|
|
214
|
+
* ⚠️ 顺序要紧:`92xxxx`(北交所**新号段**,如 832982→920982)以 9 开头,若先判 `9 → sh`
|
|
215
|
+
* 就会被发到上交所并拿到假空页。所以北交所号段(43/83/87/920)必须先判,
|
|
216
|
+
* 只有 `900xxx`(沪市 B 股)才归 sh。
|
|
217
|
+
*/
|
|
218
|
+
export declare function sinaSymbol(code: string): string;
|
|
219
|
+
/**
|
|
220
|
+
* 取同花顺「机构一致预期 EPS」表(HTML 表格,**GBK**)。
|
|
221
|
+
*
|
|
222
|
+
* ⚠️ 两个必须记住的实测结论(2026-09-24):
|
|
223
|
+
* 1. 该站声明 `content-type: text/html`(**不带 charset**)而正文是 GBK:
|
|
224
|
+
* 按 UTF-8 解会得到 11,800 个替换字符,中文与正则全部失效。因此本请求显式声明 `encoding: 'gbk'`
|
|
225
|
+
* (`HttpRequest.encoding` 只允许来源实现填写,不暴露给模型)。
|
|
226
|
+
* 2. 页面里有 **32 个 `<table>`**,只需认"含『每股收益』表头"的那一张(行结构固定为
|
|
227
|
+
* 年度 / 预测机构数 / 最小值 / 均值 / 最大值 / 行业平均数)。**均值即机构一致预期 EPS**。
|
|
228
|
+
*
|
|
229
|
+
* 另附页面摘要块里的"预测机构数 + 同比增速"(与表格同源,作为交叉印证)。
|
|
230
|
+
*/
|
|
231
|
+
export declare function thsEpsForecast(transport: Requester, input: {
|
|
232
|
+
code: string;
|
|
233
|
+
signal?: AbortSignal;
|
|
234
|
+
}): Promise<SourceOutcome>;
|
|
235
|
+
export {};
|