dsh-kenari-plugin 0.1.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.
Files changed (116) hide show
  1. package/LICENSE +21 -0
  2. package/README.en.md +281 -0
  3. package/README.md +283 -0
  4. package/assets/kenari-favicon-128.png +0 -0
  5. package/assets/kenari-favicon-256.png +0 -0
  6. package/client/index.js +1998 -0
  7. package/cordis.patch.yml +137 -0
  8. package/lib/billing.d.ts +120 -0
  9. package/lib/billing.d.ts.map +1 -0
  10. package/lib/billing.js +209 -0
  11. package/lib/billing.js.map +1 -0
  12. package/lib/catalog-view.d.ts +78 -0
  13. package/lib/catalog-view.d.ts.map +1 -0
  14. package/lib/catalog-view.js +113 -0
  15. package/lib/catalog-view.js.map +1 -0
  16. package/lib/catalog.d.ts +239 -0
  17. package/lib/catalog.d.ts.map +1 -0
  18. package/lib/catalog.js +445 -0
  19. package/lib/catalog.js.map +1 -0
  20. package/lib/default-route.d.ts +35 -0
  21. package/lib/default-route.d.ts.map +1 -0
  22. package/lib/default-route.js +111 -0
  23. package/lib/default-route.js.map +1 -0
  24. package/lib/errors.d.ts +29 -0
  25. package/lib/errors.d.ts.map +1 -0
  26. package/lib/errors.js +74 -0
  27. package/lib/errors.js.map +1 -0
  28. package/lib/favicon.d.ts +24 -0
  29. package/lib/favicon.d.ts.map +1 -0
  30. package/lib/favicon.js +68 -0
  31. package/lib/favicon.js.map +1 -0
  32. package/lib/http.d.ts +104 -0
  33. package/lib/http.d.ts.map +1 -0
  34. package/lib/http.js +247 -0
  35. package/lib/http.js.map +1 -0
  36. package/lib/index.d.ts +83 -0
  37. package/lib/index.d.ts.map +1 -0
  38. package/lib/index.js +285 -0
  39. package/lib/index.js.map +1 -0
  40. package/lib/llm/adapter.d.ts +78 -0
  41. package/lib/llm/adapter.d.ts.map +1 -0
  42. package/lib/llm/adapter.js +429 -0
  43. package/lib/llm/adapter.js.map +1 -0
  44. package/lib/llm/candidates.d.ts +57 -0
  45. package/lib/llm/candidates.d.ts.map +1 -0
  46. package/lib/llm/candidates.js +86 -0
  47. package/lib/llm/candidates.js.map +1 -0
  48. package/lib/llm/recovery.d.ts +48 -0
  49. package/lib/llm/recovery.d.ts.map +1 -0
  50. package/lib/llm/recovery.js +118 -0
  51. package/lib/llm/recovery.js.map +1 -0
  52. package/lib/llm/retry.d.ts +35 -0
  53. package/lib/llm/retry.d.ts.map +1 -0
  54. package/lib/llm/retry.js +53 -0
  55. package/lib/llm/retry.js.map +1 -0
  56. package/lib/plans.d.ts +59 -0
  57. package/lib/plans.d.ts.map +1 -0
  58. package/lib/plans.js +118 -0
  59. package/lib/plans.js.map +1 -0
  60. package/lib/session-title.d.ts +63 -0
  61. package/lib/session-title.d.ts.map +1 -0
  62. package/lib/session-title.js +280 -0
  63. package/lib/session-title.js.map +1 -0
  64. package/lib/settings.d.ts +35 -0
  65. package/lib/settings.d.ts.map +1 -0
  66. package/lib/settings.js +78 -0
  67. package/lib/settings.js.map +1 -0
  68. package/lib/tools/account.d.ts +11 -0
  69. package/lib/tools/account.d.ts.map +1 -0
  70. package/lib/tools/account.js +132 -0
  71. package/lib/tools/account.js.map +1 -0
  72. package/lib/tools/billing.d.ts +10 -0
  73. package/lib/tools/billing.d.ts.map +1 -0
  74. package/lib/tools/billing.js +100 -0
  75. package/lib/tools/billing.js.map +1 -0
  76. package/lib/tools/count-tokens.d.ts +11 -0
  77. package/lib/tools/count-tokens.d.ts.map +1 -0
  78. package/lib/tools/count-tokens.js +84 -0
  79. package/lib/tools/count-tokens.js.map +1 -0
  80. package/lib/tools/data.d.ts +12 -0
  81. package/lib/tools/data.d.ts.map +1 -0
  82. package/lib/tools/data.js +205 -0
  83. package/lib/tools/data.js.map +1 -0
  84. package/lib/tools/docs.d.ts +13 -0
  85. package/lib/tools/docs.d.ts.map +1 -0
  86. package/lib/tools/docs.js +209 -0
  87. package/lib/tools/docs.js.map +1 -0
  88. package/lib/tools/documents.d.ts +10 -0
  89. package/lib/tools/documents.d.ts.map +1 -0
  90. package/lib/tools/documents.js +118 -0
  91. package/lib/tools/documents.js.map +1 -0
  92. package/lib/tools/media.d.ts +28 -0
  93. package/lib/tools/media.d.ts.map +1 -0
  94. package/lib/tools/media.js +599 -0
  95. package/lib/tools/media.js.map +1 -0
  96. package/lib/tools/shared.d.ts +89 -0
  97. package/lib/tools/shared.d.ts.map +1 -0
  98. package/lib/tools/shared.js +128 -0
  99. package/lib/tools/shared.js.map +1 -0
  100. package/lib/tools/x-search.d.ts +11 -0
  101. package/lib/tools/x-search.d.ts.map +1 -0
  102. package/lib/tools/x-search.js +129 -0
  103. package/lib/tools/x-search.js.map +1 -0
  104. package/lib/web/fallback.d.ts +39 -0
  105. package/lib/web/fallback.d.ts.map +1 -0
  106. package/lib/web/fallback.js +79 -0
  107. package/lib/web/fallback.js.map +1 -0
  108. package/lib/web/fetch.d.ts +19 -0
  109. package/lib/web/fetch.d.ts.map +1 -0
  110. package/lib/web/fetch.js +41 -0
  111. package/lib/web/fetch.js.map +1 -0
  112. package/lib/web/search.d.ts +21 -0
  113. package/lib/web/search.d.ts.map +1 -0
  114. package/lib/web/search.js +47 -0
  115. package/lib/web/search.js.map +1 -0
  116. package/package.json +84 -0
