dsh-shlr-bilitoolkit 0.1.14 → 0.1.16

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 (3) hide show
  1. package/README.md +98 -0
  2. package/dist/dsh.js +77 -135
  3. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,98 @@
1
+ # dsh-shlr-bilitoolkit
2
+
3
+ B 站(哔哩哔哩)接口封装的 **TypeScript** 移植版,作为 [dsh](https://dshmp.com)(DeepSeek Harness)插件提供「视频 / 搜索 / 空间 / 专栏 / 动态 / 评论 / 关注 / 消息中心」等读取能力,以及点赞、收藏、投币、关注、评论、私信、三连、发动态等写操作。
4
+
5
+ > 本项目从原 Python 工具箱(`bili/`)完整移植而来,并作为 dsh(cordis 体系)插件提供上述能力。
6
+
7
+ ## 安装
8
+
9
+ ```bash
10
+ # 已发布到 npm
11
+ dsh plugin --profile <name> add dsh-shlr-bilitoolkit@latest
12
+
13
+ # 本地开发 / 试运行
14
+ npm install
15
+ npm run build
16
+ ```
17
+
18
+ 本地登录(独立获取 Cookie,终端打印 ASCII 二维码):
19
+
20
+ ```bash
21
+ npm install && npm run login
22
+ # 或指定 Cookie 文件
23
+ BILI_COOKIE_FILE=./my_cookies.json npm run login
24
+ ```
25
+
26
+ ## 插件入口
27
+
28
+ 对齐官方 cordis 规范:`src/dsh.ts` 导出 `name` / `inject = ["tools"]` / `apply(ctx)`,在 `apply` 内用官方 `ctx.tools.register(defineTool({ name, description, parameters, output, execute }))` 注册每个工具。`defineTool` 来自 `@deepseek-ai/dsh-tools`,由宿主提供、插件不打包(保证单实例),已在 `peerDependencies` 声明。`cordis.patch.yml` 为组合包配置层(`- insert: - id: bili name: dsh-shlr-bilitoolkit`,按包名解析到 `main`)。较旧、没有 `ctx.tools` 的 dsh 仍保留 `ctx.registerTool` / `ctx.services.tools.register` 回退。
29
+
30
+ ## 工具清单
31
+
32
+ 同类能力已用参数合并(`type` / `target` / `action` / `list`),**功能未减少**。
33
+
34
+ - **读取**:
35
+ `bili_recommend` · `bili_video_detail` · `bili_video_subtitle` · `bili_search` · `bili_space_info` · `bili_space_videos` · `bili_article_view` · `bili_article_list` · `bili_dynamic_feed` · `bili_dynamic_space` · `bili_comment_list` · `bili_relation_list` · `bili_notify_unread` · `bili_notify_list` · `bili_fav_folders` · `bili_video_pagelist` · `bili_video_playurl` · `bili_video_status` · `bili_space_collections` · `bili_space_collection` · `bili_fav_folder_videos` · `bili_dynamic_detail` · `bili_message_unread` · `bili_message_history` · `bili_relation_stat` · `bili_user_following` · `bili_profile_info` · `bili_comment_replies`
36
+ - **写操作**(均会真实修改账号状态,需登录):
37
+ `bili_like` · `bili_follow` · `bili_favorite` · `bili_send_message` · `bili_triple` · `bili_dynamic_publish` · `bili_dynamic_repost` · `bili_coin` · `bili_fav_folder_create` · `bili_fav_folder_delete` · `bili_dynamic_like` · `bili_dynamic_delete` · `bili_comment_send` · `bili_comment_reply` · `bili_comment_delete` · `bili_comment_like` · `bili_message_recall` · `bili_profile_update`
38
+ - **辅助**:`bili_login_status`(查登录态)· `bili_gen_qr`(生成登录二维码)· `bili_cache_clear`(清空本地缓存)
39
+
40
+ 合并工具的公共参数约定:
41
+
42
+ | 工具 | 参数 |
43
+ | --- | --- |
44
+ | `bili_search` | `type=video\|user`,`order`(仅视频) |
45
+ | `bili_relation_list` | `type=followings\|followers` |
46
+ | `bili_notify_list` | `type=replies\|ats\|likes` |
47
+ | `bili_favorite` | `action=add\|remove` |
48
+ | `bili_video_subtitle` | `list=true` 时列字幕轨,否则取正文 |
49
+ | `bili_comment_*` | `target=video\|dynamic\|article`,`id`(bvid / 动态 id / 专栏 cv 号) |
50
+ | `bili_profile_update` | `uname` / `sex` / `birthday` / `sign`(至少一项) |
51
+
52
+ ## 数据目录
53
+
54
+ Cookie / 缓存 / 二维码 / 设备号统一放在一个目录,默认 `bili_data/`(相对 dsh 工作目录),避免散落污染工作目录:
55
+
56
+ | 文件 | 用途 |
57
+ | --- | --- |
58
+ | `bili_cookies.json` | 登录 Cookie(`SESSDATA` / `bili_jct`) |
59
+ | `bili_cache.json` | WBI 签名密钥 + `bvid→aid` 缓存(原子写、损坏自愈) |
60
+ | `bili_qrcode.png` | 最近一次生成的登录二维码 |
61
+ | `bili_device.json` | 私信设备号 `dev_id`(跨重启稳定,降低风控) |
62
+
63
+ 可用环境变量:`BILI_DATA_DIR`(改目录)、`BILI_COOKIE_FILE` / `BILI_CACHE_FILE` / `BILI_QR_FILE` / `BILI_DEVICE_FILE`(精确指定单个文件)。兼容旧版:若新目录还没有 Cookie、而旧位置 `./bili_cookies.json` 有,会继续沿用。
64
+
65
+ ## 给 Agent 的约定
66
+
67
+ 仓库根目录的 [`AGENTS.md`](./AGENTS.md) 约定:需要登录时必须调用 `bili_gen_qr` 并把二维码**图片**展示给用户(而非只给链接或 ASCII)。把该 `AGENTS.md` 放到 dsh 工作目录,agent 即会遵守。
68
+
69
+ ## 目录结构
70
+
71
+ ```
72
+ src/
73
+ client.ts BiliClient:Session / Cookie / 请求 / 节流 / 重试 / 风控 / 表单编码
74
+ wbi.ts WbiSigner:w_rid / wts 签名(官方 mixin_key 算法,密钥落盘缓存 6h)
75
+ cache.ts JsonCache:本地 JSON 缓存(TTL / 原子写 / 损坏自愈)
76
+ errors.ts BiliApiError / CaptchaRequired / CookieError
77
+ models.ts 纯数据模型 + toDict()
78
+ formatting.ts 播放量 / 时长 / HTML 清理等展示格式化
79
+ pagination.ts iterPages:统一分页异步生成器
80
+ endpoints.ts 所有接口 URL 常量
81
+ apis/ video / search / space / comment / message / interact / dynamic /
82
+ article / relation / notify / profile / auth / download
83
+ dsh.ts dsh 插件入口:apply(ctx) + 工具清单
84
+ index.ts 对外导出
85
+ tests/ 离线单测(wbi 签名、格式化、缓存、lossless JSON 等)
86
+ cordis.patch.yml dsh 组合包配置层
87
+ scripts/login.ts npm run login 扫码登录脚本
88
+ ```
89
+
90
+ ## 与原 Python 版的差异 / 已知限制
91
+
92
+ - **写接口为表单编码**:`client.post` 默认把对象编码为 `application/x-www-form-urlencoded`,仅动态点赞等少数接口显式走 JSON(`jsonBody=true`)。
93
+ - **动态仅支持纯文字**:带图 / 投票 / 预约的富动态需额外图片上传流程,暂未实现。
94
+ - **下载 / ffmpeg**:`src/apis/download.ts` 为纯工具函数,未接入 dsh 工具。
95
+
96
+ ## 风控说明
97
+
98
+ 内置与原 Python 版一致的基础降风控策略:请求节流(默认 0.6s + 0~0.4s 抖动)、GET 退避重试、写操作不重试、HTTP 412 / `-412` 立即停手、WBI 签名。但单账号操作量上限、避免短时间切 IP/UA 等仍需使用习惯配合。设备号 `dev_id` 已落盘保持稳定。
package/dist/dsh.js CHANGED
@@ -141,31 +141,38 @@ export const TOOLS = [
141
141
  },
142
142
  {
143
143
  id: "bili_video_subtitle",
144
- description: "获取视频字幕纯文本(优先 AI 字幕 / 中文),用于「读取视频内容」。无字幕返回空串。",
144
+ description: "视频字幕:默认返回字幕纯文本(优先中文);list=true 时返回可用字幕轨列表。",
145
145
  parameters: {
146
146
  type: "object",
147
147
  properties: {
148
148
  bvid: { type: "string" },
149
149
  lang: { type: "string", default: "zh" },
150
+ list: { type: "boolean", default: false, description: "true 则只列字幕轨,不取正文" },
150
151
  },
151
152
  required: ["bvid"],
152
153
  },
153
154
  handler: async (a) => {
154
155
  const c = await getClient();
156
+ if (Boolean(a.list ?? false)) {
157
+ const subs = await c.video.subtitles(a.bvid);
158
+ return subs.map((s) => s.toDict());
159
+ }
155
160
  return { text: await c.video.subtitleText(a.bvid, a.lang ?? "zh") };
156
161
  },
157
162
  },
158
163
  {
159
- id: "bili_search_video",
160
- description: "搜索 B 站视频。",
164
+ id: "bili_search",
165
+ description: "搜索 B 站视频或用户。",
161
166
  parameters: {
162
167
  type: "object",
163
168
  properties: {
164
169
  keyword: { type: "string" },
170
+ type: { type: "string", enum: ["video", "user"], default: "video" },
165
171
  order: {
166
172
  type: "string",
167
173
  enum: ["totalrank", "click", "pubdate", "dm", "stow", "scores"],
168
174
  default: "totalrank",
175
+ description: "排序(仅视频)",
169
176
  },
170
177
  pageSize: { type: "integer", default: 20 },
171
178
  },
@@ -173,27 +180,15 @@ export const TOOLS = [
173
180
  },
174
181
  handler: async (a) => {
175
182
  const c = await getClient();
176
- const [videos, total] = await c.search.videos(a.keyword, 1, Number(a.pageSize ?? 20), a.order ?? "totalrank");
183
+ const ps = Number(a.pageSize ?? 20);
184
+ if (String(a.type ?? "video") === "user") {
185
+ const [users, total] = await c.search.users(a.keyword, 1, ps);
186
+ return { total, users: users.map((u) => u.toDict()) };
187
+ }
188
+ const [videos, total] = await c.search.videos(a.keyword, 1, ps, a.order ?? "totalrank");
177
189
  return { total, videos: videos.map((v) => v.toDict()) };
178
190
  },
179
191
  },
180
- {
181
- id: "bili_search_user",
182
- description: "搜索 B 站用户。",
183
- parameters: {
184
- type: "object",
185
- properties: {
186
- keyword: { type: "string" },
187
- pageSize: { type: "integer", default: 20 },
188
- },
189
- required: ["keyword"],
190
- },
191
- handler: async (a) => {
192
- const c = await getClient();
193
- const [users, total] = await c.search.users(a.keyword, 1, Number(a.pageSize ?? 20));
194
- return { total, users: users.map((u) => u.toDict()) };
195
- },
196
- },
197
192
  {
198
193
  id: "bili_space_info",
199
194
  description: "获取 B 站用户空间基本信息(昵称、等级、签名)。",
@@ -291,54 +286,50 @@ export const TOOLS = [
291
286
  // ---- 评论 ----
292
287
  {
293
288
  id: "bili_comment_list",
294
- description: "获取视频评论(bvid),返回评论列表与总数。",
289
+ description: "获取评论列表(视频 / 动态 / 专栏),返回评论列表。",
295
290
  parameters: {
296
291
  type: "object",
297
292
  properties: {
298
- bvid: { type: "string" },
293
+ target: { type: "string", enum: ["video", "dynamic", "article"], default: "video" },
294
+ id: { type: "string", description: "视频 bvid / 动态 id / 专栏 cv 号" },
299
295
  sort: { type: "string", enum: ["time", "like", "reply"], default: "time" },
300
296
  pageSize: { type: "integer", default: 20 },
301
297
  },
302
- required: ["bvid"],
298
+ required: ["id"],
303
299
  },
304
300
  handler: async (a) => {
305
301
  const c = await getClient();
306
- const [comments, total] = await c.comment.listVideo(a.bvid, 1, Number(a.pageSize ?? 20), a.sort ?? "time");
307
- return { total, comments: comments.map((x) => x.toDict()) };
302
+ const target = String(a.target ?? "video");
303
+ const ps = Number(a.pageSize ?? 20);
304
+ const sort = a.sort ?? "time";
305
+ let items;
306
+ if (target === "dynamic")
307
+ [items] = await c.comment.listDynamic(a.id, 1, ps, sort);
308
+ else if (target === "article")
309
+ [items] = await c.comment.listArticle(a.id, 1, ps, sort);
310
+ else
311
+ [items] = await c.comment.listVideo(a.id, 1, ps, sort);
312
+ return { comments: items.map((x) => x.toDict()) };
308
313
  },
309
314
  },
310
315
  // ---- 关注关系 ----
311
316
  {
312
- id: "bili_relation_followings",
313
- description: "获取某用户的关注列表。",
314
- parameters: {
315
- type: "object",
316
- properties: {
317
- uid: { type: "integer" },
318
- pageSize: { type: "integer", default: 20 },
319
- },
320
- required: ["uid"],
321
- },
322
- handler: async (a) => {
323
- const c = await getClient();
324
- const [users, total] = await c.relation.followings(Number(a.uid), 1, Number(a.pageSize ?? 20));
325
- return { total, users: users.map((x) => x.toDict()) };
326
- },
327
- },
328
- {
329
- id: "bili_relation_followers",
330
- description: "获取某用户的粉丝列表。",
317
+ id: "bili_relation_list",
318
+ description: "获取某用户的关注列表或粉丝列表。",
331
319
  parameters: {
332
320
  type: "object",
333
321
  properties: {
334
322
  uid: { type: "integer" },
323
+ type: { type: "string", enum: ["followings", "followers"], default: "followings" },
335
324
  pageSize: { type: "integer", default: 20 },
336
325
  },
337
326
  required: ["uid"],
338
327
  },
339
328
  handler: async (a) => {
340
329
  const c = await getClient();
341
- const [users, total] = await c.relation.followers(Number(a.uid), 1, Number(a.pageSize ?? 20));
330
+ const [users, total] = String(a.type ?? "followings") === "followers"
331
+ ? await c.relation.followers(Number(a.uid), 1, Number(a.pageSize ?? 20))
332
+ : await c.relation.followings(Number(a.uid), 1, Number(a.pageSize ?? 20));
342
333
  return { total, users: users.map((x) => x.toDict()) };
343
334
  },
344
335
  },
@@ -349,16 +340,6 @@ export const TOOLS = [
349
340
  parameters: { type: "object", properties: {} },
350
341
  handler: async () => (await getClient()).notify.unread(),
351
342
  },
352
- {
353
- id: "bili_notify_replies",
354
- description: "获取「回复我的」消息列表(需登录)。",
355
- parameters: { type: "object", properties: {} },
356
- handler: async () => {
357
- const c = await getClient();
358
- const [items, cursor, isEnd] = await c.notify.replies();
359
- return { isEnd, cursor, notices: items.map((x) => x.toDict()) };
360
- },
361
- },
362
343
  // ---- 收藏夹(读取) ----
363
344
  {
364
345
  id: "bili_fav_folders",
@@ -401,35 +382,24 @@ export const TOOLS = [
401
382
  },
402
383
  {
403
384
  id: "bili_favorite",
404
- description: "收藏视频到收藏夹(写操作,需登录)。",
385
+ description: "收藏或取消收藏视频到收藏夹(写操作,需登录)。",
405
386
  parameters: {
406
387
  type: "object",
407
388
  properties: {
408
389
  bvid: { type: "string" },
390
+ action: { type: "string", enum: ["add", "remove"], default: "add" },
409
391
  folderName: { type: "string" },
410
392
  },
411
393
  required: ["bvid"],
412
394
  },
413
- handler: async (a) => {
414
- const fid = await (await getClient()).interact.favorite(a.bvid, undefined, a.folderName);
415
- return { folderId: fid };
416
- },
417
- },
418
- {
419
- id: "bili_send_comment",
420
- description: "发表视频评论(写操作,需登录)。",
421
- parameters: {
422
- type: "object",
423
- properties: {
424
- bvid: { type: "string" },
425
- message: { type: "string" },
426
- },
427
- required: ["bvid", "message"],
428
- },
429
395
  handler: async (a) => {
430
396
  const c = await getClient();
431
- const comment = await c.comment.sendVideo(a.bvid, a.message);
432
- return comment.toDict();
397
+ if (String(a.action ?? "add") === "remove") {
398
+ const fid = await c.interact.unfavorite(a.bvid, undefined, a.folderName);
399
+ return { ok: true, folderId: fid };
400
+ }
401
+ const fid = await c.interact.favorite(a.bvid, undefined, a.folderName);
402
+ return { ok: true, folderId: fid };
433
403
  },
434
404
  },
435
405
  {
@@ -522,19 +492,6 @@ export const TOOLS = [
522
492
  return pages.map((p) => p.toDict());
523
493
  },
524
494
  },
525
- {
526
- id: "bili_video_subtitles",
527
- description: "列出视频可用的字幕轨(语言 / 名称 / 链接),不含正文。",
528
- parameters: {
529
- type: "object",
530
- properties: { bvid: { type: "string" } },
531
- required: ["bvid"],
532
- },
533
- handler: async (a) => {
534
- const subs = await (await getClient()).video.subtitles(a.bvid);
535
- return subs.map((s) => s.toDict());
536
- },
537
- },
538
495
  {
539
496
  id: "bili_video_playurl",
540
497
  description: "获取视频播放地址信息(清晰度、可用清晰度列表、durl 直链)。高清晰度可能需登录 / 大会员。",
@@ -668,22 +625,6 @@ export const TOOLS = [
668
625
  return { ok: true };
669
626
  },
670
627
  },
671
- {
672
- id: "bili_unfavorite",
673
- description: "把视频从收藏夹移除(写操作,需登录)。",
674
- parameters: {
675
- type: "object",
676
- properties: {
677
- bvid: { type: "string" },
678
- folderName: { type: "string", description: "收藏夹名(可选,默认取默认收藏夹)" },
679
- },
680
- required: ["bvid"],
681
- },
682
- handler: async (a) => {
683
- const fid = await (await getClient()).interact.unfavorite(a.bvid, undefined, a.folderName);
684
- return { ok: true, folderId: fid };
685
- },
686
- },
687
628
  {
688
629
  id: "bili_user_following",
689
630
  description: "查询我是否已关注某用户。",
@@ -885,21 +826,25 @@ export const TOOLS = [
885
826
  },
886
827
  // ---- 消息中心补充 ----
887
828
  {
888
- id: "bili_notify_ats",
889
- description: "获取「@我的」消息列表(需登录)。",
890
- parameters: { type: "object", properties: {} },
891
- handler: async () => {
892
- const [items, , isEnd] = await (await getClient()).notify.ats();
893
- return { isEnd, notices: items.map((x) => x.toDict()) };
829
+ id: "bili_notify_list",
830
+ description: "消息中心列表:回复我的 / @我的 / 收到的赞(需登录)。",
831
+ parameters: {
832
+ type: "object",
833
+ properties: {
834
+ type: { type: "string", enum: ["replies", "ats", "likes"], default: "replies" },
835
+ },
894
836
  },
895
- },
896
- {
897
- id: "bili_notify_likes",
898
- description: "获取「收到的赞」列表(需登录)。",
899
- parameters: { type: "object", properties: {} },
900
- handler: async () => {
901
- const [items, , isEnd] = await (await getClient()).notify.likes();
902
- return { isEnd, notices: items.map((x) => x.toDict()) };
837
+ handler: async (a) => {
838
+ const c = await getClient();
839
+ const t = String(a.type ?? "replies");
840
+ let items;
841
+ if (t === "ats")
842
+ [items] = await c.notify.ats();
843
+ else if (t === "likes")
844
+ [items] = await c.notify.likes();
845
+ else
846
+ [items] = await c.notify.replies();
847
+ return { notices: items.map((x) => x.toDict()) };
903
848
  },
904
849
  },
905
850
  // ---- 私信补充 ----
@@ -959,39 +904,36 @@ export const TOOLS = [
959
904
  parameters: { type: "object", properties: {} },
960
905
  handler: async () => (await getClient()).profile.info(),
961
906
  },
962
- {
963
- id: "bili_profile_update_sign",
964
- description: "修改我的个性签名(写操作,需登录,会进入审核)。",
965
- parameters: {
966
- type: "object",
967
- properties: { sign: { type: "string" } },
968
- required: ["sign"],
969
- },
970
- handler: async (a) => {
971
- await (await getClient()).profile.updateSign(String(a.sign ?? ""));
972
- return { ok: true };
973
- },
974
- },
975
907
  {
976
908
  id: "bili_profile_update",
977
- description: "修改我的昵称 / 性别 / 生日(写操作,需登录,至少改一项)。",
909
+ description: "修改我的昵称 / 性别 / 生日 / 签名(写操作,需登录,至少改一项)。",
978
910
  parameters: {
979
911
  type: "object",
980
912
  properties: {
981
913
  uname: { type: "string", description: "新昵称(1~20 字符)" },
982
914
  sex: { type: "string", enum: ["男", "女", "保密"] },
983
915
  birthday: { type: "string", description: "YYYY-MM-DD" },
916
+ sign: { type: "string", description: "个性签名(≤70 字)" },
984
917
  },
985
918
  },
986
919
  handler: async (a) => {
987
- await (await getClient()).profile.updateProfile(a.uname != null ? String(a.uname) : undefined, a.sex != null ? String(a.sex) : undefined, a.birthday != null ? String(a.birthday) : undefined);
920
+ const c = await getClient();
921
+ const hasProfile = a.uname != null || a.sex != null || a.birthday != null;
922
+ if (a.sign == null && !hasProfile) {
923
+ throw new Error("至少修改一项:uname / sex / birthday / sign");
924
+ }
925
+ if (a.sign != null)
926
+ await c.profile.updateSign(String(a.sign));
927
+ if (hasProfile) {
928
+ await c.profile.updateProfile(a.uname != null ? String(a.uname) : undefined, a.sex != null ? String(a.sex) : undefined, a.birthday != null ? String(a.birthday) : undefined);
929
+ }
988
930
  return { ok: true };
989
931
  },
990
932
  },
991
933
  // ---- 登录状态 ----
992
934
  {
993
935
  id: "bili_login_status",
994
- description: "查询当前是否已登录 B 站(返回用户名)。【用户想登录、或任何需登录操作报未登录(-101/CookieError)时,先调用本工具】;若返回未登录,必须立即调用 bili_gen_qr 向用户展示登录二维码图片。若此前调用过 bili_gen_qr,本工具会顺带轮询扫码状态:用户扫码并在手机确认后,返回登录成功并自动保存 Cookie。",
936
+ description: "查登录态(返回用户名)。想登录或报未登录(-101)时先调本工具;未登录则必须立即调 bili_gen_qr 展示二维码图片。刚调过 bili_gen_qr 时本工具会顺带轮询,用户确认后自动保存 Cookie。",
995
937
  parameters: { type: "object", properties: {} },
996
938
  handler: async () => {
997
939
  const c = await getClient();
@@ -1040,7 +982,7 @@ export const TOOLS = [
1040
982
  },
1041
983
  {
1042
984
  id: "bili_gen_qr",
1043
- description: "【B 站登录唯一入口】生成 B 站扫码登录二维码图片。调用后必须把返回的二维码图片展示给用户扫码(用 qrDataUrl / qrImageUrl / qrImagePath 渲染成图片),不要只给链接、不要让用户手动跑命令。返回:url(二维码内容链接)、qrcodeKey(轮询用)、qrDataUrl(base64 data URL)、qrImageUrl(file:// 链接)、qrImagePath(本地 PNG 绝对路径)。用户扫码确认后,调用 bili_login_status 完成登录并保存 Cookie。",
985
+ description: "【登录唯一入口】生成扫码登录二维码。必须把返回的二维码图片展示给用户扫(用 qrDataUrl / qrImageUrl / qrImagePath 渲染成图片),不要只给链接。返回 url / qrcodeKey / qrDataUrl / qrImageUrl / qrImagePath。用户确认后调 bili_login_status 完成登录。",
1044
986
  parameters: { type: "object", properties: {} },
1045
987
  handler: async () => {
1046
988
  const c = await getClient();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-shlr-bilitoolkit",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "description": "深海猎人专用B站接口工具箱 dsh 插件。",
5
5
  "type": "module",
6
6
  "main": "dist/dsh.js",