dsh-shlr-bilitoolkit 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dsh.d.ts +7 -0
- package/dist/dsh.js +77 -53
- package/package.json +1 -1
package/dist/dsh.d.ts
CHANGED
|
@@ -22,6 +22,13 @@ export interface DshTool {
|
|
|
22
22
|
presentationMeta?: (args: any, value: any) => unknown;
|
|
23
23
|
presentResult?: (args: any, result: any) => any;
|
|
24
24
|
}
|
|
25
|
+
/** 把任意返回值规整成「可无损序列化」的 JSON。
|
|
26
|
+
*
|
|
27
|
+
* dsh 要求工具的规范值必须能无损 JSON 序列化:对象里不能带 `undefined`
|
|
28
|
+
* (`JSON.stringify` 会静默丢键,导致 round-trip 丢失),也不能有 NaN/Infinity。
|
|
29
|
+
* 这里统一处理:丢 undefined、NaN/Infinity 转 null、bigint 转字符串。
|
|
30
|
+
*/
|
|
31
|
+
export declare function toLosslessJson(value: unknown): unknown;
|
|
25
32
|
export declare const TOOLS: DshTool[];
|
|
26
33
|
export declare function apply(ctx: any): void;
|
|
27
34
|
export * from "./index.js";
|
package/dist/dsh.js
CHANGED
|
@@ -11,16 +11,16 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
13
13
|
import QRCode from "qrcode";
|
|
14
|
+
import { writeFileSync } from "node:fs";
|
|
15
|
+
import { resolve } from "node:path";
|
|
16
|
+
import { pathToFileURL } from "node:url";
|
|
14
17
|
import { BiliClient } from "./client.js";
|
|
15
18
|
import { Collection, Page, PlayInfo, Subtitle, User, Video } from "./models.js";
|
|
16
19
|
import { QR_STATUS_EXPIRED, QR_STATUS_SCANNED, QR_STATUS_SUCCESS, } from "./apis/auth.js";
|
|
17
20
|
// 插件入口必须导出的字段(Cordis 据此识别并注入相应服务)
|
|
18
21
|
export const name = "bili-toolkit";
|
|
19
|
-
|
|
20
|
-
export const inject = ["tools", "attachments"];
|
|
22
|
+
export const inject = ["tools"];
|
|
21
23
|
let cached = null;
|
|
22
|
-
// 附件服务(apply 时从 ctx 捕获);缺失时二维码退化为文本
|
|
23
|
-
let attachmentStore = null;
|
|
24
24
|
// 最近一次生成、等待扫码确认的二维码 key(登录完成链路用)
|
|
25
25
|
let pendingQrKey = null;
|
|
26
26
|
async function getClient() {
|
|
@@ -64,6 +64,34 @@ function toDshParameters(schema) {
|
|
|
64
64
|
function textOf(value) {
|
|
65
65
|
return typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
66
66
|
}
|
|
67
|
+
/** 把任意返回值规整成「可无损序列化」的 JSON。
|
|
68
|
+
*
|
|
69
|
+
* dsh 要求工具的规范值必须能无损 JSON 序列化:对象里不能带 `undefined`
|
|
70
|
+
* (`JSON.stringify` 会静默丢键,导致 round-trip 丢失),也不能有 NaN/Infinity。
|
|
71
|
+
* 这里统一处理:丢 undefined、NaN/Infinity 转 null、bigint 转字符串。
|
|
72
|
+
*/
|
|
73
|
+
export function toLosslessJson(value) {
|
|
74
|
+
if (typeof value === "number")
|
|
75
|
+
return Number.isFinite(value) ? value : null;
|
|
76
|
+
if (typeof value === "bigint")
|
|
77
|
+
return value.toString();
|
|
78
|
+
if (Array.isArray(value)) {
|
|
79
|
+
return value.map((v) => {
|
|
80
|
+
const sv = toLosslessJson(v);
|
|
81
|
+
return sv === undefined ? null : sv;
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
if (value && typeof value === "object") {
|
|
85
|
+
const out = {};
|
|
86
|
+
for (const [k, v] of Object.entries(value)) {
|
|
87
|
+
const sv = toLosslessJson(v);
|
|
88
|
+
if (sv !== undefined)
|
|
89
|
+
out[k] = sv;
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
return value; // string / boolean / null / undefined
|
|
94
|
+
}
|
|
67
95
|
export const TOOLS = [
|
|
68
96
|
{
|
|
69
97
|
id: "bili_recommend",
|
|
@@ -468,14 +496,15 @@ export const TOOLS = [
|
|
|
468
496
|
},
|
|
469
497
|
{
|
|
470
498
|
id: "bili_gen_qr",
|
|
471
|
-
description: "生成 B
|
|
499
|
+
description: "生成 B 站扫码登录二维码图片。返回二维码内容链接 url、轮询用的 qrcodeKey,以及可供 dsh 自行获取/渲染的二维码图片:qrDataUrl(base64 data URL)、qrImageUrl(file:// 链接)、qrImagePath(本地 PNG 绝对路径)。用户扫码确认后,调用 bili_login_status 完成登录并保存 Cookie。",
|
|
472
500
|
parameters: { type: "object", properties: {} },
|
|
473
501
|
handler: async () => {
|
|
474
502
|
const c = await getClient();
|
|
475
503
|
const [url, key] = await c.auth.generate();
|
|
476
504
|
pendingQrKey = key;
|
|
477
|
-
//
|
|
478
|
-
let
|
|
505
|
+
// 渲染成 PNG,并给出多种「dsh 可自行获取图片」的形式
|
|
506
|
+
let qrImagePath = null;
|
|
507
|
+
let qrDataUrl = null;
|
|
479
508
|
try {
|
|
480
509
|
const png = await QRCode.toBuffer(url, {
|
|
481
510
|
type: "png",
|
|
@@ -483,62 +512,58 @@ export const TOOLS = [
|
|
|
483
512
|
margin: 2,
|
|
484
513
|
width: 320,
|
|
485
514
|
});
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
mediaType: "image/png",
|
|
490
|
-
name: "bili-login-qr.png",
|
|
491
|
-
});
|
|
492
|
-
}
|
|
515
|
+
qrDataUrl = `data:image/png;base64,${png.toString("base64")}`;
|
|
516
|
+
qrImagePath = resolve(process.cwd(), "bili_qrcode.png");
|
|
517
|
+
writeFileSync(qrImagePath, png);
|
|
493
518
|
}
|
|
494
519
|
catch {
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
// ascii 仅作为附件不可用时的兜底
|
|
498
|
-
let ascii = null;
|
|
499
|
-
if (!qrImage) {
|
|
500
|
-
try {
|
|
501
|
-
ascii = await QRCode.toString(url, { type: "utf8" });
|
|
502
|
-
}
|
|
503
|
-
catch {
|
|
504
|
-
ascii = null;
|
|
505
|
-
}
|
|
520
|
+
qrImagePath = null;
|
|
521
|
+
qrDataUrl = null;
|
|
506
522
|
}
|
|
507
|
-
return {
|
|
523
|
+
return {
|
|
524
|
+
url,
|
|
525
|
+
qrcodeKey: key,
|
|
526
|
+
qrImagePath,
|
|
527
|
+
qrImageUrl: qrImagePath ? pathToFileURL(qrImagePath).href : null,
|
|
528
|
+
qrDataUrl,
|
|
529
|
+
};
|
|
508
530
|
},
|
|
509
|
-
//
|
|
510
|
-
render: (_args, value) =>
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
|
|
531
|
+
// 面向模型:简洁文本 + 一个 file:// 图片引用(小,不塞 base64)
|
|
532
|
+
render: (_args, value) => {
|
|
533
|
+
const lines = ["已生成 B 站登录二维码,请展示给用户扫码:"];
|
|
534
|
+
if (value?.qrImageUrl)
|
|
535
|
+
lines.push(``);
|
|
536
|
+
else if (value?.qrDataUrl)
|
|
537
|
+
lines.push(``);
|
|
538
|
+
lines.push(`二维码链接:${value?.url ?? ""}`);
|
|
539
|
+
lines.push(`qrcodeKey=${value?.qrcodeKey ?? ""}`);
|
|
540
|
+
lines.push("用户扫码并在手机确认后,调用 bili_login_status 完成登录。");
|
|
541
|
+
return [{ type: "text", text: lines.join("\n") }];
|
|
542
|
+
},
|
|
543
|
+
// 把图片引用透传给 presentResult(仅 UI 用)
|
|
519
544
|
presentationMeta: (_args, value) => ({
|
|
520
|
-
|
|
521
|
-
|
|
545
|
+
qrDataUrl: value?.qrDataUrl ?? null,
|
|
546
|
+
qrImageUrl: value?.qrImageUrl ?? null,
|
|
547
|
+
qrImagePath: value?.qrImagePath ?? null,
|
|
522
548
|
url: value?.url ?? null,
|
|
523
549
|
}),
|
|
524
|
-
// UI
|
|
550
|
+
// UI 呈现:用自包含的 base64 data URL 以 markdown 图片渲染,dsh 自行获取
|
|
525
551
|
presentResult: (_args, result) => {
|
|
526
552
|
const meta = (result?.meta ?? {});
|
|
527
553
|
const content = [];
|
|
528
|
-
if (meta.
|
|
529
|
-
content.push({ type: "
|
|
530
|
-
content.push({
|
|
531
|
-
type: "text",
|
|
532
|
-
text: "请用「哔哩哔哩」App 扫描上方二维码,并在手机上确认登录。",
|
|
533
|
-
});
|
|
554
|
+
if (meta.qrDataUrl) {
|
|
555
|
+
content.push({ type: "text", text: `` });
|
|
534
556
|
}
|
|
535
|
-
else if (meta.
|
|
536
|
-
content.push({ type: "text", text:
|
|
537
|
-
content.push({ type: "text", text: `二维码链接:${meta.url ?? ""}` });
|
|
557
|
+
else if (meta.qrImageUrl) {
|
|
558
|
+
content.push({ type: "text", text: `` });
|
|
538
559
|
}
|
|
539
|
-
|
|
540
|
-
content.push({ type: "text", text:
|
|
560
|
+
if (meta.qrImagePath) {
|
|
561
|
+
content.push({ type: "text", text: `二维码图片已保存到:${meta.qrImagePath}` });
|
|
541
562
|
}
|
|
563
|
+
content.push({
|
|
564
|
+
type: "text",
|
|
565
|
+
text: "请用「哔哩哔哩」App 扫描上方二维码,并在手机上确认登录。",
|
|
566
|
+
});
|
|
542
567
|
return { card: "generic", title: "扫码登录 B 站", content };
|
|
543
568
|
},
|
|
544
569
|
},
|
|
@@ -555,8 +580,6 @@ export const TOOLS = [
|
|
|
555
580
|
},
|
|
556
581
|
];
|
|
557
582
|
export function apply(ctx) {
|
|
558
|
-
// 捕获附件服务,供 bili_gen_qr 把二维码登记成可在 UI 渲染的图片
|
|
559
|
-
attachmentStore = ctx?.attachments ?? null;
|
|
560
583
|
for (const tool of TOOLS) {
|
|
561
584
|
const output = {
|
|
562
585
|
schema: { type: "object", additionalProperties: true },
|
|
@@ -572,7 +595,8 @@ export function apply(ctx) {
|
|
|
572
595
|
parameters: toDshParameters(tool.parameters),
|
|
573
596
|
output,
|
|
574
597
|
async execute(args) {
|
|
575
|
-
|
|
598
|
+
// 统一规整为无损 JSON,避免 toDict 里的 undefined / NaN 触发序列化错误
|
|
599
|
+
return toLosslessJson(await tool.handler(args));
|
|
576
600
|
},
|
|
577
601
|
};
|
|
578
602
|
if (tool.presentResult) {
|