@@ -0,0 +1,89 @@
1
+ /**
2
+ * REST 工具族共享基建:工具依赖、计费记账、成本回显与通用 render 辅助。
3
+ * 每个工具文件只声明自己的参数 schema 与端点映射,网络与错误映射集中在 http.ts,
4
+ * 目录与计价集中在 catalog.ts,账本集中在 billing.ts。
5
+ * @module dsh-kenari-plugin/tools/shared
6
+ */
7
+ import type { Context } from '@deepseek-ai/cordis';
8
+ import type { KenariHttpDeps } from '../http.js';
9
+ import type { Config } from '../index.js';
10
+ import type { KenariCatalog, KenariModel } from '../catalog.js';
11
+ import type { BalanceMonitor, BillingLedger } from '../billing.js';
12
+ export { formatRupiah, GLOBAL_SCOPE } from '../billing.js';
13
+ /** 工具族共享的运行时依赖:传输层 + 目录 + 账本 + 余额监控 + attachment(经 ctx)。 */
14
+ export interface ToolsDeps {
15
+ readonly http: KenariHttpDeps;
16
+ readonly ctx: Context;
17
+ /** 实时配置视图:settings 提交后下一次操作即读到新值。 */
18
+ readonly config: Config;
19
+ /** 模型目录(TTL 缓存,公开端点)。 */
20
+ readonly catalog: KenariCatalog;
21
+ /** 计费账本(按会话累计)。 */
22
+ readonly billing: BillingLedger;
23
+ /** 余额监控(MCP 入口,TTL 缓存)。 */
24
+ readonly balance: BalanceMonitor;
25
+ }
26
+ /** 单次调用的计费回显行;cost 缺失时不产生误导。 */
27
+ export declare function costLine(microIdr: number | undefined): string;
28
+ /** 会话作用域:优先 dsh 的 SessionId,无 agent 的调用落到 global。 */
29
+ export declare function scopeOf(exec: {
30
+ agent?: {
31
+ id: string;
32
+ };
33
+ } | undefined): string;
34
+ /**
35
+ * 花费前预检:会话预算已用尽就拒绝(抛 KenariBudgetError)。
36
+ * 用在每个会产生费用的工具入口。
37
+ */
38
+ export declare function spendGuard(deps: ToolsDeps, exec: {
39
+ agent?: {
40
+ id: string;
41
+ };
42
+ } | undefined, tool: string): void;
43
+ /** 一次花费的记账输入。 */
44
+ export interface SpendInput {
45
+ tool: string;
46
+ model?: string;
47
+ /** 响应回显的实际扣费(micro-IDR);有它就以它为准。 */
48
+ costMicroIdr?: number;
49
+ /** 目录单价 × 数量推算的预估费用(micro-IDR)。 */
50
+ estimateMicroIdr?: number;
51
+ /** 预估口径说明,例如「1 张 × Rp 175」。 */
52
+ estimateNote?: string;
53
+ /** 明确的口径补充,例如「复用命中,本次免费」。 */
54
+ note?: string;
55
+ /** 该调用是否计费;false 表示免费工具(如目录查询、token 计数)。 */
56
+ billable?: boolean;
57
+ }
58
+ /**
59
+ * 记账并生成回显文本。实际值优先于预估值;两者都缺且声明计费时如实说明未计入,
60
+ * 避免模型把「没写费用」当成「免费」。
61
+ */
62
+ export declare function billSpend(deps: ToolsDeps, exec: {
63
+ agent?: {
64
+ id: string;
65
+ };
66
+ } | undefined, input: SpendInput): Promise<string>;
67
+ /**
68
+ * 非 token 单位按整单位上取。
69
+ *
70
+ * 实测(2026-09-10,mimo-v2-5-tts):21 字符的 TTS 输入按 1 个「1k 字符」单位计费
71
+ * Rp 250,而非 0.021 单位。所以 `image`/`second`/`1k_chars`/`song`/`request`/
72
+ * `megapixel` 这类按件单位都要向上取整,否则预估会低一到两个数量级。
73
+ * `token_1m` 是按 token 线性计价,保留小数才是正确口径。
74
+ */
75
+ export declare function billedUnits(units: number, unit: string | undefined): {
76
+ units: number;
77
+ roundedUp: boolean;
78
+ };
79
+ /**
80
+ * 按目录计价行预估一次调用:`units` 必须以该行的计费单位表达
81
+ * (图像=张、视频=秒、语音=千字符、token 类=百万 token)。
82
+ * 找不到模型或该端点单价时返回空预估(不阻断调用)。
83
+ */
84
+ export declare function estimateByUnit(deps: ToolsDeps, modelId: string, endpoint: string, units: number, variant?: string): Promise<{
85
+ model?: KenariModel;
86
+ microIdr?: number;
87
+ note?: string;
88
+ }>;
89
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/tools/shared.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAkB,MAAM,eAAe,CAAA;AAE/E,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAGlE,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAY1D,4DAA4D;AAC5D,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAA;IAC7B,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAA;IACrB,qCAAqC;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,yBAAyB;IACzB,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAA;IAC/B,mBAAmB;IACnB,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAA;IAC/B,2BAA2B;IAC3B,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAA;CACjC;AAED,gCAAgC;AAChC,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAE7D;AAED,qDAAqD;AACrD,wBAAgB,OAAO,CAAC,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAAG,SAAS,GAAG,MAAM,CAE5E;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAE5G;AAED,iBAAiB;AACjB,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oCAAoC;IACpC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,mCAAmC;IACnC,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,+BAA+B;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,6BAA6B;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,IAAI,EAAE,SAAS,EACf,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAAG,SAAS,EAC5C,KAAK,EAAE,UAAU,GAChB,OAAO,CAAC,MAAM,CAAC,CAuCjB;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAI1G;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC;IAAE,KAAK,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAwBpE"}
@@ -0,0 +1,128 @@
1
+ /**
2
+ * REST 工具族共享基建:工具依赖、计费记账、成本回显与通用 render 辅助。
3
+ * 每个工具文件只声明自己的参数 schema 与端点映射,网络与错误映射集中在 http.ts,
4
+ * 目录与计价集中在 catalog.ts,账本集中在 billing.ts。
5
+ * @module dsh-kenari-plugin/tools/shared
6
+ */
7
+ import { priceLineOf, unitLabel } from '../catalog.js';
8
+ import { GLOBAL_SCOPE, formatRupiah } from '../billing.js';
9
+ export { formatRupiah, GLOBAL_SCOPE } from '../billing.js';
10
+ /** 端点 → 目录 modality:只有专用能力模型不在默认目录里。 */
11
+ function modalityOfEndpoint(endpoint) {
12
+ switch (endpoint) {
13
+ case 'embeddings': return 'embedding';
14
+ case 'rerank': return 'rerank';
15
+ case 'moderations': return 'moderation';
16
+ default: return undefined;
17
+ }
18
+ }
19
+ /** 单次调用的计费回显行;cost 缺失时不产生误导。 */
20
+ export function costLine(microIdr) {
21
+ return microIdr === undefined ? '' : `\n\n费用:${formatRupiah(microIdr)}(micro_idr ${microIdr})`;
22
+ }
23
+ /** 会话作用域:优先 dsh 的 SessionId,无 agent 的调用落到 global。 */
24
+ export function scopeOf(exec) {
25
+ return exec?.agent?.id ?? GLOBAL_SCOPE;
26
+ }
27
+ /**
28
+ * 花费前预检:会话预算已用尽就拒绝(抛 KenariBudgetError)。
29
+ * 用在每个会产生费用的工具入口。
30
+ */
31
+ export function spendGuard(deps, exec, tool) {
32
+ deps.billing.assertWithinBudget(scopeOf(exec), tool);
33
+ }
34
+ /**
35
+ * 记账并生成回显文本。实际值优先于预估值;两者都缺且声明计费时如实说明未计入,
36
+ * 避免模型把「没写费用」当成「免费」。
37
+ */
38
+ export async function billSpend(deps, exec, input) {
39
+ const billable = input.billable ?? true;
40
+ const scope = scopeOf(exec);
41
+ let head;
42
+ if (input.costMicroIdr !== undefined) {
43
+ deps.billing.recordSpend(scope, {
44
+ tool: input.tool,
45
+ ...(input.model !== undefined ? { model: input.model } : {}),
46
+ microIdr: input.costMicroIdr,
47
+ estimated: false,
48
+ });
49
+ head = `费用:${formatRupiah(input.costMicroIdr)}(实际扣费${input.costMicroIdr === 0 ? ',复用命中免费' : ''})`;
50
+ }
51
+ else if (input.estimateMicroIdr !== undefined) {
52
+ deps.billing.recordSpend(scope, {
53
+ tool: input.tool,
54
+ ...(input.model !== undefined ? { model: input.model } : {}),
55
+ microIdr: input.estimateMicroIdr,
56
+ estimated: true,
57
+ });
58
+ const per = input.estimateNote === undefined ? '' : `(${input.estimateNote})`;
59
+ head = `费用:约 ${formatRupiah(input.estimateMicroIdr)}(按目录单价预估${per},最终以账单为准)`;
60
+ }
61
+ else if (billable) {
62
+ head = '费用:本次调用既无回显也未查到单价,未计入累计';
63
+ }
64
+ if (head === undefined)
65
+ return '';
66
+ if (input.note !== undefined)
67
+ head = `${head};${input.note}`;
68
+ const totals = deps.billing.spendTotals(scope);
69
+ const scopeLabel = scope === GLOBAL_SCOPE ? '本进程' : '本会话';
70
+ const lines = [`${head}|${scopeLabel}累计:${formatRupiah(totals.microIdr)}(${totals.calls} 次)`];
71
+ const thresholdRp = deps.config.lowBalanceAlertRp ?? 0;
72
+ if (thresholdRp > 0) {
73
+ const alert = await deps.balance.alertIfLow(thresholdRp);
74
+ if (alert !== undefined)
75
+ lines.push(alert);
76
+ }
77
+ return `\n\n${lines.join('\n')}`;
78
+ }
79
+ /**
80
+ * 非 token 单位按整单位上取。
81
+ *
82
+ * 实测(2026-09-10,mimo-v2-5-tts):21 字符的 TTS 输入按 1 个「1k 字符」单位计费
83
+ * Rp 250,而非 0.021 单位。所以 `image`/`second`/`1k_chars`/`song`/`request`/
84
+ * `megapixel` 这类按件单位都要向上取整,否则预估会低一到两个数量级。
85
+ * `token_1m` 是按 token 线性计价,保留小数才是正确口径。
86
+ */
87
+ export function billedUnits(units, unit) {
88
+ if (unit === 'token_1m')
89
+ return { units, roundedUp: false };
90
+ const whole = Math.ceil(units);
91
+ return { units: whole, roundedUp: whole !== units };
92
+ }
93
+ /**
94
+ * 按目录计价行预估一次调用:`units` 必须以该行的计费单位表达
95
+ * (图像=张、视频=秒、语音=千字符、token 类=百万 token)。
96
+ * 找不到模型或该端点单价时返回空预估(不阻断调用)。
97
+ */
98
+ export async function estimateByUnit(deps, modelId, endpoint, units, variant) {
99
+ try {
100
+ const resolved = await deps.catalog.resolve(modelId, modalityOfEndpoint(endpoint));
101
+ if (resolved === undefined)
102
+ return {};
103
+ const line = priceLineOf(resolved.model, endpoint, variant);
104
+ if (line?.micro_idr === undefined)
105
+ return { model: resolved.model };
106
+ const label = unitLabel(line.unit);
107
+ const billed = billedUnits(units, line.unit);
108
+ const unitPrice = line.micro_idr;
109
+ const parts = [
110
+ `${Number.isInteger(billed.units) ? billed.units : billed.units.toFixed(6)} ${label}`,
111
+ `× Rp ${(unitPrice / 1_000_000).toLocaleString('id-ID')}`,
112
+ ];
113
+ if (billed.roundedUp)
114
+ parts.push(`(不足一单位按一单位计,输入量 ${units.toFixed(6)})`);
115
+ if (variant !== undefined)
116
+ parts.push(`@ ${variant}`);
117
+ return {
118
+ model: resolved.model,
119
+ microIdr: Math.round(unitPrice * billed.units),
120
+ note: parts.join(' '),
121
+ };
122
+ }
123
+ catch {
124
+ // 目录不可用时预估失效,调用本身继续
125
+ return {};
126
+ }
127
+ }
128
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/tools/shared.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * X(原 Twitter)搜索工具:POST /v1/x/search,过滤参数全量透传。
3
+ * allowed/excluded handles 互斥且各 ≤20(不含 @);日期 YYYY-MM-DD。
4
+ * 按次计费到余额,失败调用不计费。
5
+ * @module dsh-kenari-plugin/tools/x-search
6
+ */
7
+ import { defineTool } from '@deepseek-ai/dsh-tools';
8
+ import type { ToolsDeps } from './shared.js';
9
+ /** 注册 kenari_x_search。 */
10
+ export declare function registerXSearchTool(deps: ToolsDeps, register: (tool: ReturnType<typeof defineTool>) => void): void;
11
+ //# sourceMappingURL=x-search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"x-search.d.ts","sourceRoot":"","sources":["../../src/tools/x-search.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AA4B5C,0BAA0B;AAC1B,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,KAAK,IAAI,GAAG,IAAI,CA+FlH"}
@@ -0,0 +1,129 @@
1
+ /**
2
+ * X(原 Twitter)搜索工具:POST /v1/x/search,过滤参数全量透传。
3
+ * allowed/excluded handles 互斥且各 ≤20(不含 @);日期 YYYY-MM-DD。
4
+ * 按次计费到余额,失败调用不计费。
5
+ * @module dsh-kenari-plugin/tools/x-search
6
+ */
7
+ import { defineTool } from '@deepseek-ai/dsh-tools';
8
+ import { billSpend, spendGuard } from './shared.js';
9
+ import { kenariPost } from '../http.js';
10
+ /** handle 约束:≤20 个、不含 @(OpenAPI pattern 限字母数字下划线)。 */
11
+ const MAX_HANDLES = 20;
12
+ /** 校验 handle 数组:去 @ 前缀、查数量与字符集;违规抛参数错误。 */
13
+ function normalizeHandles(handles, field) {
14
+ const stripped = handles.map((handle) => handle.replace(/^@/, ''));
15
+ if (stripped.length > MAX_HANDLES) {
16
+ throw new Error(`${field} 最多 ${MAX_HANDLES} 个,收到 ${stripped.length} 个`);
17
+ }
18
+ for (const handle of stripped) {
19
+ if (!/^[A-Za-z0-9_]{1,40}$/.test(handle)) {
20
+ throw new Error(`${field} 含非法 handle "${handle}":只允许字母、数字、下划线(不含 @)`);
21
+ }
22
+ }
23
+ return stripped;
24
+ }
25
+ /** 日期形参校验:只验形状(YYYY-MM-DD),语义由 Kenari 侧把关。 */
26
+ function assertDate(value, field) {
27
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) {
28
+ throw new Error(`${field} 必须是 YYYY-MM-DD 格式,收到 "${value}"`);
29
+ }
30
+ }
31
+ /** 注册 kenari_x_search。 */
32
+ export function registerXSearchTool(deps, register) {
33
+ register(defineTool({
34
+ name: 'kenari_x_search',
35
+ description: 'Search X (Twitter) live via Kenari and get a synthesized answer with x.com source links. Billed per search to your Kenari balance. Optional filters: allowed/excluded handles (mutually exclusive, max 20, no @), date range, image/video understanding.',
36
+ parameters: {
37
+ query: { type: 'string', required: true, description: 'The X search query. Must be non-empty.' },
38
+ allowed_x_handles: {
39
+ type: 'array',
40
+ items: { type: 'string' },
41
+ description: 'Only include posts from these handles. Cannot be combined with excluded_x_handles. Max 20, without @.',
42
+ },
43
+ excluded_x_handles: {
44
+ type: 'array',
45
+ items: { type: 'string' },
46
+ description: 'Exclude posts from these handles. Cannot be combined with allowed_x_handles. Max 20, without @.',
47
+ },
48
+ from_date: { type: 'string', description: 'Start date, YYYY-MM-DD.' },
49
+ to_date: { type: 'string', description: 'End date, YYYY-MM-DD.' },
50
+ enable_image_understanding: { type: 'boolean', description: 'Let the backend read images in posts.' },
51
+ enable_video_understanding: { type: 'boolean', description: 'Let the backend read videos in posts.' },
52
+ },
53
+ output: {
54
+ schema: {
55
+ type: 'object',
56
+ additionalProperties: false,
57
+ properties: {
58
+ answer: { type: 'string', required: true },
59
+ citations: {
60
+ type: 'array',
61
+ required: true,
62
+ items: {
63
+ type: 'object',
64
+ additionalProperties: false,
65
+ properties: {
66
+ url: { type: 'string' },
67
+ title: { type: 'string' },
68
+ },
69
+ },
70
+ },
71
+ cost_micro_idr: { type: 'integer' },
72
+ text: { type: 'string', required: true },
73
+ },
74
+ },
75
+ render: (_args, value) => [{ type: 'text', text: value.text }],
76
+ },
77
+ timeoutMs: 60_000,
78
+ isConcurrencySafe: () => true,
79
+ async execute(args, exec) {
80
+ spendGuard(deps, exec, 'kenari_x_search');
81
+ // 互斥与形状约束 schema 表达不了,参数级校验(硬约束:同传报参数错)
82
+ if (args.allowed_x_handles !== undefined && args.excluded_x_handles !== undefined) {
83
+ throw new Error('allowed_x_handles 与 excluded_x_handles 不可同时使用');
84
+ }
85
+ if (args.query.trim().length === 0) {
86
+ throw new Error('query 不能为空');
87
+ }
88
+ const filter = {};
89
+ if (args.allowed_x_handles !== undefined) {
90
+ filter.allowed_x_handles = normalizeHandles(args.allowed_x_handles, 'allowed_x_handles');
91
+ }
92
+ if (args.excluded_x_handles !== undefined) {
93
+ filter.excluded_x_handles = normalizeHandles(args.excluded_x_handles, 'excluded_x_handles');
94
+ }
95
+ if (args.from_date !== undefined) {
96
+ assertDate(args.from_date, 'from_date');
97
+ filter.from_date = args.from_date;
98
+ }
99
+ if (args.to_date !== undefined) {
100
+ assertDate(args.to_date, 'to_date');
101
+ filter.to_date = args.to_date;
102
+ }
103
+ if (args.enable_image_understanding !== undefined)
104
+ filter.enable_image_understanding = args.enable_image_understanding;
105
+ if (args.enable_video_understanding !== undefined)
106
+ filter.enable_video_understanding = args.enable_video_understanding;
107
+ const body = { query: args.query };
108
+ if (Object.keys(filter).length > 0)
109
+ body.x_search_filter = filter;
110
+ const result = await kenariPost(deps.http, '/x/search', body);
111
+ const citations = (result.citations ?? [])
112
+ .map((c) => `- [${c.title ?? c.url}](${c.url})`)
113
+ .join('\n');
114
+ const billed = await billSpend(deps, exec, {
115
+ tool: 'kenari_x_search',
116
+ costMicroIdr: result.cost_micro_idr,
117
+ note: '按次计费',
118
+ });
119
+ const text = `${result.answer ?? '(无答案)'}${citations.length > 0 ? `\n\n来源:\n${citations}` : ''}${billed}`;
120
+ return {
121
+ answer: result.answer ?? '',
122
+ citations: result.citations ?? [],
123
+ ...(result.cost_micro_idr !== undefined ? { cost_micro_idr: result.cost_micro_idr } : {}),
124
+ text,
125
+ };
126
+ },
127
+ }));
128
+ }
129
+ //# sourceMappingURL=x-search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"x-search.js","sourceRoot":"","sources":["../../src/tools/x-search.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * 组合 provider:Kenari 优先,调用失败后回退 dsh 默认 provider。
3
+ * web seam 无回退链(硬约束 6),回退只能在调用失败后发生;
4
+ * 兜底实例只实例化不注册(硬约束 3)。
5
+ * @module dsh-kenari-plugin/web/fallback
6
+ */
7
+ import { DeepSeekSearchProvider } from '@deepseek-ai/dsh-web-search-deepseek';
8
+ import { HttpFetchProvider } from '@deepseek-ai/dsh-web-fetch-http';
9
+ import type { WebFetchProvider, WebFetchRequest, WebFetchResult, WebSearchProvider, WebSearchRequest, WebSearchResult } from '@deepseek-ai/dsh-web';
10
+ /** 回退日志的最小形状(ctx.logger 满足)。 */
11
+ export interface FallbackLogger {
12
+ warn(msg: string): void;
13
+ info(msg: string): void;
14
+ }
15
+ /**
16
+ * Kenari 优先搜索。Kenari available() 恒真(key 缺失在调用时以 WebError 暴露,
17
+ * 因为 available() 禁止网络请求),所以选择权在 seam,回退在本类内部完成。
18
+ */
19
+ export declare class KenariFirstSearch implements WebSearchProvider {
20
+ private readonly kenari;
21
+ private readonly official;
22
+ private readonly logger?;
23
+ readonly id = "kenari-fallback";
24
+ constructor(kenari: WebSearchProvider, official: DeepSeekSearchProvider, logger?: FallbackLogger | undefined);
25
+ /** Kenari 或官方任一可用即选择本组合 provider。 */
26
+ available(): boolean;
27
+ search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchResult>;
28
+ }
29
+ /** Kenari 优先抓取,回退到本地匿名 HTTP provider。 */
30
+ export declare class KenariFirstFetch implements WebFetchProvider {
31
+ private readonly kenari;
32
+ private readonly local;
33
+ private readonly logger?;
34
+ readonly id = "kenari-fallback";
35
+ constructor(kenari: WebFetchProvider, local: HttpFetchProvider, logger?: FallbackLogger | undefined);
36
+ available(): boolean;
37
+ fetch(request: WebFetchRequest, signal?: AbortSignal): Promise<WebFetchResult>;
38
+ }
39
+ //# sourceMappingURL=fallback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fallback.d.ts","sourceRoot":"","sources":["../../src/web/fallback.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAA;AAC7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAA;AACnE,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EAChB,MAAM,sBAAsB,CAAA;AAE7B,gCAAgC;AAChC,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB;AAaD;;;GAGG;AACH,qBAAa,iBAAkB,YAAW,iBAAiB;IAIvD,OAAO,CAAC,QAAQ,CAAC,MAAM;IAEvB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IAN1B,QAAQ,CAAC,EAAE,qBAAoB;gBAGZ,MAAM,EAAE,iBAAiB,EAEzB,QAAQ,EAAE,sBAAsB,EAChC,MAAM,CAAC,EAAE,cAAc,YAAA;IAG1C,qCAAqC;IACrC,SAAS,IAAI,OAAO;IAId,MAAM,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC;CAaxF;AAED,yCAAyC;AACzC,qBAAa,gBAAiB,YAAW,gBAAgB;IAIrD,OAAO,CAAC,QAAQ,CAAC,MAAM;IAEvB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IAN1B,QAAQ,CAAC,EAAE,qBAAoB;gBAGZ,MAAM,EAAE,gBAAgB,EAExB,KAAK,EAAE,iBAAiB,EACxB,MAAM,CAAC,EAAE,cAAc,YAAA;IAG1C,SAAS,IAAI,OAAO;IAKd,KAAK,CAAC,OAAO,EAAE,eAAe,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC;CAarF"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * 组合 provider:Kenari 优先,调用失败后回退 dsh 默认 provider。
3
+ * web seam 无回退链(硬约束 6),回退只能在调用失败后发生;
4
+ * 兜底实例只实例化不注册(硬约束 3)。
5
+ * @module dsh-kenari-plugin/web/fallback
6
+ */
7
+ import { DeepSeekSearchProvider } from '@deepseek-ai/dsh-web-search-deepseek';
8
+ import { HttpFetchProvider } from '@deepseek-ai/dsh-web-fetch-http';
9
+ function logFallback(logger, outcome) {
10
+ logger?.warn(`kenari-fallback: ${outcome.direction} 失败后回退(${outcome.reason},${outcome.elapsedMs}ms)`);
11
+ }
12
+ /**
13
+ * Kenari 优先搜索。Kenari available() 恒真(key 缺失在调用时以 WebError 暴露,
14
+ * 因为 available() 禁止网络请求),所以选择权在 seam,回退在本类内部完成。
15
+ */
16
+ export class KenariFirstSearch {
17
+ kenari;
18
+ official;
19
+ logger;
20
+ id = 'kenari-fallback';
21
+ constructor(kenari,
22
+ // 兜底实例:import 自 dsh 包入口,只实例化不注册(id 冲突会抛 WEB_DUPLICATE_PROVIDER)
23
+ official, logger) {
24
+ this.kenari = kenari;
25
+ this.official = official;
26
+ this.logger = logger;
27
+ }
28
+ /** Kenari 或官方任一可用即选择本组合 provider。 */
29
+ available() {
30
+ return this.kenari.available() || this.official.available();
31
+ }
32
+ async search(request, signal) {
33
+ const start = Date.now();
34
+ try {
35
+ return await this.kenari.search(request, signal);
36
+ }
37
+ catch (err) {
38
+ logFallback(this.logger, {
39
+ direction: `search "${request.query}"`,
40
+ reason: String(err),
41
+ elapsedMs: Date.now() - start,
42
+ });
43
+ return this.official.search(request, signal);
44
+ }
45
+ }
46
+ }
47
+ /** Kenari 优先抓取,回退到本地匿名 HTTP provider。 */
48
+ export class KenariFirstFetch {
49
+ kenari;
50
+ local;
51
+ logger;
52
+ id = 'kenari-fallback';
53
+ constructor(kenari,
54
+ // 同上:只实例化不注册
55
+ local, logger) {
56
+ this.kenari = kenari;
57
+ this.local = local;
58
+ this.logger = logger;
59
+ }
60
+ available() {
61
+ // 本地 fetch provider 无凭据语义,available() 恒真
62
+ return this.kenari.available() || this.local.available();
63
+ }
64
+ async fetch(request, signal) {
65
+ const start = Date.now();
66
+ try {
67
+ return await this.kenari.fetch(request, signal);
68
+ }
69
+ catch (err) {
70
+ logFallback(this.logger, {
71
+ direction: `fetch "${request.url}"`,
72
+ reason: String(err),
73
+ elapsedMs: Date.now() - start,
74
+ });
75
+ return this.local.fetch(request, signal);
76
+ }
77
+ }
78
+ }
79
+ //# sourceMappingURL=fallback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fallback.js","sourceRoot":"","sources":["../../src/web/fallback.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAA;AAC7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAA;AAuBnE,SAAS,WAAW,CAAC,MAAkC,EAAE,OAAwB;IAC/E,MAAM,EAAE,IAAI,CAAC,oBAAoB,OAAO,CAAC,SAAS,UAAU,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,SAAS,KAAK,CAAC,CAAA;AACvG,CAAC;AAED;;;GAGG;AACH,MAAM,OAAO,iBAAiB;IAIT;IAEA;IACA;IANV,EAAE,GAAG,iBAAiB,CAAA;IAE/B,YACmB,MAAyB;IAC1C,gEAAgE;IAC/C,QAAgC,EAChC,MAAuB;QAHvB,WAAM,GAAN,MAAM,CAAmB;QAEzB,aAAQ,GAAR,QAAQ,CAAwB;QAChC,WAAM,GAAN,MAAM,CAAiB;IACvC,CAAC;IAEJ,qCAAqC;IACrC,SAAS;QACP,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAA;IAC7D,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,OAAyB,EAAE,MAAoB;QAC1D,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACxB,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;QAClD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE;gBACvB,SAAS,EAAE,WAAW,OAAO,CAAC,KAAK,GAAG;gBACtC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC;gBACnB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;aAC9B,CAAC,CAAA;YACF,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;QAC9C,CAAC;IACH,CAAC;CACF;AAED,yCAAyC;AACzC,MAAM,OAAO,gBAAgB;IAIR;IAEA;IACA;IANV,EAAE,GAAG,iBAAiB,CAAA;IAE/B,YACmB,MAAwB;IACzC,aAAa;IACI,KAAwB,EACxB,MAAuB;QAHvB,WAAM,GAAN,MAAM,CAAkB;QAExB,UAAK,GAAL,KAAK,CAAmB;QACxB,WAAM,GAAN,MAAM,CAAiB;IACvC,CAAC;IAEJ,SAAS;QACP,yCAAyC;QACzC,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAA;IAC1D,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,OAAwB,EAAE,MAAoB;QACxD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACxB,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;QACjD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE;gBACvB,SAAS,EAAE,UAAU,OAAO,CAAC,GAAG,GAAG;gBACnC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC;gBACnB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;aAC9B,CAAC,CAAA;YACF,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;QAC1C,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Kenari web 抓取 provider:`POST /v1/web/fetch` 映射为 seam 的 WebFetchProvider。
3
+ * Kenari 返回纯文本正文;seam 的非 2xx 是结果不是错误,这里恒报 200(Kenari 已代抓)。
4
+ * @module dsh-kenari-plugin/web/fetch
5
+ */
6
+ import type { WebFetchProvider, WebFetchRequest, WebFetchResult } from '@deepseek-ai/dsh-web';
7
+ import type { KenariHttpDeps } from '../http.js';
8
+ import type { RecordWebSpend } from './search.js';
9
+ export declare const KENARI_FETCH_PROVIDER_ID = "kenari-fallback";
10
+ export declare class KenariFetchProvider implements WebFetchProvider {
11
+ private readonly deps;
12
+ private readonly recordSpend?;
13
+ readonly id = "kenari-fallback";
14
+ constructor(deps: KenariHttpDeps, recordSpend?: RecordWebSpend | undefined);
15
+ /** key 现场解析,本地判定可用性;不发网络请求。 */
16
+ available(): boolean;
17
+ fetch(request: WebFetchRequest, signal?: AbortSignal): Promise<WebFetchResult>;
18
+ }
19
+ //# sourceMappingURL=fetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/web/fetch.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC7F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEjD,eAAO,MAAM,wBAAwB,oBAAoB,CAAA;AAKzD,qBAAa,mBAAoB,YAAW,gBAAgB;IAIxD,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;IAJ/B,QAAQ,CAAC,EAAE,qBAA2B;gBAGnB,IAAI,EAAE,cAAc,EACpB,WAAW,CAAC,EAAE,cAAc,YAAA;IAG/C,+BAA+B;IAC/B,SAAS,IAAI,OAAO;IAId,KAAK,CAAC,OAAO,EAAE,eAAe,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC;CAsBrF"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Kenari web 抓取 provider:`POST /v1/web/fetch` 映射为 seam 的 WebFetchProvider。
3
+ * Kenari 返回纯文本正文;seam 的非 2xx 是结果不是错误,这里恒报 200(Kenari 已代抓)。
4
+ * @module dsh-kenari-plugin/web/fetch
5
+ */
6
+ import { kenariPost } from '../http.js';
7
+ export const KENARI_FETCH_PROVIDER_ID = 'kenari-fallback';
8
+ /** fetch 正文尾部最多附带的链接数,防 links[] 过长污染上下文。 */
9
+ const MAX_APPENDED_LINKS = 20;
10
+ export class KenariFetchProvider {
11
+ deps;
12
+ recordSpend;
13
+ id = KENARI_FETCH_PROVIDER_ID;
14
+ constructor(deps, recordSpend) {
15
+ this.deps = deps;
16
+ this.recordSpend = recordSpend;
17
+ }
18
+ /** key 现场解析,本地判定可用性;不发网络请求。 */
19
+ available() {
20
+ return true;
21
+ }
22
+ async fetch(request, signal) {
23
+ const response = await kenariPost(this.deps, '/web/fetch', { url: request.url }, signal);
24
+ if (typeof response.cost_micro_idr === 'number')
25
+ this.recordSpend?.(response.cost_micro_idr, 'fetch');
26
+ const links = response.links ?? [];
27
+ const bodyParts = [response.content ?? ''];
28
+ if (links.length > 0) {
29
+ // links[] 附正文尾部(任务包要求),空行分隔便于 consumer 展示
30
+ bodyParts.push('', links.slice(0, MAX_APPENDED_LINKS).join('\n'));
31
+ }
32
+ return {
33
+ url: request.url,
34
+ // Kenari 代抓正文成功即视为 200;目标站错误已在 Kenari 侧消化为正文内容
35
+ statusCode: 200,
36
+ body: { kind: 'text', content: bodyParts.join('\n') },
37
+ truncated: false,
38
+ };
39
+ }
40
+ }
41
+ //# sourceMappingURL=fetch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch.js","sourceRoot":"","sources":["../../src/web/fetch.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAAE,UAAU,EAA4B,MAAM,YAAY,CAAA;AAGjE,MAAM,CAAC,MAAM,wBAAwB,GAAG,iBAAiB,CAAA;AAEzD,4CAA4C;AAC5C,MAAM,kBAAkB,GAAG,EAAE,CAAA;AAE7B,MAAM,OAAO,mBAAmB;IAIX;IACA;IAJV,EAAE,GAAG,wBAAwB,CAAA;IAEtC,YACmB,IAAoB,EACpB,WAA4B;QAD5B,SAAI,GAAJ,IAAI,CAAgB;QACpB,gBAAW,GAAX,WAAW,CAAiB;IAC5C,CAAC;IAEJ,+BAA+B;IAC/B,SAAS;QACP,OAAO,IAAI,CAAA;IACb,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,OAAwB,EAAE,MAAoB;QACxD,MAAM,QAAQ,GAAG,MAAM,UAAU,CAC/B,IAAI,CAAC,IAAI,EACT,YAAY,EACZ,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,EACpB,MAAM,CACP,CAAA;QACD,IAAI,OAAO,QAAQ,CAAC,cAAc,KAAK,QAAQ;YAAE,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC,CAAA;QACrG,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,IAAI,EAAE,CAAA;QAClC,MAAM,SAAS,GAAG,CAAC,QAAQ,CAAC,OAAO,IAAI,EAAE,CAAC,CAAA;QAC1C,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrB,0CAA0C;YAC1C,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,kBAAkB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;QACnE,CAAC;QACD,OAAO;YACL,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,+CAA+C;YAC/C,UAAU,EAAE,GAAG;YACf,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;YACrD,SAAS,EAAE,KAAK;SACjB,CAAA;IACH,CAAC;CACF"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Kenari web 搜索 provider:`POST /v1/web/search` 映射为 seam 的 WebSearchProvider。
3
+ * available() 只做本地检查(硬约束 5);字段映射集中在文件顶部便于跟进 rc 变动。
4
+ * @module dsh-kenari-plugin/web/search
5
+ */
6
+ import type { WebSearchProvider, WebSearchRequest, WebSearchResult } from '@deepseek-ai/dsh-web';
7
+ import type { KenariHttpDeps } from '../http.js';
8
+ /** seam 词汇 → Kenari 请求的映射要点(对照知识库 kenari-api.md)。 */
9
+ export declare const KENARI_SEARCH_PROVIDER_ID = "kenari-fallback";
10
+ /** 计费回调:web 搜索按次扣费,记进共享账本(无会话归属,落 global 作用域)。 */
11
+ export type RecordWebSpend = (microIdr: number, kind: 'search' | 'fetch') => void;
12
+ export declare class KenariSearchProvider implements WebSearchProvider {
13
+ private readonly deps;
14
+ private readonly recordSpend?;
15
+ readonly id = "kenari-fallback";
16
+ constructor(deps: KenariHttpDeps, recordSpend?: RecordWebSpend | undefined);
17
+ /** key 现场解析,本地判定可用性;不发网络请求。 */
18
+ available(): boolean;
19
+ search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchResult>;
20
+ }
21
+ //# sourceMappingURL=search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/web/search.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,eAAe,EAAmB,MAAM,sBAAsB,CAAA;AACjH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAOhD,qDAAqD;AACrD,eAAO,MAAM,yBAAyB,oBAAoB,CAAA;AAE1D,kDAAkD;AAClD,MAAM,MAAM,cAAc,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,KAAK,IAAI,CAAA;AAEjF,qBAAa,oBAAqB,YAAW,iBAAiB;IAI1D,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;IAJ/B,QAAQ,CAAC,EAAE,qBAA4B;gBAGpB,IAAI,EAAE,cAAc,EACpB,WAAW,CAAC,EAAE,cAAc,YAAA;IAG/C,+BAA+B;IAC/B,SAAS,IAAI,OAAO;IAId,MAAM,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC;CAyBxF"}