@dsh-ft/dsh-fft-ftshare 0.0.0-stage → 0.3.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/LICENSE +21 -0
- package/README.md +80 -2
- package/catalog.json +9762 -0
- package/catalog.meta.json +7 -0
- package/catalog.txt +250 -0
- package/cordis.patch.yml +27 -0
- package/lib/capabilities.d.ts +47 -0
- package/lib/capabilities.d.ts.map +1 -0
- package/lib/capabilities.js +145 -0
- package/lib/capabilities.js.map +1 -0
- package/lib/catalog-host.d.ts +19 -0
- package/lib/catalog-host.d.ts.map +1 -0
- package/lib/catalog-host.js +31 -0
- package/lib/catalog-host.js.map +1 -0
- package/lib/catalog.d.ts +51 -0
- package/lib/catalog.d.ts.map +1 -0
- package/lib/catalog.js +60 -0
- package/lib/catalog.js.map +1 -0
- package/lib/client.d.ts +52 -0
- package/lib/client.d.ts.map +1 -0
- package/lib/client.js +149 -0
- package/lib/client.js.map +1 -0
- package/lib/contract.d.ts +122 -0
- package/lib/contract.d.ts.map +1 -0
- package/lib/contract.js +24 -0
- package/lib/contract.js.map +1 -0
- package/lib/errors.d.ts +93 -0
- package/lib/errors.d.ts.map +1 -0
- package/lib/errors.js +261 -0
- package/lib/errors.js.map +1 -0
- package/lib/index.d.ts +64 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +160 -0
- package/lib/index.js.map +1 -0
- package/lib/mask.d.ts +66 -0
- package/lib/mask.d.ts.map +1 -0
- package/lib/mask.js +106 -0
- package/lib/mask.js.map +1 -0
- package/lib/project.d.ts +98 -0
- package/lib/project.d.ts.map +1 -0
- package/lib/project.js +326 -0
- package/lib/project.js.map +1 -0
- package/lib/route.d.ts +48 -0
- package/lib/route.d.ts.map +1 -0
- package/lib/route.js +90 -0
- package/lib/route.js.map +1 -0
- package/lib/tools.d.ts +31 -0
- package/lib/tools.d.ts.map +1 -0
- package/lib/tools.js +132 -0
- package/lib/tools.js.map +1 -0
- package/lib/transport.d.ts +53 -0
- package/lib/transport.d.ts.map +1 -0
- package/lib/transport.js +147 -0
- package/lib/transport.js.map +1 -0
- package/package.json +91 -4
package/lib/errors.js
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 上游失败的归一:把 FTShare 网关千奇百怪的报错折成 {@link FtshareNotice}
|
|
3
|
+
* 加一个四态结局。
|
|
4
|
+
*
|
|
5
|
+
* 这是 P8 的解药所在。网关对「套餐没开通这个接口」返回的是 HTTP 403 +
|
|
6
|
+
* `route_not_allowed`,`dsh-kline` 把它归类成 `capability_limited` 之后,摘要里
|
|
7
|
+
* 只留下一个 `0`;调用方于是无法区分「今天真的没有涨停事件」和「这个套餐根本
|
|
8
|
+
* 调不了这个接口」。本模块只做一件事:把权限失败与真实失败分开,并各自带上
|
|
9
|
+
* 用户能看懂的中文说明。
|
|
10
|
+
* @module @dsh-ft/dsh-fft-ftshare/errors
|
|
11
|
+
*/
|
|
12
|
+
import { FTSHARE_UPGRADE_URL } from './contract.js';
|
|
13
|
+
/**
|
|
14
|
+
* 权限类错误码:上游 HTTP 层的 403 语义,以及本包与 `dsh-kline` 的归一码。
|
|
15
|
+
* 只列确定表达「无权」的码,不把 404/参数错误也算进来。**配额类不算权限**:匿名
|
|
16
|
+
* 通道有 500 次/日的额度,用尽与「套餐没开通」是两种事,见 {@link QUOTA_CODES}。
|
|
17
|
+
*/
|
|
18
|
+
const PERMISSION_CODES = new Set([
|
|
19
|
+
'403',
|
|
20
|
+
'forbidden',
|
|
21
|
+
'route_not_allowed',
|
|
22
|
+
'capability_limited',
|
|
23
|
+
'permission_denied',
|
|
24
|
+
'insufficient_permissions',
|
|
25
|
+
'unauthorized',
|
|
26
|
+
'401',
|
|
27
|
+
'invalid_key',
|
|
28
|
+
'invalid_api_key',
|
|
29
|
+
'auth_required',
|
|
30
|
+
'subscription_required',
|
|
31
|
+
]);
|
|
32
|
+
/**
|
|
33
|
+
* 独占的配额类错误码:出现即是额度问题。`429` 是它的 HTTP 形态。
|
|
34
|
+
*
|
|
35
|
+
* `upstream_unavailable` **不在这里**:匿名额度用尽和上游真的停机共用这个码,码本身
|
|
36
|
+
* 不能定案,见 {@link isQuotaSignal}。
|
|
37
|
+
*/
|
|
38
|
+
const QUOTA_CODES = new Set(['insufficient_quota', 'quota_exceeded', 'quota_exhausted', '429']);
|
|
39
|
+
/** 配额用尽时附在失败提示后的文案。 */
|
|
40
|
+
export const FTSHARE_QUOTA_NOTICE = '免费配额用尽,配置 FTShare API Key 或稍后重试';
|
|
41
|
+
/**
|
|
42
|
+
* 额度用尽的文案信号。
|
|
43
|
+
*
|
|
44
|
+
* 额度耗尽时上游只说人话不给专属码(「最近 1 天最多访问 500 次」),而匿名通道的
|
|
45
|
+
* 额度用尽与真实停机共用 `UPSTREAM_UNAVAILABLE`。这里只认「用尽 / 耗尽 / 最多访问」
|
|
46
|
+
* 这类表述,**不认「额度」二字本身**:套餐提示里同样会出现「额度」,先按文案判权限
|
|
47
|
+
* 就会把额度问题说成「套餐没开通」。
|
|
48
|
+
*/
|
|
49
|
+
const QUOTA_TEXT = /(?:配额|额度)[^。;;]{0,10}(?:用尽|用满|耗尽|用完)|最多访问|(?:超出|超过)[^。;;]{0,6}(?:额度|配额|上限)|quota[\s_-]*(?:exceeded|exhausted)/i;
|
|
50
|
+
/**
|
|
51
|
+
* 一个码或一句话是否表达「额度用尽」。
|
|
52
|
+
* @param code - 归一后的上游错误码。
|
|
53
|
+
* @param upstreamCode - 信封里的上游 HTTP 状态码,可能没有。
|
|
54
|
+
* @param message - 上游说明。
|
|
55
|
+
* @returns 是否属于额度用尽。
|
|
56
|
+
*/
|
|
57
|
+
export function isQuotaSignal(code, upstreamCode, message) {
|
|
58
|
+
const exclusive = (candidate) => candidate !== undefined && QUOTA_CODES.has(candidate.toLowerCase());
|
|
59
|
+
if (exclusive(code) || exclusive(upstreamCode))
|
|
60
|
+
return true;
|
|
61
|
+
// 共用码(`upstream_unavailable`)与任何未知码都要文案佐证:真正停机也报这个码,
|
|
62
|
+
// 只说「bad gateway」时按额度处理会把停机说成额度用尽。
|
|
63
|
+
return QUOTA_TEXT.test(message);
|
|
64
|
+
}
|
|
65
|
+
/** 中文/英文文案里的权限信号。用于上游只给一句话、不给码的情况。 */
|
|
66
|
+
const PERMISSION_TEXT = /套餐|权限|订阅|升级|未开通|未授权|无权限|额度|permission|subscription|unauthori[sz]ed|forbidden|not\s+allowed/i;
|
|
67
|
+
/** 凭据本身有问题(未携带、校验失败、含非法字符)。与套餐无关,但同样不是「没有数据」。 */
|
|
68
|
+
const API_KEY_TEXT = /(?:未收到|未通过|未配置|缺失|无效|不受支持|不完整)[^。;;]{0,12}API\s*Key|API\s*Key[^。;;]{0,12}(?:未收到|未通过|未配置|缺失|无效|校验失败|不受支持|不完整)/i;
|
|
69
|
+
/** 值得原样重试的失败:限流、超时、网络抖动、上游 5xx。 */
|
|
70
|
+
const RETRYABLE_CODES = new Set(['429', 'rate_limited', 'timeout', 'etimedout', 'econnreset', 'econnrefused', 'enotfound', 'fetch_failed', '502', '503', '504', 'upstream_error']);
|
|
71
|
+
/** 凭据类拒绝:API Key 缺失或无效,与套餐档位无关,措辞要分开。 */
|
|
72
|
+
const CREDENTIAL_CODES = new Set(['401', 'unauthorized', 'invalid_key', 'invalid_api_key', 'auth_required']);
|
|
73
|
+
/** 判断一个值是不是可当对象读的记录。 */
|
|
74
|
+
export function isRecord(value) {
|
|
75
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
76
|
+
}
|
|
77
|
+
/** 读一个非空字符串;其余一律 undefined。 */
|
|
78
|
+
export function asText(value) {
|
|
79
|
+
return typeof value === 'string' && value.trim() !== '' ? value.trim() : undefined;
|
|
80
|
+
}
|
|
81
|
+
/** 读一个非空字符串或数字码(上游的 `upstream_code` 是数字);其余一律 undefined。 */
|
|
82
|
+
function asCode(value) {
|
|
83
|
+
return typeof value === 'number' && Number.isFinite(value) ? String(value) : asText(value);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* 一个码或一句话是否表达「无权访问」。
|
|
87
|
+
* @param code - 上游错误码,可能没有。
|
|
88
|
+
* @param message - 上游错误文案,可能为空串。
|
|
89
|
+
* @returns 是否属于权限失败。
|
|
90
|
+
*/
|
|
91
|
+
export function isPermissionSignal(code, message) {
|
|
92
|
+
if (code !== undefined && PERMISSION_CODES.has(code.toLowerCase()))
|
|
93
|
+
return true;
|
|
94
|
+
return PERMISSION_TEXT.test(message) || API_KEY_TEXT.test(message);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* 一个码是否值得原样重试。
|
|
98
|
+
* @param code - 归一后的上游错误码。
|
|
99
|
+
* @param message - 上游说明。
|
|
100
|
+
* @param upstreamCode - 信封里的上游 HTTP 状态码:`UPSTREAM_UNAVAILABLE` 包裹的 502/504 仍可重试。
|
|
101
|
+
*/
|
|
102
|
+
export function isRetryableSignal(code, message, upstreamCode) {
|
|
103
|
+
if (code !== undefined && RETRYABLE_CODES.has(code.toLowerCase()))
|
|
104
|
+
return true;
|
|
105
|
+
if (upstreamCode !== undefined && RETRYABLE_CODES.has(upstreamCode.toLowerCase()))
|
|
106
|
+
return true;
|
|
107
|
+
return /超时|限流|重试|timeout|rate[_\s-]*limit|temporar|throttl/i.test(message);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* 从 MCP 客户端抛出的错误(其 message 常是被字符串化的一段上游 JSON)里抽出码与说明。
|
|
111
|
+
*
|
|
112
|
+
* 上游两种形态都出现过:结构化 `{"error":{"code":…,"message":…}}`,以及
|
|
113
|
+
* 一行纯文本。两者都要能读,读不出来就保留原文。
|
|
114
|
+
* @param value - 抛出物或上游返回的文本。
|
|
115
|
+
* @returns 归一后的码与说明。
|
|
116
|
+
*/
|
|
117
|
+
export function parseUpstreamError(value) {
|
|
118
|
+
const text = value instanceof Error ? value.message : typeof value === 'string' ? value : String(value);
|
|
119
|
+
const candidates = [];
|
|
120
|
+
try {
|
|
121
|
+
const parsed = JSON.parse(text);
|
|
122
|
+
if (isRecord(parsed)) {
|
|
123
|
+
candidates.push(parsed);
|
|
124
|
+
if (isRecord(parsed.error))
|
|
125
|
+
candidates.push(parsed.error);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
// 不是 JSON:按纯文本处理。
|
|
130
|
+
}
|
|
131
|
+
for (const candidate of candidates) {
|
|
132
|
+
const message = asText(candidate.message) ?? asText(candidate.error) ?? asText(candidate.detail);
|
|
133
|
+
if (message === undefined)
|
|
134
|
+
continue;
|
|
135
|
+
const code = asCode(candidate.code) ?? asCode(candidate.error_code);
|
|
136
|
+
const upstreamCode = asCode(candidate.upstream_code) ?? asCode(candidate.upstreamCode);
|
|
137
|
+
const requiredPlan = asText(candidate.required_plan) ?? asText(candidate.requiredPlan) ?? asText(candidate.plan);
|
|
138
|
+
return {
|
|
139
|
+
...code !== undefined ? { code } : {},
|
|
140
|
+
...upstreamCode !== undefined ? { upstreamCode } : {},
|
|
141
|
+
message,
|
|
142
|
+
...requiredPlan !== undefined ? { requiredPlan } : {},
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
// 纯文本里可能自带 `403 route_not_allowed` 这类码。
|
|
146
|
+
const matched = /\b(401|403|429|5\d\d)\b\s*([a-z_]+)?/iu.exec(text);
|
|
147
|
+
// 也可能只是 `code: 说明` 的前缀形式(MCP 客户端会把错误正文直接塞进 message)。
|
|
148
|
+
const prefixed = /^([a-z][a-z0-9_]{2,})\s*[::]/iu.exec(text.trim());
|
|
149
|
+
const code = asText(matched?.[2]) ?? asText(matched?.[1]) ?? asText(prefixed?.[1]);
|
|
150
|
+
const upstreamCode = asText(matched?.[1]);
|
|
151
|
+
return {
|
|
152
|
+
...code !== undefined ? { code } : {},
|
|
153
|
+
...upstreamCode !== undefined && upstreamCode !== code ? { upstreamCode } : {},
|
|
154
|
+
message: text.trim() || 'FTShare 未返回错误说明',
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* 造一条「无权访问」提示。措辞刻意把「不是没有数据」写进去:P8 的病根就是这句
|
|
159
|
+
* 缺失,让「套餐没开通」看起来像「今天什么都没有」。
|
|
160
|
+
*
|
|
161
|
+
* 档位只在上游自己给了 `required_plan` 时才写进提示:接口名里的版本号与订阅档位
|
|
162
|
+
* 没有可复核的对应关系,按它猜一个档位会把用户引向错误的套餐。
|
|
163
|
+
* @param tool - 上游工具名。
|
|
164
|
+
* @param code - 上游错误码,缺省按 `route_not_allowed` 记。
|
|
165
|
+
* @param message - 上游说明。
|
|
166
|
+
* @param requiredPlan - 上游信封里给出的所需档位;没给就不猜。
|
|
167
|
+
* @returns 可直接展示的提示。
|
|
168
|
+
*/
|
|
169
|
+
export function deniedNotice(tool, code, message, requiredPlan) {
|
|
170
|
+
const credential = code !== undefined && CREDENTIAL_CODES.has(code.toLowerCase());
|
|
171
|
+
const cause = credential
|
|
172
|
+
? `凭据未配置或无效(${code})`
|
|
173
|
+
: `当前套餐未开通该数据能力(上游 ${code ?? '403 route_not_allowed'})`;
|
|
174
|
+
const remedy = credential ? '有效的 API Key' : `订阅${requiredPlan ?? '更高的档位'}`;
|
|
175
|
+
return {
|
|
176
|
+
code: code ?? 'route_not_allowed',
|
|
177
|
+
message: `FTShare 无权访问 ${tool}:${cause},需要${remedy}。这不代表「没有数据」。上游说明:${message}`,
|
|
178
|
+
...requiredPlan !== undefined ? { requiredPlan } : {},
|
|
179
|
+
upgradeUrl: FTSHARE_UPGRADE_URL,
|
|
180
|
+
retryable: false,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* 把一次失败折成结局与提示。
|
|
185
|
+
* @param error - 抛出物。
|
|
186
|
+
* @param tool - 上游工具名,出现在提示正文里。
|
|
187
|
+
* @returns 结局(`denied` 或 `failed`)与可直接展示的提示。
|
|
188
|
+
*/
|
|
189
|
+
export function classifyFailure(error, tool) {
|
|
190
|
+
const parsed = parseUpstreamError(error);
|
|
191
|
+
// 顺序即优先级:显式权限码 > 额度用尽 > 权限文案 > 普通失败。
|
|
192
|
+
// 额度必须排在权限**文案**之前——额度提示里就带「额度」二字,先按文案判权限会把
|
|
193
|
+
// 「额度用尽」说成「套餐没开通」,并吞掉后面的配额后缀。
|
|
194
|
+
const explicitDenial = parsed.code !== undefined && PERMISSION_CODES.has(parsed.code.toLowerCase());
|
|
195
|
+
const quota = !explicitDenial && isQuotaSignal(parsed.code, parsed.upstreamCode, parsed.message);
|
|
196
|
+
if (quota) {
|
|
197
|
+
const code = parsed.code ?? 'upstream_unavailable';
|
|
198
|
+
return {
|
|
199
|
+
state: 'failed',
|
|
200
|
+
notice: {
|
|
201
|
+
code,
|
|
202
|
+
message: `FTShare ${tool} 调用失败(${code}):${parsed.message}。${FTSHARE_QUOTA_NOTICE}`,
|
|
203
|
+
retryable: true,
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
if (isPermissionSignal(parsed.code, parsed.message)) {
|
|
208
|
+
return { state: 'denied', notice: deniedNotice(tool, parsed.code, parsed.message, parsed.requiredPlan) };
|
|
209
|
+
}
|
|
210
|
+
const code = parsed.code ?? 'upstream_error';
|
|
211
|
+
const retryable = isRetryableSignal(parsed.code, parsed.message, parsed.upstreamCode);
|
|
212
|
+
const tail = retryable ? '。可稍后重试。' : '';
|
|
213
|
+
return {
|
|
214
|
+
state: 'failed',
|
|
215
|
+
notice: {
|
|
216
|
+
code,
|
|
217
|
+
message: `FTShare ${tool} 调用失败(${code}):${parsed.message}${tail}`,
|
|
218
|
+
retryable,
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* 从「调用成功但信封说自己无权」的载荷里读出权限提示。
|
|
224
|
+
*
|
|
225
|
+
* 部分网关把订阅门禁放在成功信封的 `metadata.permissions` 里,而不是返回错误;
|
|
226
|
+
* 只看空数组就会把这种情况读成「没有数据」。这里的标记判定与投研舱既有实现保持
|
|
227
|
+
* 同一口径:必须出现显式的权限标记或权限文案,普通空数组不算。
|
|
228
|
+
* @param tool - 上游工具名。
|
|
229
|
+
* @param value - 上游返回的结构化载荷。
|
|
230
|
+
* @returns 权限提示;没有标记时 undefined。
|
|
231
|
+
*/
|
|
232
|
+
export function permissionNoticeFromPayload(tool, value) {
|
|
233
|
+
if (!isRecord(value))
|
|
234
|
+
return undefined;
|
|
235
|
+
const records = [value];
|
|
236
|
+
const metadata = value.metadata;
|
|
237
|
+
if (isRecord(metadata)) {
|
|
238
|
+
records.push(metadata);
|
|
239
|
+
if (Array.isArray(metadata.permissions))
|
|
240
|
+
records.push(...metadata.permissions.filter(isRecord));
|
|
241
|
+
if (isRecord(metadata.permission))
|
|
242
|
+
records.push(metadata.permission);
|
|
243
|
+
}
|
|
244
|
+
if (isRecord(value.permission))
|
|
245
|
+
records.push(value.permission);
|
|
246
|
+
if (Array.isArray(value.permissions))
|
|
247
|
+
records.push(...value.permissions.filter(isRecord));
|
|
248
|
+
for (const record of records) {
|
|
249
|
+
const code = asText(record.code) ?? asText(record.error_code);
|
|
250
|
+
const message = asText(record.message) ?? asText(record.reason) ?? asText(record.detail) ?? '';
|
|
251
|
+
const marker = [record.access_denied, record.accessDenied, record.permission_denied, record.permissionDenied,
|
|
252
|
+
record.subscription_required, record.subscriptionRequired, record.upgrade_required, record.upgradeRequired]
|
|
253
|
+
.some(flag => flag === true);
|
|
254
|
+
if (!marker && !isPermissionSignal(code, message))
|
|
255
|
+
continue;
|
|
256
|
+
const requiredPlan = asText(record.required_plan) ?? asText(record.requiredPlan) ?? asText(record.plan);
|
|
257
|
+
return deniedNotice(tool, code, message || '上游返回了订阅权限限制', requiredPlan);
|
|
258
|
+
}
|
|
259
|
+
return undefined;
|
|
260
|
+
}
|
|
261
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 宿主平面(**全局**)入口:`provide('ftshareData')`。
|
|
3
|
+
*
|
|
4
|
+
* 为什么这一行必须挂在全局、而不是金融 preset 里:preset 内 `provide` 会直接抛
|
|
5
|
+
* `Preset services require isolate realms`
|
|
6
|
+
* (`@deepseek-ai/dsh-agent-preset-registry` 的 `mount.ts`,`mountPreset` 结尾的
|
|
7
|
+
* `leakedServices` 检查);而按提示加上 `isolate` 之后,服务会变成 preset 私有
|
|
8
|
+
* 的隔离域,preset 之外的插件再也 `inject` 不到,公用底座就白叫了。所以:
|
|
9
|
+
* **provider 全局,工具与投影钩子在 preset**(见 `./tools`、`./project`)。
|
|
10
|
+
*
|
|
11
|
+
* 凭据一律走 `ctx.ftshareSettings.apiKey.resolve()`,本包不读任何凭据文件。
|
|
12
|
+
* @module @dsh-ft/dsh-fft-ftshare
|
|
13
|
+
*/
|
|
14
|
+
import type { Context, Logger } from '@deepseek-ai/cordis';
|
|
15
|
+
import { type FtshareDataService } from './contract.js';
|
|
16
|
+
import { type FtshareTransport } from './transport.js';
|
|
17
|
+
export * from './contract.js';
|
|
18
|
+
export { FTSHARE_GATEWAY_ENDPOINT } from './transport.js';
|
|
19
|
+
/** Cordis 插件名。 */
|
|
20
|
+
export declare const name = "@dsh-ft/dsh-fft-ftshare";
|
|
21
|
+
/** 凭据来自设置服务;缺这一项本行挂起等待,属刻意的 fail-loud。 */
|
|
22
|
+
export declare const inject: string[];
|
|
23
|
+
/** 本行发布的服务名。 */
|
|
24
|
+
export declare const provide: "ftshareData"[];
|
|
25
|
+
/** 宿主行的可选配置。 */
|
|
26
|
+
export interface FtshareDataConfig {
|
|
27
|
+
/** 覆盖网关地址;默认 {@link FTSHARE_GATEWAY_ENDPOINT}。 */
|
|
28
|
+
endpoint?: string;
|
|
29
|
+
/** MCP 客户端自称的名字,只影响上游日志。 */
|
|
30
|
+
clientName?: string;
|
|
31
|
+
}
|
|
32
|
+
/** 建服务所需的依赖,全部可注入,便于测试。 */
|
|
33
|
+
export interface FtshareDataServiceOptions {
|
|
34
|
+
/** 传输层;测试注入假实现即可完全不联网。 */
|
|
35
|
+
transport: FtshareTransport;
|
|
36
|
+
/** 具名 logger。 */
|
|
37
|
+
logger?: Logger;
|
|
38
|
+
/** 时钟,测试用来固定 `source.fetchedAt`。 */
|
|
39
|
+
now?: () => Date;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* 从任意上游载荷里找出明细行。
|
|
43
|
+
*
|
|
44
|
+
* 规则按优先级:数组自己就是行;`data`/`rows`/… 里第一个非空数组是行;否则找第一
|
|
45
|
+
* 个「元素是对象的数组」;再否则,若记录里还有非信封字段,就把整条记录当作一行
|
|
46
|
+
* (单对象返回值也是数据)。只有「信封里什么都没有」才返回空数组,也就是
|
|
47
|
+
* `empty`——这正是「真的没有数据」的定义。
|
|
48
|
+
* @param value - 上游结构化载荷。
|
|
49
|
+
* @returns 明细行;空数组表示确实没有数据。
|
|
50
|
+
*/
|
|
51
|
+
export declare function normalizeRows(value: unknown): Record<string, unknown>[];
|
|
52
|
+
/**
|
|
53
|
+
* 建一个 `ctx.ftshareData`。
|
|
54
|
+
* @param options - 见 {@link FtshareDataServiceOptions}。
|
|
55
|
+
* @returns 服务实现;`call` 不抛异常,失败走 `state`/`notice`。
|
|
56
|
+
*/
|
|
57
|
+
export declare function createFtshareDataService(options: FtshareDataServiceOptions): FtshareDataService;
|
|
58
|
+
/**
|
|
59
|
+
* 注册 `ctx.ftshareData`。
|
|
60
|
+
* @param ctx - 宿主上下文;必须是全局(根 loader)子树,preset 子树会抛错。
|
|
61
|
+
* @param config - 可选配置。
|
|
62
|
+
*/
|
|
63
|
+
export declare function apply(ctx: Context, config?: FtshareDataConfig): void;
|
|
64
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAG1D,OAAO,EAAgD,KAAK,kBAAkB,EAAwB,MAAM,eAAe,CAAA;AAE3H,OAAO,EAAoD,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAExG,cAAc,eAAe,CAAA;AAC7B,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAA;AAEzD,kBAAkB;AAClB,eAAO,MAAM,IAAI,4BAA4B,CAAA;AAE7C,0CAA0C;AAC1C,eAAO,MAAM,MAAM,UAAsB,CAAA;AAEzC,gBAAgB;AAChB,eAAO,MAAM,OAAO,iBAAyB,CAAA;AAa7C,gBAAgB;AAChB,MAAM,WAAW,iBAAiB;IAChC,kDAAkD;IAClD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,4BAA4B;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,2BAA2B;AAC3B,MAAM,WAAW,yBAAyB;IACxC,0BAA0B;IAC1B,SAAS,EAAE,gBAAgB,CAAA;IAC3B,iBAAiB;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,oCAAoC;IACpC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;CACjB;AAmBD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAiBvE;AAQD;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,yBAAyB,GAAG,kBAAkB,CAoC/F;AAuBD;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAUpE"}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 宿主平面(**全局**)入口:`provide('ftshareData')`。
|
|
3
|
+
*
|
|
4
|
+
* 为什么这一行必须挂在全局、而不是金融 preset 里:preset 内 `provide` 会直接抛
|
|
5
|
+
* `Preset services require isolate realms`
|
|
6
|
+
* (`@deepseek-ai/dsh-agent-preset-registry` 的 `mount.ts`,`mountPreset` 结尾的
|
|
7
|
+
* `leakedServices` 检查);而按提示加上 `isolate` 之后,服务会变成 preset 私有
|
|
8
|
+
* 的隔离域,preset 之外的插件再也 `inject` 不到,公用底座就白叫了。所以:
|
|
9
|
+
* **provider 全局,工具与投影钩子在 preset**(见 `./tools`、`./project`)。
|
|
10
|
+
*
|
|
11
|
+
* 凭据一律走 `ctx.ftshareSettings.apiKey.resolve()`,本包不读任何凭据文件。
|
|
12
|
+
* @module @dsh-ft/dsh-fft-ftshare
|
|
13
|
+
*/
|
|
14
|
+
import { FTSHARE_DATA_SERVICE } from './contract.js';
|
|
15
|
+
import { asText, classifyFailure, isRecord, permissionNoticeFromPayload } from './errors.js';
|
|
16
|
+
import { createFtshareTransport } from './transport.js';
|
|
17
|
+
export * from './contract.js';
|
|
18
|
+
export { FTSHARE_GATEWAY_ENDPOINT } from './transport.js';
|
|
19
|
+
/** Cordis 插件名。 */
|
|
20
|
+
export const name = '@dsh-ft/dsh-fft-ftshare';
|
|
21
|
+
/** 凭据来自设置服务;缺这一项本行挂起等待,属刻意的 fail-loud。 */
|
|
22
|
+
export const inject = ['ftshareSettings'];
|
|
23
|
+
/** 本行发布的服务名。 */
|
|
24
|
+
export const provide = [FTSHARE_DATA_SERVICE];
|
|
25
|
+
/**
|
|
26
|
+
* 载荷里当「明细容器」看待的键,按优先级排列。
|
|
27
|
+
*
|
|
28
|
+
* 上游数据接口的返回形态不统一:多数是 `{"data":[…]}`,也有 `rows`/`items`/
|
|
29
|
+
* `result`。这个列表是**读取顺序**,不是白名单——列表里没有的键会在兜底扫描里
|
|
30
|
+
* 作为「任意数组」被找到。
|
|
31
|
+
*/
|
|
32
|
+
const PAYLOAD_KEYS = ['data', 'rows', 'items', 'records', 'list', 'result', 'payload', 'response'];
|
|
33
|
+
/** 只描述调用本身、不含事实的键;判断「是否存在事实」时忽略。 */
|
|
34
|
+
const ENVELOPE_KEYS = new Set(['ok', 'success', 'cached', 'metadata', 'message', 'error', 'code', 'status', 'request_id', 'permission', 'permissions', 'source']);
|
|
35
|
+
/** 只留下记录元素。 */
|
|
36
|
+
function recordsOf(value) {
|
|
37
|
+
return value.filter(isRecord);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* 从任意上游载荷里找出明细行。
|
|
41
|
+
*
|
|
42
|
+
* 规则按优先级:数组自己就是行;`data`/`rows`/… 里第一个非空数组是行;否则找第一
|
|
43
|
+
* 个「元素是对象的数组」;再否则,若记录里还有非信封字段,就把整条记录当作一行
|
|
44
|
+
* (单对象返回值也是数据)。只有「信封里什么都没有」才返回空数组,也就是
|
|
45
|
+
* `empty`——这正是「真的没有数据」的定义。
|
|
46
|
+
* @param value - 上游结构化载荷。
|
|
47
|
+
* @returns 明细行;空数组表示确实没有数据。
|
|
48
|
+
*/
|
|
49
|
+
export function normalizeRows(value) {
|
|
50
|
+
if (Array.isArray(value))
|
|
51
|
+
return recordsOf(value);
|
|
52
|
+
if (!isRecord(value))
|
|
53
|
+
return [];
|
|
54
|
+
for (const key of PAYLOAD_KEYS) {
|
|
55
|
+
if (!(key in value))
|
|
56
|
+
continue;
|
|
57
|
+
const found = normalizeRows(value[key]);
|
|
58
|
+
if (found.length > 0)
|
|
59
|
+
return found;
|
|
60
|
+
}
|
|
61
|
+
for (const [key, candidate] of Object.entries(value)) {
|
|
62
|
+
if (ENVELOPE_KEYS.has(key) || !Array.isArray(candidate))
|
|
63
|
+
continue;
|
|
64
|
+
const found = recordsOf(candidate);
|
|
65
|
+
if (found.length > 0)
|
|
66
|
+
return found;
|
|
67
|
+
}
|
|
68
|
+
// 兜底:没有任何数组键、但确实带了非信封字段时,整条记录算一行。数组键(哪怕是
|
|
69
|
+
// 空数组)已经在上面的扫描里表过态,不能再把 `{"data": []}` 读成「有一条数据」。
|
|
70
|
+
const meaningful = Object.keys(value).filter(key => !ENVELOPE_KEYS.has(key) && !Array.isArray(value[key]));
|
|
71
|
+
return meaningful.length > 0 ? [value] : [];
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* 上游接口清单的缓存时长。清单一天最多变几次,而目录产物是发版时生成的,运行期
|
|
75
|
+
* 只用来回答「这个接口名上游认不认」,所以短缓存足够。
|
|
76
|
+
*/
|
|
77
|
+
const TOOLS_TTL_MS = 5 * 60_000;
|
|
78
|
+
/**
|
|
79
|
+
* 建一个 `ctx.ftshareData`。
|
|
80
|
+
* @param options - 见 {@link FtshareDataServiceOptions}。
|
|
81
|
+
* @returns 服务实现;`call` 不抛异常,失败走 `state`/`notice`。
|
|
82
|
+
*/
|
|
83
|
+
export function createFtshareDataService(options) {
|
|
84
|
+
const now = options.now ?? (() => new Date());
|
|
85
|
+
let cache;
|
|
86
|
+
return {
|
|
87
|
+
async call(interfaceName, args = {}, callOptions) {
|
|
88
|
+
const source = { name: 'FTShare', fetchedAt: now().toISOString() };
|
|
89
|
+
const startedAt = Date.now();
|
|
90
|
+
try {
|
|
91
|
+
const { value } = await options.transport.call(interfaceName, args, callOptions);
|
|
92
|
+
const rows = normalizeRows(value);
|
|
93
|
+
if (rows.length > 0)
|
|
94
|
+
return { ok: true, state: 'available', rows, source };
|
|
95
|
+
const gated = permissionNoticeFromPayload(interfaceName, value);
|
|
96
|
+
// 成功信封 + 权限标记 = 无权,不是没有数据。这是 P8 的第二种形态。
|
|
97
|
+
if (gated !== undefined)
|
|
98
|
+
return { ok: false, state: 'denied', rows: [], notice: gated, source };
|
|
99
|
+
return { ok: true, state: 'empty', rows: [], source };
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
const { state, notice } = classifyFailure(error, interfaceName);
|
|
103
|
+
options.logger?.warn('ftshare.call_classified', { tool: interfaceName, state, code: notice.code, durationMs: Date.now() - startedAt });
|
|
104
|
+
return { ok: false, state, rows: [], notice, source };
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
async listTools() {
|
|
108
|
+
if (cache !== undefined && Date.now() - cache.at < TOOLS_TTL_MS)
|
|
109
|
+
return cache.tools;
|
|
110
|
+
try {
|
|
111
|
+
const tools = await options.transport.listTools();
|
|
112
|
+
cache = { tools, at: Date.now() };
|
|
113
|
+
return tools;
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
// 清单是增强信息,不该让调用方拿到异常;失败就退回上一次的清单。
|
|
117
|
+
options.logger?.warn('ftshare.list_tools_failed', { message: error instanceof Error ? error.message : String(error) });
|
|
118
|
+
return cache?.tools ?? [];
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* 取一次凭据。设置服务缺失或读取抛错都不让整行崩掉:返回 undefined,网关会以
|
|
125
|
+
* 「未携带 API Key」给出可诊断的拒绝。
|
|
126
|
+
* @param ctx - 宿主上下文。
|
|
127
|
+
* @param logger - 具名 logger。
|
|
128
|
+
* @returns API Key,或 undefined。
|
|
129
|
+
*/
|
|
130
|
+
async function resolveApiKey(ctx, logger) {
|
|
131
|
+
const apiKey = ctx.ftshareSettings?.apiKey;
|
|
132
|
+
if (apiKey === undefined) {
|
|
133
|
+
logger.warn('ftshare.api_key_namespace_missing', { message: '设置服务未注册 API Key 命名空间,FTShare 取数将不可用' });
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
return asText(await apiKey.resolve());
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
logger.warn('ftshare.api_key_resolve_failed', { message: error instanceof Error ? error.message : String(error) });
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* 注册 `ctx.ftshareData`。
|
|
146
|
+
* @param ctx - 宿主上下文;必须是全局(根 loader)子树,preset 子树会抛错。
|
|
147
|
+
* @param config - 可选配置。
|
|
148
|
+
*/
|
|
149
|
+
export function apply(ctx, config) {
|
|
150
|
+
const logger = ctx.logger('dsh-fft-ftshare');
|
|
151
|
+
const transport = createFtshareTransport({
|
|
152
|
+
endpoint: config?.endpoint,
|
|
153
|
+
clientName: config?.clientName,
|
|
154
|
+
resolveApiKey: () => resolveApiKey(ctx, logger),
|
|
155
|
+
logger,
|
|
156
|
+
});
|
|
157
|
+
const host = ctx;
|
|
158
|
+
host.provide(FTSHARE_DATA_SERVICE, createFtshareDataService({ transport, logger }));
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/lib/mask.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* preset 平面:把 `dsh_kline` 的 **FTShare wrapper 工具**从模型可见面掩掉。
|
|
3
|
+
*
|
|
4
|
+
* ### 为什么掩而不是删
|
|
5
|
+
*
|
|
6
|
+
* kline 的 MCP 工具是那一层(mcp 行)的自有注册,MCP 客户端**没有工具白名单**
|
|
7
|
+
* (`@deepseek-ai/dsh-mcp-client`),所以「只挂一部分 MCP 工具」做不到,只能在
|
|
8
|
+
* 可见性上掩。替代能力走 `ctx.ftshareData`(`ftshare_*` 与 `ftshare_call`),
|
|
9
|
+
* 这也是「替代接口必须进接口表」的硬理由。
|
|
10
|
+
*
|
|
11
|
+
* ### 为什么落到 agent 自己的 layer
|
|
12
|
+
*
|
|
13
|
+
* `ctx.tools.restrict()` 只过滤**继承来的**工具,永不过滤调用者自己层的注册
|
|
14
|
+
* (`@deepseek-ai/dsh-tools` 的 `view()`)。preset 行 ctx 的 scope 就是它的
|
|
15
|
+
* standing scope,kline 工具属于那一层的自有注册,在它上面 restrict 会直接抛
|
|
16
|
+
* `unknown global tool`。落到 agent 自己的 layer 还有两个好处:随 agent 销毁自动
|
|
17
|
+
* 解除,且只影响这一支。
|
|
18
|
+
*
|
|
19
|
+
* ### 四个坑(都实测过)
|
|
20
|
+
*
|
|
21
|
+
* 1. **未知名字会抛**:必须与当前可见集取交集再 restrict。
|
|
22
|
+
* 2. **`agent/created` 是串行监听**:监听器抛错会连带 agent 创建失败,因此这里
|
|
23
|
+
* 自己 try/catch 记 warn,绝不上抛。
|
|
24
|
+
* 3. **MCP 工具可能晚于 agent 创建到达**(stdio + 重连),所以同时监听
|
|
25
|
+
* `tools/change`;掩掉的工具会从可见集消失,因此「交集」本身就是去重,不会
|
|
26
|
+
* 重复 append 限制。
|
|
27
|
+
* 4. **清单只有一处**:{@link MASKED_KLINE_TOOLS},由 `dsh-fft-bundle` 的契约测试
|
|
28
|
+
* 断言它是 kline `server.py` 里 `@mcp.tool` 名字集合的子集。
|
|
29
|
+
* @module @dsh-ft/dsh-fft-ftshare/mask
|
|
30
|
+
*/
|
|
31
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
32
|
+
/** Cordis 插件名。 */
|
|
33
|
+
export declare const name = "@dsh-ft/dsh-fft-ftshare/mask";
|
|
34
|
+
/**
|
|
35
|
+
* 被掩掉的 kline 工具(MCP 公共名去掉 `mcp__<server>__` 前缀之后的那一段)。
|
|
36
|
+
*
|
|
37
|
+
* 只掩 FTShare wrapper:它们的实现直接连 FTShare 网关,凭据与错误语义都自成一套,
|
|
38
|
+
* 现在由本底座统一承接。「行情 / K 线 / 指标 / 自选」是 kline 的保留能力,一条都不
|
|
39
|
+
* 在这里。
|
|
40
|
+
*
|
|
41
|
+
* 边界由人定过一次(2026-10-16):`configure_ftshare` / `test_ftshare_connection` /
|
|
42
|
+
* `data_source_status` **不掩** —— 它们属于 kline 自己的设置与自检面,掩掉会让
|
|
43
|
+
* kline 侧边栏的既有路径失效。
|
|
44
|
+
*/
|
|
45
|
+
export declare const MASKED_KLINE_TOOLS: readonly string[];
|
|
46
|
+
/**
|
|
47
|
+
* 一个 kline 工具名对应的 MCP 公共名。
|
|
48
|
+
* @param tool - 短名,例如 `market_pulse`。
|
|
49
|
+
* @returns 公共名,例如 `mcp__dsh-kline__market_pulse`。
|
|
50
|
+
*/
|
|
51
|
+
export declare function publicToolName(tool: string): string;
|
|
52
|
+
/**
|
|
53
|
+
* 纯函数:算出这次该 deny 哪些名字。
|
|
54
|
+
*
|
|
55
|
+
* 与当前可见集取交集是必须的——`restrict` 对未知名字直接抛错。掩掉的工具会从可见集
|
|
56
|
+
* 消失,所以下一次调用自然只剩新到达的那些,不需要另记「已 deny 集合」。
|
|
57
|
+
* @param visible - 该 agent 当前可见的工具名。
|
|
58
|
+
* @returns 需要 deny 的名字,按 {@link MASKED_KLINE_TOOLS} 的顺序。
|
|
59
|
+
*/
|
|
60
|
+
export declare function maskPlan(visible: Iterable<string>): string[];
|
|
61
|
+
/**
|
|
62
|
+
* 注册掩码行。
|
|
63
|
+
* @param ctx - preset 子树的上下文(standing scope)。
|
|
64
|
+
*/
|
|
65
|
+
export declare function apply(ctx: Context): void;
|
|
66
|
+
//# sourceMappingURL=mask.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mask.d.ts","sourceRoot":"","sources":["../src/mask.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAIlD,kBAAkB;AAClB,eAAO,MAAM,IAAI,iCAAiC,CAAA;AAElD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,EAAE,SAAS,MAAM,EAI/C,CAAA;AA8BD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE,CAG5D;AAED;;;GAGG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CA+BxC"}
|