dsh-llm-codebuddy-power 1.0.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.
Files changed (44) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +66 -0
  3. package/README.zh.md +66 -0
  4. package/cordis.patch.yml +32 -0
  5. package/lib/cli-login.js +2099 -0
  6. package/lib/client.js +2814 -0
  7. package/lib/index.js +3957 -0
  8. package/lib/types/adapter.d.ts +58 -0
  9. package/lib/types/atomic-file.d.ts +43 -0
  10. package/lib/types/auth-service.d.ts +195 -0
  11. package/lib/types/cli/login.d.ts +23 -0
  12. package/lib/types/client/AccountSwitcher.d.ts +53 -0
  13. package/lib/types/client/CodeBuddySection.d.ts +62 -0
  14. package/lib/types/client/ComposerControls.d.ts +29 -0
  15. package/lib/types/client/DrawToolCard.d.ts +24 -0
  16. package/lib/types/client/ModelPanel.d.ts +77 -0
  17. package/lib/types/client/TurnCreditPill.d.ts +33 -0
  18. package/lib/types/client/UsageIndicator.d.ts +21 -0
  19. package/lib/types/client/icons.d.ts +24 -0
  20. package/lib/types/client/index.d.ts +34 -0
  21. package/lib/types/client/locales.d.ts +90 -0
  22. package/lib/types/client/poll.d.ts +19 -0
  23. package/lib/types/client/store.d.ts +59 -0
  24. package/lib/types/client/wire.d.ts +147 -0
  25. package/lib/types/codebuddy.d.ts +187 -0
  26. package/lib/types/constants.d.ts +67 -0
  27. package/lib/types/credit-store.d.ts +25 -0
  28. package/lib/types/image-tool.d.ts +34 -0
  29. package/lib/types/index.d.ts +79 -0
  30. package/lib/types/login.d.ts +43 -0
  31. package/lib/types/model-enrich.d.ts +125 -0
  32. package/lib/types/prefs.d.ts +43 -0
  33. package/lib/types/rpc-route.d.ts +25 -0
  34. package/lib/types/selection.d.ts +35 -0
  35. package/lib/types/serialize.d.ts +37 -0
  36. package/lib/types/session-store.d.ts +63 -0
  37. package/lib/types/session.d.ts +415 -0
  38. package/lib/types/settings.d.ts +18 -0
  39. package/lib/types/sse.d.ts +22 -0
  40. package/lib/types/storage.d.ts +118 -0
  41. package/lib/types/translate.d.ts +61 -0
  42. package/lib/types/types.d.ts +345 -0
  43. package/lib/types/usage.d.ts +109 -0
  44. package/package.json +119 -0
