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,280 @@
1
+ /**
2
+ * 会话标题前缀:`yyyyMMddHHmmss-`(模板可自定义,可开关)。
3
+ *
4
+ * dsh 的标题是 log-only 的 `session/title` 事件,事件数据固定为
5
+ * `{ title, messageSeqs, source }` —— 没有独立的前缀字段,前缀只能是 title
6
+ * 字符串的一部分。所以本模块在服务端事件层统一改写:剥掉旧前缀、按本会话的
7
+ * 开始时间(本机时区)重算,再 append 一条同源事件;标题取最后一条,因此 Web、
8
+ * TUI、headless 读到的是同一个字符串。
9
+ *
10
+ * 开始时间取「本会话第一条人类 `user/message` 的时间」,而不是
11
+ * `session.header.createdAt`:dsh 的 Web 侧会**复用空白会话**,记录可能在工作区
12
+ * 打开时就建好,比真正开口说话早几个小时,用 createdAt 会写出对不上的时间。
13
+ * 没有任何人类消息时(例如对空会话手动改名)才退回 `header.createdAt`;fork
14
+ * 子会话跳过 seed 继承的事件,所以拿到的是子会话自己的第一条消息。
15
+ *
16
+ * 不注册 provider、不改 dsh 行:监听器挂在 bundle 插件 ctx 上(未打 scope 标签的
17
+ * 上下文全局收得到会话事件),只在标题事件与 fork 创建时动手。自动标题
18
+ * (provider / fallback)与手动 rename 一视同仁 —— 浏览器 fork 之后会用一次
19
+ * `source.kind === 'user'` 的 rename 把父标题带进子会话,按 source 区分会在 fork
20
+ * 上漏改前缀。
21
+ *
22
+ * @module dsh-kenari-plugin/session-title
23
+ */
24
+ /** 模板里会被替换成时间的 token,按同一趟扫描替换。 */
25
+ const TOKEN_PATTERN = /yyyy|MM|dd|HH|mm|ss/g;
26
+ /** 两位数补零。 */
27
+ function pad2(value) {
28
+ return String(value).padStart(2, '0');
29
+ }
30
+ /**
31
+ * 按会话创建时间渲染前缀模板。
32
+ * @param template - 前缀模板。
33
+ * @param createdAt - 会话创建时刻(epoch 毫秒)。
34
+ * @returns 渲染后的前缀;不含 token 的模板即模板本身。
35
+ */
36
+ export function renderTitlePrefix(template, createdAt) {
37
+ const date = new Date(createdAt);
38
+ return template.replace(TOKEN_PATTERN, (token) => {
39
+ switch (token) {
40
+ case 'yyyy': return String(date.getFullYear()).padStart(4, '0');
41
+ case 'MM': return pad2(date.getMonth() + 1);
42
+ case 'dd': return pad2(date.getDate());
43
+ case 'HH': return pad2(date.getHours());
44
+ case 'mm': return pad2(date.getMinutes());
45
+ case 'ss': return pad2(date.getSeconds());
46
+ /* v8 ignore next -- 正则已是闭集 */
47
+ default: return token;
48
+ }
49
+ });
50
+ }
51
+ /** UTF-8 安全截断,不切开码点。 */
52
+ function truncateUtf8(input, maxBytes) {
53
+ if (maxBytes <= 0)
54
+ return '';
55
+ if (Buffer.byteLength(input, 'utf8') <= maxBytes)
56
+ return input;
57
+ let used = 0;
58
+ let output = '';
59
+ for (const character of input) {
60
+ const bytes = Buffer.byteLength(character, 'utf8');
61
+ if (used + bytes > maxBytes)
62
+ break;
63
+ output += character;
64
+ used += bytes;
65
+ }
66
+ return output;
67
+ }
68
+ /** 判断事件里的 source 是否是 dsh 认识的三种形状之一。 */
69
+ function isTitleSource(value) {
70
+ if (value === null || typeof value !== 'object')
71
+ return false;
72
+ const kind = value.kind;
73
+ return kind === 'fallback' || kind === 'user' || kind === 'provider';
74
+ }
75
+ /** 判断一条 `user/message` 是不是人类发的(而非系统/插件/技能目录注入)。 */
76
+ function isHumanUserMessage(data) {
77
+ if (data === null || typeof data !== 'object')
78
+ return false;
79
+ const source = data.source;
80
+ if (source === null || typeof source !== 'object')
81
+ return false;
82
+ return source.kind === 'user';
83
+ }
84
+ /**
85
+ * 会话的开始时间:本会话第一条人类 `user/message` 的时间。
86
+ *
87
+ * 不用 `header.createdAt` —— dsh 会复用空白会话,记录可能在工作区打开时就建好,
88
+ * 比真正开口早几个小时。fork 子会话跳过 seed 继承的事件(`inheritedEventCount`),
89
+ * 所以拿到的是子会话自己的第一条消息;没有人类消息时退回 `header.createdAt`。
90
+ */
91
+ function anchorTimeOf(session) {
92
+ const inherited = typeof session.inheritedEventCount === 'number' ? session.inheritedEventCount : 0;
93
+ for (const event of session.snapshotEvents()) {
94
+ if (event.seq < inherited)
95
+ continue;
96
+ if (event.type !== 'user/message' || !isHumanUserMessage(event.data))
97
+ continue;
98
+ if (typeof event.time === 'number' && Number.isFinite(event.time))
99
+ return event.time;
100
+ }
101
+ return session.header.createdAt;
102
+ }
103
+ /** 读会话日志里最新一条标题事件的持久化数据。 */
104
+ function latestTitle(session) {
105
+ const events = session.snapshotEvents();
106
+ for (let index = events.length - 1; index >= 0; index -= 1) {
107
+ const event = events[index];
108
+ if (event === undefined || event.type !== 'session/title')
109
+ continue;
110
+ const data = event.data;
111
+ if (data === null || typeof data !== 'object')
112
+ return undefined;
113
+ const candidate = data;
114
+ if (typeof candidate.title !== 'string' || !isTitleSource(candidate.source))
115
+ return undefined;
116
+ return {
117
+ title: candidate.title,
118
+ messageSeqs: Array.isArray(candidate.messageSeqs) ? candidate.messageSeqs : [],
119
+ source: candidate.source,
120
+ };
121
+ }
122
+ return undefined;
123
+ }
124
+ /** 剥掉标题开头的一个已知前缀。候选按长度降序试,避免短前缀截断长前缀。 */
125
+ function stripKnownPrefix(title, candidates) {
126
+ for (const candidate of [...candidates].sort((left, right) => right.length - left.length)) {
127
+ if (candidate.length > 0 && title.startsWith(candidate))
128
+ return title.slice(candidate.length);
129
+ }
130
+ return title;
131
+ }
132
+ /**
133
+ * 写入设置页时的本地校验:schema 表达不了的约束在这里拦下。
134
+ * @param options - 当前配置值。
135
+ * @throws 模板为空/渲染为空/含控制字符/过长,或字节预算容不下前缀加最短正文时。
136
+ */
137
+ export function validateSessionTitlePrefix(options) {
138
+ if (!Number.isInteger(options.maxBytes) || options.maxBytes <= 0) {
139
+ throw new Error('sessionTitleMaxBytes 必须是正整数');
140
+ }
141
+ if (/[\u0000-\u001F\u007F]/u.test(options.template)) {
142
+ throw new Error('sessionTitlePrefix 不能包含控制字符或换行');
143
+ }
144
+ if (Buffer.byteLength(options.template, 'utf8') > 48) {
145
+ throw new Error('sessionTitlePrefix 过长:模板上限 48 字节');
146
+ }
147
+ if (!options.enabled)
148
+ return;
149
+ if (options.template.length === 0) {
150
+ throw new Error('sessionTitlePrefix 不能为空:前缀开关开启时必须给出模板');
151
+ }
152
+ const rendered = renderTitlePrefix(options.template, Date.now());
153
+ if (rendered.length === 0) {
154
+ throw new Error('sessionTitlePrefix 渲染结果为空,无法作为标题前缀');
155
+ }
156
+ // 前缀之外至少留 8 字节给正文,否则标题会被前缀吃光
157
+ if (Buffer.byteLength(rendered, 'utf8') + 8 > options.maxBytes) {
158
+ throw new Error('sessionTitleMaxBytes 太小:前缀之外至少要给标题留 8 字节');
159
+ }
160
+ }
161
+ /**
162
+ * 在服务端标题事件上维护前缀。
163
+ *
164
+ * 每次标题事件与每次 fork 创建后:剥掉标题开头的一个已知前缀(本会话上次写入的、
165
+ * 父会话的、以及历史版本按记录创建时间写下的),开关开启时再拼上按本会话开始时间
166
+ * 渲染的前缀,然后 append 一条同源事件。同一标题重复触发时结果与当前一致,不会
167
+ * 追加事件。
168
+ *
169
+ * @param ctx - bundle 插件上下文;监听器随 fiber 卸载自动回收。
170
+ * @param options - 前缀开关、模板与字节预算。
171
+ */
172
+ export function installSessionTitlePrefix(ctx, options) {
173
+ /** 本进程内每个会话最后一次写入的前缀,供 fork 子会话剥离父前缀。 */
174
+ const appliedPrefix = new Map();
175
+ const sessionsOf = () => ctx.get('sessions');
176
+ const parentOf = (session) => {
177
+ const parentId = session.header.parentSession;
178
+ if (parentId === undefined)
179
+ return undefined;
180
+ return sessionsOf()?.get(parentId);
181
+ };
182
+ /**
183
+ * 按当前标题算出应写入的标题。
184
+ *
185
+ * 自定义模板下旧前缀不能用固定正则识别,只能按模板 + 对应时间精确重放:
186
+ * 本会话开始时间的(正常路径)、本会话记录创建时间的(修历史版本按 createdAt
187
+ * 写下的前缀)、父会话这两种时间的(覆盖 fork)、以及进程内记下的实际写入值
188
+ * (覆盖模板改过之后仍在本进程内的会话)。
189
+ */
190
+ const desiredTitleOf = (session, current) => {
191
+ const template = options.template();
192
+ const ownPrefix = renderTitlePrefix(template, anchorTimeOf(session));
193
+ const candidates = [ownPrefix, renderTitlePrefix(template, session.header.createdAt)];
194
+ const knownOwn = appliedPrefix.get(session.id);
195
+ if (knownOwn !== undefined)
196
+ candidates.push(knownOwn);
197
+ const parent = parentOf(session);
198
+ if (parent !== undefined) {
199
+ const knownParent = appliedPrefix.get(parent.id);
200
+ if (knownParent !== undefined)
201
+ candidates.push(knownParent);
202
+ candidates.push(renderTitlePrefix(template, anchorTimeOf(parent)));
203
+ candidates.push(renderTitlePrefix(template, parent.header.createdAt));
204
+ }
205
+ const body = stripKnownPrefix(current, candidates);
206
+ if (!options.enabled() || ownPrefix.length === 0)
207
+ return body;
208
+ const budget = options.maxBytes() - Buffer.byteLength(ownPrefix, 'utf8');
209
+ const truncated = truncateUtf8(body, Math.max(budget, 0));
210
+ return truncated.length === 0 ? ownPrefix : ownPrefix + truncated;
211
+ };
212
+ /** 重写一条已知会话的标题。调用方保证在 append 发布窗口之外。 */
213
+ const rewrite = (session) => {
214
+ // 会话可能已从 store 卸下;此时 append 不会进持久化,直接放弃
215
+ if (sessionsOf()?.get(session.id) !== session)
216
+ return;
217
+ const latest = latestTitle(session);
218
+ if (latest === undefined)
219
+ return;
220
+ const desired = desiredTitleOf(session, latest.title);
221
+ if (desired.length === 0 || desired === latest.title)
222
+ return;
223
+ if (options.enabled()) {
224
+ const ownPrefix = renderTitlePrefix(options.template(), anchorTimeOf(session));
225
+ if (ownPrefix.length > 0)
226
+ appliedPrefix.set(session.id, ownPrefix);
227
+ }
228
+ session.append('session/title', {
229
+ title: desired,
230
+ messageSeqs: [...latest.messageSeqs],
231
+ source: latest.source,
232
+ });
233
+ };
234
+ /**
235
+ * 标题事件发生在 append 的发布窗口内,同步再 append 会被 dsh 的 reentrancy
236
+ * 检查拒绝。defer 一拍,并在执行时重读最新标题:更新的标题已经落地时,
237
+ * 这次改写自然退化为幂等空操作,不会覆盖它。
238
+ */
239
+ const scheduleRewrite = (session) => {
240
+ queueMicrotask(() => {
241
+ try {
242
+ rewrite(session);
243
+ }
244
+ catch (error) {
245
+ ctx.logger.warn(`kenari: session title prefix update failed: ${String(error)}`);
246
+ }
247
+ });
248
+ };
249
+ ctx.on('session/event', (session, event) => {
250
+ if (event.type !== 'session/title')
251
+ return;
252
+ scheduleRewrite(session);
253
+ });
254
+ ctx.on('session/created', (session) => {
255
+ const target = session;
256
+ // fork 子会话继承父标题(带父会话时间戳),按子会话自己的开始时间重写
257
+ if (target.header.parentSession !== undefined) {
258
+ scheduleRewrite(target);
259
+ return;
260
+ }
261
+ // 非 fork:只修历史版本按「记录创建时间」写下的前缀。旧版把空白会话的
262
+ // createdAt(可能比真正开口早几小时)当前缀;这里仅在存储里的前缀正好等于
263
+ // createdAt 的全渲染值、而开始时间不同时才改写,所以既修对了又不会在用户
264
+ // 改开关/改模板时回溯已有标题。
265
+ const latest = latestTitle(target);
266
+ if (latest === undefined)
267
+ return;
268
+ const anchor = anchorTimeOf(target);
269
+ if (anchor === target.header.createdAt)
270
+ return;
271
+ const legacy = renderTitlePrefix(options.template(), target.header.createdAt);
272
+ if (legacy.length === 0 || !latest.title.startsWith(legacy))
273
+ return;
274
+ scheduleRewrite(target);
275
+ });
276
+ ctx.on('session/disposed', (session) => {
277
+ appliedPrefix.delete(session.id);
278
+ });
279
+ }
280
+ //# sourceMappingURL=session-title.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-title.js","sourceRoot":"","sources":["../src/session-title.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAsDH,kCAAkC;AAClC,MAAM,aAAa,GAAG,sBAAsB,CAAA;AAE5C,aAAa;AACb,SAAS,IAAI,CAAC,KAAa;IACzB,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;AACvC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAgB,EAAE,SAAiB;IACnE,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,CAAA;IAChC,OAAO,QAAQ,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE;QAC/C,QAAQ,KAAK,EAAE,CAAC;YACd,KAAK,MAAM,CAAC,CAAC,OAAO,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;YAC/D,KAAK,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAA;YAC3C,KAAK,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAA;YACtC,KAAK,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;YACvC,KAAK,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAA;YACzC,KAAK,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAA;YACzC,8BAA8B;YAC9B,OAAO,CAAC,CAAC,OAAO,KAAK,CAAA;QACvB,CAAC;IACH,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,wBAAwB;AACxB,SAAS,YAAY,CAAC,KAAa,EAAE,QAAgB;IACnD,IAAI,QAAQ,IAAI,CAAC;QAAE,OAAO,EAAE,CAAA;IAC5B,IAAI,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,QAAQ;QAAE,OAAO,KAAK,CAAA;IAC9D,IAAI,IAAI,GAAG,CAAC,CAAA;IACZ,IAAI,MAAM,GAAG,EAAE,CAAA;IACf,KAAK,MAAM,SAAS,IAAI,KAAK,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,SAAS,EAAE,MAAM,CAAC,CAAA;QAClD,IAAI,IAAI,GAAG,KAAK,GAAG,QAAQ;YAAE,MAAK;QAClC,MAAM,IAAI,SAAS,CAAA;QACnB,IAAI,IAAI,KAAK,CAAA;IACf,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED,uCAAuC;AACvC,SAAS,aAAa,CAAC,KAAc;IACnC,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAA;IAC7D,MAAM,IAAI,GAAI,KAA4B,CAAC,IAAI,CAAA;IAC/C,OAAO,IAAI,KAAK,UAAU,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,UAAU,CAAA;AACtE,CAAC;AAED,mDAAmD;AACnD,SAAS,kBAAkB,CAAC,IAAa;IACvC,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAA;IAC3D,MAAM,MAAM,GAAI,IAA6B,CAAC,MAAM,CAAA;IACpD,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAA;IAC/D,OAAQ,MAA6B,CAAC,IAAI,KAAK,MAAM,CAAA;AACvD,CAAC;AAED;;;;;;GAMG;AACH,SAAS,YAAY,CAAC,OAAqB;IACzC,MAAM,SAAS,GAAG,OAAO,OAAO,CAAC,mBAAmB,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAA;IACnG,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC;QAC7C,IAAI,KAAK,CAAC,GAAG,GAAG,SAAS;YAAE,SAAQ;QACnC,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,IAAI,CAAC;YAAE,SAAQ;QAC9E,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;YAAE,OAAO,KAAK,CAAC,IAAI,CAAA;IACtF,CAAC;IACD,OAAO,OAAO,CAAC,MAAM,CAAC,SAAS,CAAA;AACjC,CAAC;AAED,4BAA4B;AAC5B,SAAS,WAAW,CAAC,OAAqB;IACxC,MAAM,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAA;IACvC,KAAK,IAAI,KAAK,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAC3D,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAA;QAC3B,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,KAAK,eAAe;YAAE,SAAQ;QACnE,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAA;QACvB,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAA;QAC/D,MAAM,SAAS,GAAG,IAA+B,CAAA;QACjD,IAAI,OAAO,SAAS,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,MAAM,CAAC;YAAE,OAAO,SAAS,CAAA;QAC7F,OAAO;YACL,KAAK,EAAE,SAAS,CAAC,KAAK;YACtB,WAAW,EAAE,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,WAAuB,CAAC,CAAC,CAAC,EAAE;YAC1F,MAAM,EAAE,SAAS,CAAC,MAAM;SACzB,CAAA;IACH,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,yCAAyC;AACzC,SAAS,gBAAgB,CAAC,KAAa,EAAE,UAA6B;IACpE,KAAK,MAAM,SAAS,IAAI,CAAC,GAAG,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1F,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC;YAAE,OAAO,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;IAC/F,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CAAC,OAI1C;IACC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,QAAQ,IAAI,CAAC,EAAE,CAAC;QACjE,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAA;IAChD,CAAC;IACD,IAAI,wBAAwB,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAA;IACnD,CAAC;IACD,IAAI,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,GAAG,EAAE,EAAE,CAAC;QACrD,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAA;IACrD,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,OAAO;QAAE,OAAM;IAC5B,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAA;IAC1D,CAAC;IACD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;IAChE,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAA;IACvD,CAAC;IACD,6BAA6B;IAC7B,IAAI,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;QAC/D,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAA;IAC7D,CAAC;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,yBAAyB,CAAC,GAAY,EAAE,OAAkC;IACxF,yCAAyC;IACzC,MAAM,aAAa,GAAG,IAAI,GAAG,EAAkB,CAAA;IAE/C,MAAM,UAAU,GAAG,GAA6B,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,CAAwC,CAAA;IAE7G,MAAM,QAAQ,GAAG,CAAC,OAAqB,EAA4B,EAAE;QACnE,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,aAAa,CAAA;QAC7C,IAAI,QAAQ,KAAK,SAAS;YAAE,OAAO,SAAS,CAAA;QAC5C,OAAO,UAAU,EAAE,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAA;IACpC,CAAC,CAAA;IAED;;;;;;;OAOG;IACH,MAAM,cAAc,GAAG,CAAC,OAAqB,EAAE,OAAe,EAAU,EAAE;QACxE,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAA;QACnC,MAAM,SAAS,GAAG,iBAAiB,CAAC,QAAQ,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,CAAA;QACpE,MAAM,UAAU,GAAa,CAAC,SAAS,EAAE,iBAAiB,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAA;QAC/F,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QAC9C,IAAI,QAAQ,KAAK,SAAS;YAAE,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACrD,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAA;QAChC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,WAAW,GAAG,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;YAChD,IAAI,WAAW,KAAK,SAAS;gBAAE,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;YAC3D,UAAU,CAAC,IAAI,CAAC,iBAAiB,CAAC,QAAQ,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;YAClE,UAAU,CAAC,IAAI,CAAC,iBAAiB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAA;QACvE,CAAC;QACD,MAAM,IAAI,GAAG,gBAAgB,CAAC,OAAO,EAAE,UAAU,CAAC,CAAA;QAClD,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAA;QAC7D,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,GAAG,MAAM,CAAC,UAAU,CAAC,SAAS,EAAE,MAAM,CAAC,CAAA;QACxE,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAA;QACzD,OAAO,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,GAAG,SAAS,CAAA;IACnE,CAAC,CAAA;IAED,wCAAwC;IACxC,MAAM,OAAO,GAAG,CAAC,OAAqB,EAAQ,EAAE;QAC9C,wCAAwC;QACxC,IAAI,UAAU,EAAE,EAAE,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,OAAO;YAAE,OAAM;QACrD,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,CAAA;QACnC,IAAI,MAAM,KAAK,SAAS;YAAE,OAAM;QAChC,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAA;QACrD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,KAAK,MAAM,CAAC,KAAK;YAAE,OAAM;QAC5D,IAAI,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;YACtB,MAAM,SAAS,GAAG,iBAAiB,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,CAAA;YAC9E,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC;gBAAE,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,CAAC,CAAA;QACpE,CAAC;QACD,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE;YAC9B,KAAK,EAAE,OAAO;YACd,WAAW,EAAE,CAAC,GAAG,MAAM,CAAC,WAAW,CAAC;YACpC,MAAM,EAAE,MAAM,CAAC,MAAM;SACtB,CAAC,CAAA;IACJ,CAAC,CAAA;IAED;;;;OAIG;IACH,MAAM,eAAe,GAAG,CAAC,OAAqB,EAAQ,EAAE;QACtD,cAAc,CAAC,GAAG,EAAE;YAClB,IAAI,CAAC;gBACH,OAAO,CAAC,OAAO,CAAC,CAAA;YAClB,CAAC;YAAC,OAAO,KAAc,EAAE,CAAC;gBACxB,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,+CAA+C,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;YACjF,CAAC;QACH,CAAC,CAAC,CAAA;IACJ,CAAC,CAAA;IAED,GAAG,CAAC,EAAE,CAAC,eAAe,EAAE,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;QACzC,IAAK,KAA0B,CAAC,IAAI,KAAK,eAAe;YAAE,OAAM;QAChE,eAAe,CAAC,OAAkC,CAAC,CAAA;IACrD,CAAC,CAAC,CAAA;IAEF,GAAG,CAAC,EAAE,CAAC,iBAAiB,EAAE,CAAC,OAAO,EAAE,EAAE;QACpC,MAAM,MAAM,GAAG,OAAkC,CAAA;QACjD,uCAAuC;QACvC,IAAI,MAAM,CAAC,MAAM,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;YAC9C,eAAe,CAAC,MAAM,CAAC,CAAA;YACvB,OAAM;QACR,CAAC;QACD,uCAAuC;QACvC,2CAA2C;QAC3C,2CAA2C;QAC3C,kBAAkB;QAClB,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,CAAA;QAClC,IAAI,MAAM,KAAK,SAAS;YAAE,OAAM;QAChC,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAAA;QACnC,IAAI,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,SAAS;YAAE,OAAM;QAC9C,MAAM,MAAM,GAAG,iBAAiB,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;QAC7E,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,OAAM;QACnE,eAAe,CAAC,MAAM,CAAC,CAAA;IACzB,CAAC,CAAC,CAAA;IAEF,GAAG,CAAC,EAAE,CAAC,kBAAkB,EAAE,CAAC,OAAO,EAAE,EAAE;QACrC,aAAa,CAAC,MAAM,CAAE,OAAmC,CAAC,EAAE,CAAC,CAAA;IAC/D,CAAC,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * 设置节的 Host 半边:把插件 Config 注册成 dsh 设置命名空间(Settings 页可见、可写)。
3
+ *
4
+ * `installSection` 让本节的合成层(cordis patch 里的 config)成为 base,用户层
5
+ * 写 `$DSH_HOME/settings.yaml`;两者都经 schema 校验与 `validate` 把关。
6
+ * 改动落盘后 `setSource` 给出的 thunk 立即返回新值 —— 所以插件用 {@link LiveConfig}
7
+ * 的实时视图读取配置,下一次操作就生效,不需要重启。
8
+ *
9
+ * 结构性字段(是否需要注册 provider / 工具、预算封顶的账本实例)在插件加载时定型,
10
+ * 改动它们要重启 dsh;这一点在 README 与设置卡片里写明。
11
+ * @module dsh-kenari-plugin/settings
12
+ */
13
+ import type { Context } from '@deepseek-ai/cordis';
14
+ import type z from '@deepseek-ai/schemastery';
15
+ import type { Config } from './index.js';
16
+ /** 设置命名空间:必须匹配 `/^[a-z][a-z0-9-]*$/`。 */
17
+ export declare const KENARI_SETTINGS_NAMESPACE = "kenari";
18
+ /** 实时配置视图:`view` 每次属性读取都取当次权威值。 */
19
+ export interface LiveConfig {
20
+ /** 可当普通 Config 读的实时视图(值在每次读取时解析)。 */
21
+ readonly view: Config;
22
+ /** 取当次权威配置(诊断与需要完整对象时用)。 */
23
+ current(): Config;
24
+ }
25
+ /**
26
+ * schema 表达不了的约束在**写入时**拒绝(不是等到下次请求才炸)。
27
+ * 全部为本地判断:设置页写值时不该触发网络请求。
28
+ */
29
+ export declare function validateKenariConfig(value: Config): void;
30
+ /**
31
+ * 注册 Kenari 设置节并返回实时配置视图。
32
+ * 无 settings provider 挂载时只是退化为「配置即 composition 层」,插件照常工作。
33
+ */
34
+ export declare function installKenariSettings(ctx: Context, schema: z<Config>, config: Config): LiveConfig;
35
+ //# sourceMappingURL=settings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAGlD,OAAO,KAAK,CAAC,MAAM,0BAA0B,CAAA;AAC7C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAGxC,yCAAyC;AACzC,eAAO,MAAM,yBAAyB,WAAW,CAAA;AAEjD,mCAAmC;AACnC,MAAM,WAAW,UAAU;IACzB,qCAAqC;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,4BAA4B;IAC5B,OAAO,IAAI,MAAM,CAAA;CAClB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CA4BxD;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,CAyBjG"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * 设置节的 Host 半边:把插件 Config 注册成 dsh 设置命名空间(Settings 页可见、可写)。
3
+ *
4
+ * `installSection` 让本节的合成层(cordis patch 里的 config)成为 base,用户层
5
+ * 写 `$DSH_HOME/settings.yaml`;两者都经 schema 校验与 `validate` 把关。
6
+ * 改动落盘后 `setSource` 给出的 thunk 立即返回新值 —— 所以插件用 {@link LiveConfig}
7
+ * 的实时视图读取配置,下一次操作就生效,不需要重启。
8
+ *
9
+ * 结构性字段(是否需要注册 provider / 工具、预算封顶的账本实例)在插件加载时定型,
10
+ * 改动它们要重启 dsh;这一点在 README 与设置卡片里写明。
11
+ * @module dsh-kenari-plugin/settings
12
+ */
13
+ import { validateSessionTitlePrefix } from './session-title.js';
14
+ /** 设置命名空间:必须匹配 `/^[a-z][a-z0-9-]*$/`。 */
15
+ export const KENARI_SETTINGS_NAMESPACE = 'kenari';
16
+ /**
17
+ * schema 表达不了的约束在**写入时**拒绝(不是等到下次请求才炸)。
18
+ * 全部为本地判断:设置页写值时不该触发网络请求。
19
+ */
20
+ export function validateKenariConfig(value) {
21
+ const baseURL = value.baseURL ?? '';
22
+ if (!URL.canParse(baseURL)) {
23
+ throw new Error(`baseURL 必须是绝对 URL(例如 https://kenari.id),收到 "${baseURL}"`);
24
+ }
25
+ const providerId = value.nativeProviderId ?? '';
26
+ if (!/^[a-z][a-z0-9-]*$/.test(providerId)) {
27
+ throw new Error(`nativeProviderId 必须以小写字母开头且只含小写字母、数字、连字符,收到 "${providerId}"`);
28
+ }
29
+ for (const [alias, target] of Object.entries(value.modelAliases ?? {})) {
30
+ if (alias.trim().length === 0 || target.trim().length === 0) {
31
+ throw new Error('modelAliases 的别名与目标模型 id 都不能为空');
32
+ }
33
+ }
34
+ if ((value.balanceCacheTtlMs ?? 0) > (value.catalogCacheTtlMs ?? 0) * 10) {
35
+ throw new Error('balanceCacheTtlMs 不应比 catalogCacheTtlMs 大一个数量级:余额会长期陈旧');
36
+ }
37
+ // dsh 的 resolveRetryPolicy 会拒绝空的可重试码列表;在这里先拦,
38
+ // 免得用户写完设置页要等到下次加载插件才炸
39
+ if ((value.modelRetryableCodes ?? []).length === 0) {
40
+ throw new Error('modelRetryableCodes 不能为空:空列表会让重试策略无效');
41
+ }
42
+ // 前缀模板与字节预算:开关开启时模板必须渲染出非空前缀,且预算容得下前缀加正文
43
+ validateSessionTitlePrefix({
44
+ enabled: value.sessionTitlePrefixEnabled ?? true,
45
+ template: value.sessionTitlePrefix ?? 'yyyyMMddHHmmss-',
46
+ maxBytes: value.sessionTitleMaxBytes ?? 96,
47
+ });
48
+ }
49
+ /**
50
+ * 注册 Kenari 设置节并返回实时配置视图。
51
+ * 无 settings provider 挂载时只是退化为「配置即 composition 层」,插件照常工作。
52
+ */
53
+ export function installKenariSettings(ctx, schema, config) {
54
+ let current = () => config;
55
+ ctx.inject(['settings'], (settingsCtx) => {
56
+ settingsCtx.settings.installSection(ctx, KENARI_SETTINGS_NAMESPACE, schema, config, {
57
+ setSource: (source) => {
58
+ current = source;
59
+ },
60
+ // 注册期不缓存解析结果:消费方每次操作读 current(),故提交即生效
61
+ onChange: () => { },
62
+ validate: validateKenariConfig,
63
+ });
64
+ });
65
+ const view = new Proxy({}, {
66
+ get: (_target, key) => current()[key],
67
+ has: (_target, key) => key in current(),
68
+ ownKeys: () => Reflect.ownKeys(current()),
69
+ getOwnPropertyDescriptor: (_target, key) => ({
70
+ value: current()[key],
71
+ enumerable: true,
72
+ configurable: true,
73
+ writable: false,
74
+ }),
75
+ });
76
+ return { view, current: () => current() };
77
+ }
78
+ //# sourceMappingURL=settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.js","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * 账户工具:kenari_balance / kenari_usage(公开 MCP 端点,无 REST 等价物)
3
+ * 与 kenari_quota(REST GET /v1/account/quota)。
4
+ * 分享页 key 调用三者都会 403(shared_key_not_allowed),错误文本给出解释。
5
+ * @module dsh-kenari-plugin/tools/account
6
+ */
7
+ import { defineTool } from '@deepseek-ai/dsh-tools';
8
+ import type { ToolsDeps } from './shared.js';
9
+ /** 注册账户三工具。 */
10
+ export declare function registerAccountTools(deps: ToolsDeps, register: (tool: ReturnType<typeof defineTool>) => void): void;
11
+ //# sourceMappingURL=account.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account.d.ts","sourceRoot":"","sources":["../../src/tools/account.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAiB5C,eAAe;AACf,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,KAAK,IAAI,GAAG,IAAI,CAwEnH"}
@@ -0,0 +1,132 @@
1
+ /**
2
+ * 账户工具:kenari_balance / kenari_usage(公开 MCP 端点,无 REST 等价物)
3
+ * 与 kenari_quota(REST GET /v1/account/quota)。
4
+ * 分享页 key 调用三者都会 403(shared_key_not_allowed),错误文本给出解释。
5
+ * @module dsh-kenari-plugin/tools/account
6
+ */
7
+ import { defineTool } from '@deepseek-ai/dsh-tools';
8
+ import { kenariGet, kenariMcpCall } from '../http.js';
9
+ /** REST 403 的分享页 key 特征码(OpenAPI:shared_key_not_allowed)。 */
10
+ function isSharedKeyMessage(message) {
11
+ return message.includes('shared_key_not_allowed') || message.includes('shared key');
12
+ }
13
+ /** 统一错误转译:分享页 key 给出可执行解释,其余原样透传。 */
14
+ function explainAccountError(err) {
15
+ const text = String(err);
16
+ if (isSharedKeyMessage(text) || text.includes('403')) {
17
+ return '该 key 来自分享页(share page):balance / usage / quota 读取的是 key 所有者的账户数据,因此被拒绝(403 shared_key_not_allowed)。消费类工具(搜索、抓取、生成)不受影响;需要读取账户数字请改用自己账号的 key。';
18
+ }
19
+ return `kenari 账户查询失败:${text}`;
20
+ }
21
+ /** 注册账户三工具。 */
22
+ export function registerAccountTools(deps, register) {
23
+ register(defineTool({
24
+ name: 'kenari_balance',
25
+ description: 'Your Kenari wallet balance in Indonesian Rupiah. Requires a personal (non-shared) kn- API key.',
26
+ parameters: {},
27
+ output: {
28
+ schema: {
29
+ type: 'object',
30
+ additionalProperties: false,
31
+ properties: { text: { type: 'string', required: true } },
32
+ },
33
+ render: (_args, value) => [{ type: 'text', text: value.text }],
34
+ },
35
+ timeoutMs: 30_000,
36
+ isConcurrencySafe: () => true,
37
+ async execute() {
38
+ try {
39
+ const text = await kenariMcpCall(deps.http, 'kenari_balance');
40
+ return { text: `Kenari 余额:${text}` };
41
+ }
42
+ catch (err) {
43
+ return { text: explainAccountError(err) };
44
+ }
45
+ },
46
+ }));
47
+ register(defineTool({
48
+ name: 'kenari_usage',
49
+ description: 'Your Kenari usage for the last 30 days (requests, tokens, and cost per model). Requires a personal (non-shared) kn- API key.',
50
+ parameters: {},
51
+ output: {
52
+ schema: {
53
+ type: 'object',
54
+ additionalProperties: false,
55
+ properties: { text: { type: 'string', required: true } },
56
+ },
57
+ render: (_args, value) => [{ type: 'text', text: value.text }],
58
+ },
59
+ timeoutMs: 30_000,
60
+ isConcurrencySafe: () => true,
61
+ async execute() {
62
+ try {
63
+ const text = await kenariMcpCall(deps.http, 'kenari_usage');
64
+ return { text: `Kenari 近 30 天用量:\n${text}` };
65
+ }
66
+ catch (err) {
67
+ return { text: explainAccountError(err) };
68
+ }
69
+ },
70
+ }));
71
+ register(defineTool({
72
+ name: 'kenari_quota',
73
+ description: 'Your Kenari plan windows and coupon quota in Rupiah (5-hour / weekly / monthly rolling windows). Requires a personal (non-shared) kn- API key.',
74
+ parameters: {},
75
+ output: {
76
+ schema: {
77
+ type: 'object',
78
+ additionalProperties: false,
79
+ properties: { text: { type: 'string', required: true } },
80
+ },
81
+ render: (_args, value) => [{ type: 'text', text: value.text }],
82
+ },
83
+ timeoutMs: 30_000,
84
+ isConcurrencySafe: () => true,
85
+ async execute() {
86
+ try {
87
+ const quota = await kenariGet(deps.http, '/account/quota');
88
+ return { text: formatQuota(quota) };
89
+ }
90
+ catch (err) {
91
+ return { text: explainAccountError(err) };
92
+ }
93
+ },
94
+ }));
95
+ }
96
+ /** quota 响应 → 可读文本;plan 与 coupon 皆空时如实说明。 */
97
+ function formatQuota(quota) {
98
+ const lines = ['Kenari 套餐与优惠券额度:'];
99
+ const plan = quota.plan;
100
+ if (plan === null || plan === undefined) {
101
+ lines.push('- 套餐:无(从未订阅、已过期或不活跃)');
102
+ }
103
+ else {
104
+ lines.push(`- 套餐:${plan.name ?? '(未命名)'}`);
105
+ const windows = plan.windows ?? {};
106
+ const rows = [
107
+ ['5 小时', windows.five_hour],
108
+ ['每周', windows.week],
109
+ ['每月', windows.month],
110
+ ];
111
+ for (const [label, window] of rows) {
112
+ if (window === undefined)
113
+ continue;
114
+ const reset = window.resets_at === undefined ? '' : `,${window.resets_at} 重置`;
115
+ lines.push(` - ${label}窗口:已用 Rp ${window.used_rp ?? 0} / 剩余 Rp ${window.remaining_rp ?? 0}${reset}`);
116
+ }
117
+ }
118
+ const coupon = quota.coupon;
119
+ if (coupon !== null && coupon !== undefined) {
120
+ const remaining = coupon.remaining_rp === null || coupon.remaining_rp === undefined
121
+ ? '无上限'
122
+ : `Rp ${coupon.remaining_rp}`;
123
+ lines.push(`- 优惠券:${coupon.name ?? '(未命名)'},已用 Rp ${coupon.used_rp ?? 0} / 剩余 ${remaining}`);
124
+ if (coupon.expires_at !== undefined)
125
+ lines.push(` - 过期:${coupon.expires_at}`);
126
+ if (coupon.scope_models !== undefined && coupon.scope_models.length > 0) {
127
+ lines.push(` - 适用模型:${coupon.scope_models.join(', ')}`);
128
+ }
129
+ }
130
+ return lines.join('\n');
131
+ }
132
+ //# sourceMappingURL=account.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account.js","sourceRoot":"","sources":["../../src/tools/account.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * 计费总览工具:一个地方看全「这个会话花了多少、还剩多少、缓存命中多少」。
3
+ * 数据来自 billing.ts 的账本(工具调用与 web provider 共用同一个账本)与余额监控。
4
+ * @module dsh-kenari-plugin/tools/billing
5
+ */
6
+ import { defineTool } from '@deepseek-ai/dsh-tools';
7
+ import type { ToolsDeps } from './shared.js';
8
+ /** 注册 kenari_billing。 */
9
+ export declare function registerBillingTool(deps: ToolsDeps, register: (tool: ReturnType<typeof defineTool>) => void): void;
10
+ //# sourceMappingURL=billing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"billing.d.ts","sourceRoot":"","sources":["../../src/tools/billing.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAQ5C,yBAAyB;AACzB,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,KAAK,IAAI,GAAG,IAAI,CAwFlH"}