@@ -0,0 +1,415 @@
1
+ /**
2
+ * 已登录会话:按账号解析身份、token 新鲜度与缓存的模型目录。
3
+ *
4
+ * 一个对象同时持有它们,因为它们共享同一种故障模式 → token 过期会导致
5
+ * 目录不可读 → 也因为它们都必须在构建请求之前解析完成。刷新是单飞的,而且是
6
+ * **按账号**单飞:适配器每次流式调用解析一次身份,若不按账号分开,两个账号的
7
+ * 并发调用会互相取到对方的刷新结果,其中一个的 token 永远停留过期。
8
+ *
9
+ * 账号由会话决定:某个会话显式选了账号就用它,否则顺延到派生它的会话(子代理、
10
+ * fork)、默认账号、最后一次手动切换的账号,最后落到列表第一个。目录与计量快照
11
+ * 也按账号分别缓存,因为不同账号的可用模型与额度本就可以不同。
12
+ *
13
+ * @module dsh-llm-codebuddy-power/session
14
+ */
15
+ import type { UsageSnapshot } from './usage.ts';
16
+ import type { SessionAccountRef } from './session-store.ts';
17
+ import type { CodeBuddyModel, CodeBuddyModelPromotion, CodeBuddyModelTier } from './types.ts';
18
+ /** 未登录任何账号时抛出;消息中携带补救方法。 */
19
+ export declare class NotLoggedInError extends Error {
20
+ constructor(detail: string);
21
+ }
22
+ /** 与 cordis 兼容的 logger 接口,使该会话可以脱离 cordis 单独使用。 */
23
+ export interface SessionLogger {
24
+ warn: (message: unknown) => void;
25
+ error: (message: unknown) => void;
26
+ }
27
+ /** 一次目录读取的内容,与它附带的活动和档位一起缓存。 */
28
+ interface Catalog {
29
+ models: readonly CodeBuddyModel[];
30
+ promotions: readonly CodeBuddyModelPromotion[];
31
+ tiers: readonly CodeBuddyModelTier[] | undefined;
32
+ }
33
+ /** 设置页与账号切换按钮读取的账号投影。 */
34
+ export interface CodeBuddyAccountInfo {
35
+ uid: string;
36
+ nickname: string;
37
+ /**
38
+ * 刷新令牌的绝对过期时刻(epoch ms);该时刻之后必须重新登录。
39
+ *
40
+ * 用刷新令牌而不是访问令牌的到期:访问令牌在临近过期时由本插件自动刷新,用户感知不到,
41
+ * 拿它当"登录到期"会在每次刷新后变动;刷新令牌到期才是唯一需要用户重新走一次浏览器
42
+ * 登录的时刻。
43
+ */
44
+ loginExpiresAt: number;
45
+ /** 腾讯用户身份号(如 QQ openid),当账号披露该字段时。 */
46
+ uin?: string;
47
+ enterpriseId?: string;
48
+ /** 企业显示名,当账号属于企业租户时。 */
49
+ enterpriseName?: string;
50
+ /** 企业用户名(账号在租户内的名字)。 */
51
+ enterpriseUserName?: string;
52
+ departmentFullName?: string;
53
+ }
54
+ /**
55
+ * 发起一次请求的账号。
56
+ *
57
+ * 完整 uid 与昵称都在这里,因为两者要一起进入会话文件的账号表:表由文件自己编序号做键,
58
+ * 值是完整 uid 与昵称。表只在首次用到该账号时新增一条,同一账号在几十轮里只存一次。
59
+ */
60
+ export interface TurnCreditAccount {
61
+ /** 账号 uid。 */
62
+ uid: string;
63
+ /** 账号昵称,写入时刻的值。 */
64
+ nickname: string;
65
+ }
66
+ /**
67
+ * 每轮积分记录,即文件里的形状:消耗值,以及本轮用过的账号。
68
+ *
69
+ * 账号只存账号表里的序号(见 `session-store.ts`),完整 uid 与昵称在表里各存一份。模型
70
+ * 由 dsh 用量自带,插件不再存。
71
+ */
72
+ export interface TurnCreditRecord {
73
+ credit: number;
74
+ /**
75
+ * 本轮扣分用过的账号序号,按首次使用排序。一轮里用户可以切换账号(账号按钮在运行中
76
+ * 仍可用,账号又是每次模型请求解析一次),因此一轮可能真的跨账号:只有记下用过的
77
+ * 每一个,事后悬停才不会把整轮说成最后那个账号。
78
+ */
79
+ accounts: string[];
80
+ }
81
+ /**
82
+ * 解析出的一个账号:界面需要的两个事实。
83
+ *
84
+ * uid 是完整值而不是截断:截断只为显示,而那属于呈现层的取舍,不该让存储层丢掉事实。
85
+ */
86
+ export interface TurnCreditAccountView {
87
+ nickname: string;
88
+ uid: string;
89
+ }
90
+ /** 一轮积分记录解析后的形状:账号引用已按账号表展开。 */
91
+ export interface TurnCreditRecordView {
92
+ credit: number;
93
+ accounts: TurnCreditAccountView[];
94
+ }
95
+ /**
96
+ * 把一条文件记录里的账号引用按账号表展开。
97
+ *
98
+ * 引用解析不到时返回 `undefined` 而不是抛错:读取路径已经保证不会发生(校验时表已建好),
99
+ * 这里只是让"残缺记录不交给界面"这条规则不依赖上游。
100
+ * @param record - 文件里的一条轮次记录。
101
+ * @param table - 同一份文件的账号表。
102
+ * @returns 展开后的记录;有引用解析不到时为 `undefined`。
103
+ */
104
+ export declare function resolveCreditRecord(record: TurnCreditRecord, table: Record<string, SessionAccountRef>): TurnCreditRecordView | undefined;
105
+ /** 一次请求的身份:请求头,以及这些头属于哪个账号。 */
106
+ export interface CodeBuddyRequestIdentity {
107
+ headers: Record<string, string>;
108
+ account: TurnCreditAccount;
109
+ }
110
+ /**
111
+ * 持有单个插件实例的已存账号、每个会话选用的账号与它们的缓存。
112
+ *
113
+ * 内存中没有凭据时会从磁盘重新读取,这正是
114
+ * `dsh plugin --profile web codebuddy-power` 能在不重启的情况下
115
+ * 让*运行中*的 harness 登录的原因。
116
+ */
117
+ export declare class CodeBuddySession {
118
+ private readonly logger?;
119
+ /**
120
+ * `listModels` 或 `resolveModel` 报出的内容可能变化时触发一次:账号集合变化
121
+ * (登录、退出、改默认账号)或各账号已读到的目录并集变大。
122
+ */
123
+ private readonly onModelsChanged?;
124
+ private storage;
125
+ /** 内存中 `storage` 所读自的凭据文件的新鲜度标记。 */
126
+ private storageKey;
127
+ /**
128
+ * {@link storageKey} 是否足以代表 {@link storage}。
129
+ *
130
+ * `undefined` 的新鲜度有两种含义 —— 文件真的不存在,或这一次 `fs.stat` 失败(Windows
131
+ * 上并发替换文件时会短暂如此)—— 所以它不能单独用来判断“没变”。把它当成“没变”而
132
+ * `storage` 恰好是空的,账号列表就会被读成空、界面显示“未登录”,而磁盘上凭据完好。
133
+ */
134
+ private storageMemoValid;
135
+ /** 上一次加载时账号集合的 uid 列表,用来判断集合本身有没有变。 */
136
+ private storageUids;
137
+ /** 按账号 uid 单飞的令牌刷新。 */
138
+ private readonly refreshing;
139
+ /** 按账号 uid 缓存的目录;合并目录由它派生,因此它的增删决定合并目录的内容。 */
140
+ private readonly catalogs;
141
+ /**
142
+ * 模型 id → 模型,由 {@link catalogs} 派生的并集索引,同 id 保留先到的那份。
143
+ *
144
+ * 集合只由**带会话身份的**读取填充(聊天请求、模型面板),并供拿不到会话身份的
145
+ * 调用方({@link knownModel}、`listModels`)查询:那些调用方无法知道该用哪个账号
146
+ * 的目录,主动读取只能落在默认账号上 —— 而当前会话用的可能正是另一个账号。
147
+ */
148
+ private readonly knownCatalog;
149
+ /** 按账号 uid 单飞的目录读取。 */
150
+ private readonly catalogReads;
151
+ /** 按账号 uid 缓存的计量快照;在 {@link METER_TTL_MS} 内直接返回,不访问该平面。 */
152
+ private readonly usageCache;
153
+ /** 活动会话,以 dsh 会话 id 为键;在 `session/disposed` 时清理。 */
154
+ private readonly tracks;
155
+ /**
156
+ * 已读出的会话信息(选用的账号 + 每轮积分),按会话 id。
157
+ *
158
+ * 单独于 {@link tracks}:账号选择要在还没有任何回合的会话上就能读到(新建会话后
159
+ * 立刻切账号,第一条消息就得用它),而 `tracks` 只在 `turn/start` 时才有条目。
160
+ */
161
+ private readonly infos;
162
+ /** 正在从文件读取的会话信息,避免并发重复读。 */
163
+ private readonly infoReads;
164
+ constructor(logger?: SessionLogger | undefined,
165
+ /**
166
+ * `listModels` 或 `resolveModel` 报出的内容可能变化时触发一次:账号集合变化
167
+ * (登录、退出、改默认账号)或各账号已读到的目录并集变大。
168
+ */
169
+ onModelsChanged?: (() => void) | undefined);
170
+ /**
171
+ * 在首次见到某会话时以及每个新轮次,为该会话铸出当前轮的请求 id。轮请求 id
172
+ * 在 harness 打开一个该会话尚未被观察到的轮次时铸出,因此一个用户
173
+ * 提示词内的所有模型调用都共享它,即使在运行中途插入了提示词。
174
+ * @param sessionId - dsh 会话身份。
175
+ * @param turn - harness 刚打开的轮次。
176
+ * @param parentSessionId - 该会话派生自的会话(子代理、fork),没有时为 `undefined`。
177
+ */
178
+ observeTurn(sessionId: string, turn: number, parentSessionId?: string): void;
179
+ /**
180
+ * 在 harness 销毁某会话时丢弃它被跟踪的回合状态与已读出的会话信息,使这两个 map
181
+ * 不会无界增长。磁盘上的会话信息不随之删除:该事件表示会话对象被销毁,而不是用户
182
+ * 删除了会话 —— 一次性子代理跑完就会触发它。
183
+ * @param sessionId - dsh 会话身份。
184
+ */
185
+ forget(sessionId: string): void;
186
+ /**
187
+ * 某会话对应的 CodeBuddy 会话 id。由 dsh 会话身份推导,不依赖跟踪状态:
188
+ * 同一会话在任何时刻、任何进程里都得到同一个值。
189
+ * @param sessionId - dsh 会话身份。
190
+ * @returns 32 位紧凑 uuid。
191
+ */
192
+ conversationIdOf(sessionId: string): string;
193
+ /**
194
+ * 某会话当前轮的请求 id;该会话尚未被观察到时为 `undefined`。
195
+ * @param sessionId - dsh 会话身份。
196
+ * @returns 该轮的请求 id;未被观察到的会话为 `undefined`。
197
+ */
198
+ turnRequestIdOf(sessionId: string): string | undefined;
199
+ /**
200
+ * 读出某会话的信息(选用的账号 + 每轮积分),进程内缓存一次。
201
+ *
202
+ * 首读才碰磁盘:账号解析在每次请求上发生,而一个会话的账号选择不会在运行期间被
203
+ * 别的进程改动(只有本进程写它),因此缓存到会话被销毁即可。
204
+ * @param sessionId - dsh 会话身份。
205
+ * @returns 该会话的信息;文件缺失时为“未选账号、无积分”。
206
+ */
207
+ private infoOf;
208
+ /**
209
+ * 把一次 CodeBuddy 请求的扣分累加到会话当前轮。聊天请求与画图工具都
210
+ * 调用:一轮内两者都可能发生(顺序不定),扣分相加。记录合并到插件自有的
211
+ * 会话信息文件(fire-and-forget),重启后仍可读。模型不存——dsh 用量自带。
212
+ *
213
+ * 账号一并记录,且只在首次使用时追加(按完整 uid 判定是否已记过):扣分与"哪个账号
214
+ * 扣的"必须在同一时刻取自同一个事实,悬停时再查当前账号会把一轮里切换过账号的情况说错。
215
+ *
216
+ * 序号在落盘的读-改-写内部才分配:它取决于该文件当前的账号表,而那张表只有在队列里
217
+ * 读到文件之后才知道。内存里因此存解析好的账号(昵称 + 完整 uid),文件里存序号 —— 两者
218
+ * 形状不同是有意的,文件只该存一次账号,而每次查询都回表解析会让悬停这条热路径多一层查找。
219
+ * @param sessionId - dsh 会话 id。
220
+ * @param credit - 本次请求扣的积分。
221
+ * @param account - 发起本次请求的账号。
222
+ */
223
+ recordCredit(sessionId: string, credit: number, account: TurnCreditAccount): void;
224
+ /**
225
+ * 某会话某一轮的积分。内存未命中时回退到会话信息文件(重启恢复),
226
+ * 并回填缓存。
227
+ * @param sessionId - dsh 会话身份。
228
+ * @param turn - 轮次号。
229
+ * @returns 该记录;该轮没有上报任何积分时为 `undefined`。
230
+ */
231
+ creditOf(sessionId: string, turn: number): Promise<TurnCreditRecordView | undefined>;
232
+ /**
233
+ * 遗忘内存中的账号与按账号缓存的数值,并广播这次变化。
234
+ *
235
+ * 刻意不动 {@link catalogs} 与由它派生的 {@link knownCatalog}:目录内容
236
+ * (模型、促销、档位)不随凭据或按模型的覆写变化(覆写每次解析都从磁盘重读,
237
+ * 见 `model-enrich.ts` 的 `loadOverrides`),而清掉它们会把官方列表的并集一起
238
+ * 打空 —— 本方法也被改覆写与 401 失效调用,那时别的账号已并进来的模型不该消失。
239
+ * 账号真的增删时由 {@link forgetDepartedAccounts} 按 uid 精确丢弃。
240
+ */
241
+ invalidate(): void;
242
+ /**
243
+ * 由当前缓存的各账号目录重建合并目录。
244
+ *
245
+ * 账号退出后的处理必须是"重建"而不是"清空":清空会让官方列表塌回默认账号,
246
+ * 而其余仍登录账号的目录本来就在手上 —— 继续并着才是"退出后重新合并当前已有的
247
+ * 列表"。重建同时确定性地解决同 id 的归属:只剩下仍在的账号参与竞争。
248
+ */
249
+ private reindexKnownCatalog;
250
+ /**
251
+ * 丢弃已不在账号集合里的账号的缓存与计量快照,并重建合并目录。
252
+ *
253
+ * 按成员判定而不是按文件指纹:指纹会被 {@link invalidate} 置空(改一次覆写也走
254
+ * 那条路),按指纹清空并集会让别的账号的模型凭空消失;而按成员判定是幂等的 ——
255
+ * 同一个账号仍在时什么都不丢,重复调用也不会多清一次。
256
+ */
257
+ private forgetDepartedAccounts;
258
+ /**
259
+ * 只要凭据文件自上次读取以来发生了变化就重新读取,使另一个进程
260
+ * (CLI)执行的登录或登出能在不重启的情况下到达运行中的 harness:已持有的
261
+ * `storage` 不能比被重写或删除的凭据文件活得更久。
262
+ *
263
+ * 重读之后只有**账号集合本身**变了才算一次账号变更,判据是 uid 列表而不是文件
264
+ * 指纹:指纹每次写文件都变,于是每刷新一次令牌(另一个进程的 `--status` 也会)
265
+ * 或每记一次“最后切换的账号”都会被当成账号变更,从而丢掉按账号缓存的计量快照并
266
+ * 广播一次模型变更 —— 那些都不是账号增删。真的增删时才会丢弃已退出账号的缓存并
267
+ * 重建合并目录:退出一个账号后官方列表应当继续并着其余账号已读到的目录,而不是
268
+ * 塌回默认账号一个。
269
+ */
270
+ private refreshIfChanged;
271
+ /** 账号条目,按 uid 查。 */
272
+ private accountEntry;
273
+ /**
274
+ * 该会话自身及其派生祖先显式选用的账号,由近及远。读取某个会话的信息可能要碰盘,
275
+ * 因此本方法是异步的。
276
+ *
277
+ * 走到祖先是因为子代理与 fork 出来的会话在界面上没有自己的账号按钮:它们若不
278
+ * 沿用父会话,就会静默改用默认账号,把一次派发记到另一个账号头上。
279
+ */
280
+ private accountChain;
281
+ /**
282
+ * 该会话此刻生效的账号 uid。
283
+ *
284
+ * 顺序:会话(或其派生祖先)显式选用的账号 → 默认账号 → 最后一次手动切换的
285
+ * 账号 → 列表第一个。指向已退出账号的选择会被跳过,而不是当成“没有账号”;
286
+ * 全部落空时返回 `undefined`,调用方据此报未登录。
287
+ */
288
+ private accountUidFor;
289
+ private identityOf;
290
+ /** 把刷新后的账号写回内存,并尽力落盘(写不进去也不影响本进程继续使用)。 */
291
+ private persist;
292
+ /**
293
+ * 已存账号条目,在首次使用、失效之后以及文件被外部改动时从磁盘读取。
294
+ * @param sessionId - 用哪个会话的账号;会话未选用时按默认账号解析。
295
+ * @throws NotLoggedInError 未存有任何凭据时。
296
+ */
297
+ private require;
298
+ /**
299
+ * 某账号可用的身份,在访问 token 到达或临近过期时刷新它。同一账号的并发
300
+ * 调用者共享同一次刷新;不同账号各刷各的。
301
+ * @param entry - 要解析的账号条目。
302
+ * @returns 用于认证请求的身份。
303
+ * @throws NotLoggedInError 刷新 token 本身已过期、只能重新在浏览器登录时。
304
+ */
305
+ private identityFor;
306
+ private refresh;
307
+ /** 由身份构建每个已认证 CodeBuddy 请求都携带的请求头。 */
308
+ private headersFrom;
309
+ /**
310
+ * 每个已认证 CodeBuddy 请求都携带的请求头,以及这些头属于哪个账号。
311
+ *
312
+ * 两者在**同一次**身份解析中一起给出:分开解析会有两次账号选择,而账号是每次请求
313
+ * 解析的(用户可在同一步里切换),两次解析可能落到不同账号上 —— 那样记进这一轮的
314
+ * 账号名就与实际发出去的请求不符。缩写由落盘侧按同一规则算出,故此处给出完整账号。
315
+ * @param sessionId - 请求所属的会话;决定用哪个账号,缺省时用默认账号。
316
+ * @returns 身份请求头与发起该请求的账号。
317
+ */
318
+ requestIdentity(sessionId?: string): Promise<CodeBuddyRequestIdentity>;
319
+ /**
320
+ * 是否存在凭据,但不要求必须有。
321
+ * @returns 已存账号可读取且可用时为 true。
322
+ */
323
+ isLoggedIn(): Promise<boolean>;
324
+ /**
325
+ * 已存账号及其默认账号,供设置页与会话切换按钮展示。
326
+ * @returns 账号列表(顺序与磁盘一致)与默认账号 uid;未登录时列表为空。
327
+ */
328
+ accountList(): Promise<{
329
+ accounts: CodeBuddyAccountInfo[];
330
+ defaultUid?: string;
331
+ }>;
332
+ /**
333
+ * 某会话此刻生效的账号 uid。
334
+ * @param sessionId - dsh 会话身份;缺省时用默认账号。
335
+ * @returns 账号 uid;一个账号也没有时为 `undefined`。
336
+ */
337
+ activeUid(sessionId?: string): Promise<string | undefined>;
338
+ /**
339
+ * 把某会话的账号切换为指定账号,并把它记为最后一次手动切换的账号。
340
+ * @param sessionId - dsh 会话身份。
341
+ * @param uid - 要使用的账号 uid。
342
+ * @returns 该 uid 是已存账号时为 true;否则不做任何改动并返回 false。
343
+ */
344
+ activate(sessionId: string, uid: string): Promise<boolean>;
345
+ /**
346
+ * CodeBuddy 模型目录,短暂缓存,并在并发读取者之间共享。
347
+ * @param signal - 底层读取的可选取消信号。
348
+ * @param sessionId - 用哪个会话的账号;缺省时用默认账号。
349
+ * @returns 目录中的模型,按服务自身的顺序。
350
+ */
351
+ models(signal?: AbortSignal, sessionId?: string): Promise<readonly CodeBuddyModel[]>;
352
+ /**
353
+ * 目录及其推广与档位,在同一个 TTL 下按账号一起缓存,并按账号单飞,使列表与
354
+ * 面板富化共享同一次读取。
355
+ * @param signal - 底层读取的可选取消信号。
356
+ * @param sessionId - 用哪个会话的账号;缺省时用默认账号。
357
+ * @returns 模型、推广与档位。
358
+ */
359
+ catalogData(signal?: AbortSignal, sessionId?: string): Promise<Catalog>;
360
+ private readCatalog;
361
+ /**
362
+ * 从各账号已读到的目录的并集里查一个模型,**不发起任何请求**。
363
+ *
364
+ * 供拿不到会话身份的调用方使用(适配器的 `resolveModel`):它无法知道该用哪个
365
+ * 账号的目录,主动读取只能落在默认账号上,而当前会话用的可能正是另一个账号 ——
366
+ * 那会把一个账号独有的模型解析成保守的默认能力(上下文窗口、输出上限、图片输入
367
+ * 全部按最保守处理)。并集由带会话身份的读取填充,因此这个查询零成本、也不改变
368
+ * "谁在读目录"这件事。
369
+ * @param modelId - 模型 id。
370
+ * @returns 该模型;尚无任何账号读到过它时为 `undefined`。
371
+ */
372
+ knownModel(modelId: string): CodeBuddyModel | undefined;
373
+ /**
374
+ * 各账号已读到的目录并集里的全部模型。
375
+ *
376
+ * 与 {@link knownModel} 同为只读查询:官方模型列表按它取数,因此它既包含默认
377
+ * 账号的模型(冷启动那一次读取必然先做),也包含其他账号在会话里读过之后并进来
378
+ * 的模型。顺序按各账号目录并入的先后,而非任何排序 —— 官方列表自行决定呈现顺序。
379
+ * @returns 并集里的模型。
380
+ */
381
+ knownModels(): readonly CodeBuddyModel[];
382
+ /**
383
+ * 目录及其推广/档位;读不到时返回空值 —— 供面板使用的
384
+ * {@link catalogData} 建议性读取孪生体。
385
+ * @param signal - 可选取消信号。
386
+ * @param sessionId - 用哪个会话的账号;缺省时用默认账号。
387
+ * @returns 目录与活动,或空列表。
388
+ */
389
+ catalogDataOrEmpty(signal?: AbortSignal, sessionId?: string): Promise<Catalog>;
390
+ /**
391
+ * 目录;读不到时返回空列表。
392
+ *
393
+ * 列出模型是设置页上的浏览动作,所以失败必须降级为“没有可显示的内容”,
394
+ * 而不是让页面崩掉。请求路径直接使用 {@link models} 并保留真实失败。
395
+ * @param signal - 可选取消信号。
396
+ * @param sessionId - 用哪个会话的账号;缺省时用默认账号。
397
+ * @returns 目录,或空列表。
398
+ */
399
+ modelsOrEmpty(signal?: AbortSignal, sessionId?: string): Promise<readonly CodeBuddyModel[]>;
400
+ /**
401
+ * CodeBuddy 用量/配额快照;读不到时为 `undefined`。
402
+ *
403
+ * 用量是界面上的建议性读取,所以计量故障必须降级为“没有可显示的
404
+ * 内容”而不是向外传播:{@link NotLoggedInError} 表现为未登录状态,
405
+ * 其他每种失败(传输、解析、刷新过期)都在一条警告之后解析为
406
+ * `undefined`。身份通过与聊天请求相同的按账号单飞刷新解析,所以并发的计量
407
+ * 读取绝不会消耗两次刷新 token。
408
+ * @param sessionId - 用哪个会话的账号;缺省时用默认账号。
409
+ * @param signal - 可选取消信号。
410
+ * @returns 该快照;未存有任何凭据或计量平面不可达时为 `undefined`。
411
+ */
412
+ usage(sessionId?: string, signal?: AbortSignal): Promise<UsageSnapshot | undefined>;
413
+ }
414
+ export {};
415
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1,18 @@
1
+ /**
2
+ * 本插件设置命名空间的宿主 schema。
3
+ *
4
+ * 校验只在宿主发生:浏览器面若也引入 schemastery,它会连同校验器一起被打进
5
+ * `lib/client.js`,而客户端本来就把服务端返回的数据当作已校验结果接收。因此
6
+ * schema 只在此文件出现,浏览器面经 `./prefs.ts` 取同一份类型与默认值。
7
+ *
8
+ * 字段的默认值一律取自 `DEFAULT_CODEBUDDY_SETTINGS`,不在此重复字面量 ——
9
+ * 两处各写一份时,改了一处就会出现「宿主默认 90、界面显示 80」这种只有用户
10
+ * 看得见的偏差。
11
+ *
12
+ * @module dsh-llm-codebuddy-power/settings
13
+ */
14
+ import z from '@deepseek-ai/schemastery';
15
+ import type { CodeBuddyPrefs } from './prefs.ts';
16
+ /** 用户设置文档中本插件命名空间的 schema。 */
17
+ export declare const CodeBuddySettingsSchema: z<CodeBuddyPrefs>;
18
+ //# sourceMappingURL=settings.d.ts.map
@@ -0,0 +1,22 @@
1
+ /**
2
+ * 把 SSE 字节流解码为事件的 `data` 载荷。
3
+ *
4
+ * 分帧规则来自 `eventsource-parser`:分块重组、UTF-8 边界、CRLF、
5
+ * BOM、注释行,以及多个 `data:` 的拼接。本模块只保留
6
+ * 协议决策 —— 字面量 `[DONE]` 哨兵会被产出,以便由调用方
7
+ * 负责最终的 flush;它之前的 EOF 属于截断,而不是
8
+ * 可完成的响应。
9
+ *
10
+ * @module dsh-llm-codebuddy-power/sse
11
+ */
12
+ /** OpenAI 兼容流在最后一个分块之后发送的终止载荷。 */
13
+ export declare const DONE = "[DONE]";
14
+ /**
15
+ * 把 SSE 字节流解析为 data 载荷。
16
+ * @param stream - 原始 SSE 字节分块;读取可能在任何位置断开。
17
+ * @param onComment - 传输活动回调;注释不会进入载荷流。
18
+ * @returns 按到达顺序的各个载荷,`[DONE]` 在最后。
19
+ * @throws LlmError 流在未出现 `[DONE]` 就结束时抛出 `STREAM_CLOSED`。
20
+ */
21
+ export declare function parseSse(stream: ReadableStream<BufferSource>, onComment?: (comment: string) => void): AsyncGenerator<string>;
22
+ //# sourceMappingURL=sse.d.ts.map
@@ -0,0 +1,118 @@
1
+ /**
2
+ * 持久化 OAuth 账号存储,磁盘上仅属主可读。
3
+ *
4
+ * 存储位于 harness home 的规范数据目录
5
+ * (`$DSH_HOME/data/plugins/llm-codebuddy-power/`,通过 harness 所用的同一个
6
+ * `@deepseek-ai/dsh-home-paths` 解析),而不放在插件包内,因此重装不会让
7
+ * 用户退出登录。写入经 [atomic-file.ts](./atomic-file.ts) 做原子替换并按路径串行化:
8
+ * 多个账号的令牌刷新、设置页的增删与 CLI 的调用都在改同一份文件,不串行化时
9
+ * 两个并发写入会各自基于自己读到的旧内容覆盖对方。残缺文件会让用户手里只有
10
+ * 一份读不出的凭据,还无法与“从未登录过”区分开。
11
+ *
12
+ * @module dsh-llm-codebuddy-power/storage
13
+ */
14
+ /** 一个已登录账号:凭据随账号走,不同账号各有自己的令牌。 */
15
+ export interface CodeBuddyStoredAccount {
16
+ auth: {
17
+ accessToken: string;
18
+ /** 绝对过期时间,epoch ms。 */
19
+ expiresAt: number;
20
+ refreshToken: string;
21
+ /** 刷新令牌的绝对过期时间,epoch ms。 */
22
+ refreshExpiresAt: number;
23
+ domain: string;
24
+ };
25
+ account: {
26
+ uid: string;
27
+ nickname: string;
28
+ /** 腾讯用户身份号(如 QQ openid),当账号披露该字段时。 */
29
+ uin?: string;
30
+ enterpriseId?: string;
31
+ /** 企业显示名,当账号属于企业租户时。 */
32
+ enterpriseName?: string;
33
+ /** 企业用户名(账号在租户内的名字)。 */
34
+ enterpriseUserName?: string;
35
+ departmentFullName?: string;
36
+ };
37
+ }
38
+ /** 持久化的账号集合。 */
39
+ export interface CodeBuddyStorage {
40
+ /** 账号列表,顺序即展示顺序。 */
41
+ accounts: CodeBuddyStoredAccount[];
42
+ /** 用户显式指定的默认账号;未指定时缺省。 */
43
+ defaultUid?: string;
44
+ /**
45
+ * 最后一次被手动切换到(按会话)的账号;没有默认账号时作为兜底,所以存在这里
46
+ * 而不是某个会话的文件里 —— 它是账号级的事实,不是某个会话的事实。
47
+ */
48
+ lastUid?: string;
49
+ }
50
+ /**
51
+ * 凭据文件的绝对路径。
52
+ *
53
+ * 通过 `@deepseek-ai/dsh-home-paths` 解析,以跟随 harness 自身的 home 优先级
54
+ * (配置路径 > `$DSH_HOME` > `~/.dsh`),绝不会分叉到另行计算的 home。
55
+ * `DSH_CODEBUDDY_AUTH_FILE` 保留作为测试与迁移用的显式逃生口。
56
+ * @returns 绝对凭据路径,优先遵循环境变量覆盖。
57
+ */
58
+ export declare function getStoragePath(): string;
59
+ /**
60
+ * 读取已存储的账号集合。
61
+ *
62
+ * 在读取任何字节之前先检查文件模式:主机上其他用户可读的凭据会被当作不存在
63
+ * 而不是拿来使用,因此丢失仅属主模式的文件(手动 chmod 出错、从别处拷贝)
64
+ * 绝不会被加载。当作不存在也能自愈——下次登录会用 `0o600` 重写该文件。
65
+ * @returns 账号集合;没有可用内容时为空集合。文件缺失、文件损坏与权限不安全
66
+ * 刻意给出相同答案:都表示“此处没有可安全用于认证的东西”,而登录流程是
67
+ * 每一种情况的修复方式。
68
+ */
69
+ export declare function loadStorage(): Promise<CodeBuddyStorage>;
70
+ /**
71
+ * 读-改-写账号集合。
72
+ *
73
+ * 读发生在队列内部,因此排队中的后一次改动看得到前一次的结果,而不是各自
74
+ * 基于同一份旧内容。文件此刻无法安全读取时按空集合起手,与 {@link loadStorage}
75
+ * 对同一情况的判断一致。
76
+ * @param change - 由当前集合算出新集合;不得就地修改传入值。
77
+ */
78
+ export declare function updateStorage(change: (current: CodeBuddyStorage) => CodeBuddyStorage): Promise<void>;
79
+ /**
80
+ * 插入一个账号,或替换同 uid 账号的凭据。
81
+ *
82
+ * 同 uid 视为重新登录同一个账号:令牌被替换而不是追加成第二个条目,否则
83
+ * 设置页会出现两个同名徽章,退出其中一个还会留下另一个。
84
+ * @param entry - 刚登录得到的账号。
85
+ */
86
+ export declare function upsertAccount(entry: CodeBuddyStoredAccount): Promise<void>;
87
+ /**
88
+ * 按给定顺序重排账号。
89
+ *
90
+ * 先按传入顺序取已知账号,再把没被提到的账号按原有相对顺序追加在后:因此落点算错、
91
+ * 漏传某个 uid 或传入未知 uid 都不会让账号丢失或重复 —— 拖拽结果与磁盘之间只有这一处
92
+ * 取舍,写错就是把用户已登录的账号弄丢。重复的 uid 只取第一次出现。
93
+ * @param uids - 目标顺序上的账号 uid。
94
+ */
95
+ export declare function reorderAccounts(uids: readonly string[]): Promise<void>;
96
+ /**
97
+ * 移除若干账号,并清掉指向它们的默认账号与“最后一次手动切换”的账号。
98
+ * @param uids - 要移除的账号 uid。
99
+ */
100
+ export declare function removeAccounts(uids: readonly string[]): Promise<void>;
101
+ /**
102
+ * 指定或取消默认账号。
103
+ * @param uid - 默认账号的 uid;传 `undefined` 取消默认账号。
104
+ */
105
+ export declare function setDefaultAccount(uid: string | undefined): Promise<void>;
106
+ /**
107
+ * 记下最后一次被手动切换到的账号(没有默认账号时作为兜底)。
108
+ * @param uid - 被切换到的账号 uid。
109
+ */
110
+ export declare function setLastAccount(uid: string): Promise<void>;
111
+ /**
112
+ * 凭据文件的不透明新鲜度令牌:文件每次被重写或删除时它都会变化,因此内存中
113
+ * 的会话能察觉另一个进程(CLI)执行的登录与登出,而不必在什么都没变时
114
+ * 每次调用都重新读取。
115
+ * @returns 一个令牌;没有凭据文件时返回 `undefined`。
116
+ */
117
+ export declare function storageFreshness(): Promise<string | undefined>;
118
+ //# sourceMappingURL=storage.d.ts.map
@@ -0,0 +1,61 @@
1
+ /**
2
+ * 把 CodeBuddy SSE 负载转换成 harness 的 `StreamChunk` 协议。
3
+ *
4
+ * 文本、推理和工具调用索引各持有一个打开的块,索引按首次出现的顺序分配。
5
+ * `block-end`、`usage` 和 `finish` 全部推迟到 `[DONE]` 哨兵:正是这一点满足了
6
+ * 协议的两项硬性义务——usage 严格早于 finish,且 finish 之后没有任何内容
7
+ * ——对发送尾部 usage-only 块的 provider 也是如此。
8
+ *
9
+ * @module dsh-llm-codebuddy-power/translate
10
+ */
11
+ import type { FinishReason, StreamChunk, TokenUsage } from '@deepseek-ai/dsh-llm';
12
+ import type { WireUsage } from './types.ts';
13
+ /** harness Tool 定义中修复 wire 参数所需的最小子集。 */
14
+ interface ToolDefinition {
15
+ name: string;
16
+ parameters: Record<string, unknown>;
17
+ }
18
+ /**
19
+ * 修复 CodeBuddy 对未受约束 Tool 字段偶发的双重编码。
20
+ *
21
+ * 没有 `type` 的属性 schema 是合法 JSON Schema,表示其值可以是任意 JSON
22
+ * 类型。CodeBuddy 仍可能把为这种字段选定的 object 或 array 渲染成 JSON
23
+ * 字符串。只解码这一种形态;有类型和其他约束的字段、标量字符串以及格式
24
+ * 错误的 JSON 一律逐字节保持原样。
25
+ */
26
+ export declare function normalizeToolArguments(name: string, argumentsText: string, tools: readonly ToolDefinition[]): string;
27
+ /**
28
+ * 把 wire 的 `finish_reason` 取值映射到 harness 的取值。
29
+ * @param reason - wire 的取值。
30
+ * @returns 映射后的原因;任何无法识别的值都会变成 error finish,并以大写后的
31
+ * wire 取值作为其 code,这样新出现的 provider 原因会以自身形式暴露,而不是
32
+ * 被静默报告为正常停止。
33
+ */
34
+ export declare function mapFinishReason(reason: string): FinishReason;
35
+ /**
36
+ * 把 wire 的 usage 映射到 harness 的互斥计数。
37
+ *
38
+ * CodeBuddy 的 `prompt_tokens` 是 OpenAI 兼容语义,含缓存命中
39
+ * (`prompt_tokens = 缓存命中 + 未缓存输入`);harness 的 TokenUsage 约定是
40
+ * 互斥计数,所以缓存命中从 `inputTokens` 里减掉。`totalTokens` 用 wire 的
41
+ * 原始 prompt+completion 之和(与官方 DeepSeek adapter 一致),仅当
42
+ * prompt/completion 都是合法计数时才给出。
43
+ * @param usage - wire 的 usage 块。
44
+ * @returns 互斥 token 计数;合法时含完整的聚合总数。
45
+ */
46
+ export declare function mapUsage(usage: WireUsage): TokenUsage;
47
+ /**
48
+ * 消费以 `[DONE]` 结尾的 SSE 负载并产出 harness 块。
49
+ * @param payloads - 来自 `parseSse` 的负载。
50
+ * @param tools - 用于修复 CodeBuddy wire 参数的原始 Tool schema。
51
+ * @param sink - 可选出参,调用方通过它观察不随 harness 块协议传递的附带数据
52
+ * (例如服务扣减的 credits)。
53
+ * @returns 到达时即产出的增量,块结束、usage 和 finish 在 `[DONE]` 时刷出。
54
+ * @throws LlmError 在 JSON 无法解析时抛 `MALFORMED_RESPONSE`,在负载源未出现
55
+ * 哨兵就结束时抛 `STREAM_CLOSED`。
56
+ */
57
+ export declare function translate(payloads: AsyncIterable<string>, tools?: readonly ToolDefinition[], sink?: {
58
+ credit?: number;
59
+ }): AsyncGenerator<StreamChunk>;
60
+ export {};
61
+ //# sourceMappingURL=translate.d.ts.map