@zeppeli777/tools-doubao 0.0.0-stage → 0.1.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.
- package/LICENSE +21 -0
- package/README.md +105 -2
- package/config.example.json +18 -0
- package/index.mjs +204 -0
- package/package.json +38 -4
- package/scripts/login.mjs +34 -0
- package/scripts/test-offline.mjs +313 -0
- package/scripts/test-protocol.mjs +94 -0
- package/src/accounts.mjs +58 -0
- package/src/client.mjs +521 -0
- package/src/config.mjs +95 -0
- package/src/download.mjs +56 -0
- package/src/http.mjs +60 -0
- package/src/png.mjs +164 -0
- package/src/qlogin.mjs +240 -0
- package/src/upload.mjs +82 -0
- package/src/watermark.mjs +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Zeppeli777
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,106 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 豆包生图中转插件(tools-doubao)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Magpie 工具插件:让**任意 Agent** 通过**你自己的豆包账号**(网页版登录态,与豆包桌面端同账号同后端)生成图片。工具经 Magpie 内置 MCP 服务器 `/mcp/magpie/tools` 注册进所有智能体,工具名为 `doubao-image-plugin_generate_image` / `doubao-image-plugin_check_login`。
|
|
4
|
+
|
|
5
|
+
- **文生图**:prompt + 画幅比例(1:1 / 16:9 / 9:16 / 4:3 / 3:4 / auto)+ 模型档位(免费档 Seedream 5.0 Flash / 4.5 / 4.0)
|
|
6
|
+
- **图生图**:传本地图片路径或图片 URL 作参考图
|
|
7
|
+
- **多账号轮换**:登录失效自动换号;图片自动落盘并返回绝对路径
|
|
8
|
+
|
|
9
|
+
原理:用豆包网页版的 `sessionid` Cookie 免签名直调豆包内部接口(优先 web `chat_ability` 新协议,失败自动降级旧 `samantha` 客户端通道;图生图固定走 samantha 通道)。逆向接口属个人自用性质,**不稳定且存在账号风控风险**,商用请走[火山引擎官方 API](https://www.volcengine.com/product/doubao)。
|
|
10
|
+
|
|
11
|
+
## 安装
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
# 方式一:npm(推荐)
|
|
15
|
+
magpie plugin add @zeppeli777/tools-doubao
|
|
16
|
+
|
|
17
|
+
# 方式二:从 GitHub 本地克隆后文件夹就地加载
|
|
18
|
+
git clone https://github.com/Zeppeli777/doubao-image-plugin.git
|
|
19
|
+
magpie plugin add ./doubao-image-plugin
|
|
20
|
+
|
|
21
|
+
magpie plugin --json # 确认加载无 plugin: 错误
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
装完后**新开的 Agent 会话**才能看到新工具。工具名为 `doubao-image-plugin_generate_image`(npm 包安装时插件 id 取包名,为 `doubao_generate_image`)。
|
|
25
|
+
|
|
26
|
+
## 登录(获取豆包登录态)
|
|
27
|
+
|
|
28
|
+
任选其一:
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
magpie plugin login doubao-image-plugin # 或 npm 安装后的插件 id
|
|
32
|
+
node scripts/login.mjs # 本地克隆时;弹出二维码,手机豆包 App 扫码
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
弹出二维码 → 手机豆包 App 扫码并确认 → 登录态自动写入插件目录 `config.json`。
|
|
36
|
+
|
|
37
|
+
## 配置凭证(三选一)
|
|
38
|
+
|
|
39
|
+
1. **扫码登录**(见上节,推荐)
|
|
40
|
+
2. **手动**:复制 `config.example.json` 为 `config.json`,填入 `sessionid`(浏览器登录 doubao.com → F12 → Application → Cookies → `https://www.doubao.com` → 复制 `sessionid`;`ttwid`、`passport_csrf_token` 可选,带上更稳)
|
|
41
|
+
3. **环境变量**:`DOUBAO_ACCOUNTS="主号:sessionid=xxx,ttwid=yyy;小号:sessionid=zzz"`
|
|
42
|
+
|
|
43
|
+
Cookie 有效期约 30 天,过期后重新扫码即可。多账号把 `accounts` 数组填多条,登录失效自动轮换。
|
|
44
|
+
|
|
45
|
+
## 使用
|
|
46
|
+
|
|
47
|
+
Agent 里直接说"用豆包画一张…",Agent 会调用工具。手动验证不走 Magpie:
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
node scripts/test-protocol.mjs --check # 各账号登录态
|
|
51
|
+
node scripts/test-protocol.mjs "一只柴犬在樱花树下" --ratio 16:9
|
|
52
|
+
node scripts/test-protocol.mjs "改成赛博朋克风格" --ref 照片.jpg # 图生图
|
|
53
|
+
node scripts/test-protocol.mjs "一朵红玫瑰特写" --wm # 文生图 + 去水印
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
生成的图默认保存在调用方工作目录的 `generated-images/`(可在 config.json 用 `outDir` 改)。
|
|
57
|
+
|
|
58
|
+
## 配置项
|
|
59
|
+
|
|
60
|
+
| 字段 | 默认 | 说明 |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| `accounts` | `[]` | `{name, sessionid, ttwid?, passport_csrf_token?}` 数组 |
|
|
63
|
+
| `model` | `Seedream 5.0 Flash` | 默认模型档位;Pro 档需订阅,会自动回退免费档 |
|
|
64
|
+
| `ratio` | `1:1` | 默认画幅比例 |
|
|
65
|
+
| `channel` | `auto` | `web`=chat_ability 新协议(模型/比例精确);`pc`=samantha 旧通道(稳定、支持图生图);`auto`=先 web 后 pc |
|
|
66
|
+
| `outDir` | 空 | 图片保存目录,空=跟随调用方工作目录 |
|
|
67
|
+
| `timeoutSeconds` | `180` | 单次请求超时 |
|
|
68
|
+
| `ratioHint` | `true` | web 通道把比例追加进自然语言(web 端 ratio 字段实测不生效) |
|
|
69
|
+
| `removeWatermark` | `false` | 去右下角"豆包AI生成"水印(扩图裁剪法,见下) |
|
|
70
|
+
| `watermarkExtend` | `"1/4"` | 去水印的扩图比例,裁剪保留 1/(1+该值) |
|
|
71
|
+
|
|
72
|
+
## 去水印(扩图裁剪法)
|
|
73
|
+
|
|
74
|
+
开启 `removeWatermark: true`(或调用时传 `remove_watermark: true`)后,文生图会自动多走一步:
|
|
75
|
+
|
|
76
|
+
1. 正常生成图片(右下角带"豆包AI生成"标)
|
|
77
|
+
2. 以该图为参考图走图生图,提示词自动追加 **"往下扩图四分之一"** —— 模型重新演绎画面,旧水印随之消失,而新一轮水印落在底部新增区域
|
|
78
|
+
3. 下载扩图结果,**裁掉底部扩出部分(保留上方 80%)**,覆盖保存
|
|
79
|
+
|
|
80
|
+
代价与注意:
|
|
81
|
+
|
|
82
|
+
- 多花一次生成额度(一张图 = 两次生成)
|
|
83
|
+
- 画面边缘会被模型重新演绎,构图略有变化
|
|
84
|
+
- 扩图比例可用 `watermarkExtend` 调整(如 `"1/5"` → 保留 5/6)
|
|
85
|
+
- 仅对文生图生效;图生图保留用户原图语义,不自动扩图
|
|
86
|
+
- 扩图结果若非 PNG(目前实测均为 PNG)会跳过裁剪并保留原图,不会损坏文件
|
|
87
|
+
|
|
88
|
+
## 故障排查
|
|
89
|
+
|
|
90
|
+
| 现象 | 处理 |
|
|
91
|
+
|---|---|
|
|
92
|
+
| 提示登录态失效 | `sessionid` 过期(约 30 天),重新复制;多账号时会自动轮换 |
|
|
93
|
+
| `710022002 / 710022004` 风控 | 等约 30 分钟冷却 / 换网络(手机热点最快)/ 浏览器过一次滑块验证 |
|
|
94
|
+
| 提示需要订阅 | 免费账号只能用免费档(Seedream 5.0 Flash / 4.5 / 4.0),点名 Pro 档会自动回退 |
|
|
95
|
+
| `msToken` 相关风控 | 本插件默认不带 msToken(留空最安全),无需配置 |
|
|
96
|
+
| 生图很慢 | 单张通常数秒到一两分钟;超时可调大 `timeoutSeconds` |
|
|
97
|
+
|
|
98
|
+
## 参考实现
|
|
99
|
+
|
|
100
|
+
协议字段移植自开源抓包项目(Apache-2.0):
|
|
101
|
+
|
|
102
|
+
- [wangchuxiaoji-oss/doubao2api](https://github.com/wangchuxiaoji-oss/doubao2api) —— samantha 通道、上传两步流、SSE 解析
|
|
103
|
+
- [sanshidi/doubao-web-mcp](https://glama.ai/mcp/servers/sanshidi/doubao-web-mcp) —— web chat_ability 协议笔记(2026-10-19 抓包结论)
|
|
104
|
+
- [magpie-community/plugins](https://github.com/magpie-community/plugins) —— Magpie 工具插件格式样板
|
|
105
|
+
|
|
106
|
+
本仓库 `reference/` 目录存有移植时对照的源码快照(已 gitignore,仅作参考,不参与运行)。
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"accounts": [
|
|
3
|
+
{
|
|
4
|
+
"name": "主号",
|
|
5
|
+
"sessionid": "粘贴 www.doubao.com F12 → Application → Cookies → sessionid 的值",
|
|
6
|
+
"ttwid": "可选,同页面 Cookie 里的 ttwid,带上更稳",
|
|
7
|
+
"passport_csrf_token": "可选,同页面 Cookie 里的 passport_csrf_token"
|
|
8
|
+
}
|
|
9
|
+
],
|
|
10
|
+
"model": "Seedream 5.0 Flash",
|
|
11
|
+
"ratio": "1:1",
|
|
12
|
+
"channel": "auto",
|
|
13
|
+
"outDir": "",
|
|
14
|
+
"timeoutSeconds": 180,
|
|
15
|
+
"ratioHint": true,
|
|
16
|
+
"removeWatermark": false,
|
|
17
|
+
"watermarkExtend": "1/4"
|
|
18
|
+
}
|
package/index.mjs
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
// Magpie 工具插件入口:把用户自己的豆包账号(网页版登录态,与桌面端同账号同后端)
|
|
2
|
+
// 暴露为所有 Agent 可用的生图工具。经 /mcp/magpie/tools 注册为 <插件>_<工具>。
|
|
3
|
+
//
|
|
4
|
+
// 工具:
|
|
5
|
+
// generate_image — 文生图 / 图生图(参考图),可指定比例与免费档模型
|
|
6
|
+
// check_login — 各账号登录态自检
|
|
7
|
+
//
|
|
8
|
+
// 凭证配置(任选其一,优先级从高到低):
|
|
9
|
+
// 1. env DOUBAO_IMAGE_CONFIG 指向的 JSON 文件
|
|
10
|
+
// 2. 本插件目录下的 config.json(参考 config.example.json)
|
|
11
|
+
// 3. env DOUBAO_ACCOUNTS="主号:sessionid=xxx,ttwid=yyy;小号:sessionid=zzz"
|
|
12
|
+
// 4. Magpie options(magpie plugin options tools-doubao '<json>')
|
|
13
|
+
import path from "node:path";
|
|
14
|
+
import { loadConfig } from "./src/config.mjs";
|
|
15
|
+
import { generate, checkLogin, FREE_MODELS, DEFAULT_MODEL, RATIOS } from "./src/client.mjs";
|
|
16
|
+
import { DoubaoError } from "./src/http.mjs";
|
|
17
|
+
import { downloadImage, fileStamp, fileSize } from "./src/download.mjs";
|
|
18
|
+
import { qrLogin, saveQrPng, openInViewer, writeAccountToConfig } from "./src/qlogin.mjs";
|
|
19
|
+
import { removeWatermarkByExtend } from "./src/watermark.mjs";
|
|
20
|
+
|
|
21
|
+
// 本地文件夹加载时 Magpie 用目录名作为插件 id(工具前缀也来自它)
|
|
22
|
+
const PLUGIN_ID = "doubao-image-plugin";
|
|
23
|
+
|
|
24
|
+
function resolveOutDir(args, context, cfg) {
|
|
25
|
+
if (args.out_dir) return String(args.out_dir);
|
|
26
|
+
if (cfg.outDir) return String(cfg.outDir);
|
|
27
|
+
const base = context?.directory || context?.worktree || process.cwd();
|
|
28
|
+
return path.join(String(base), "generated-images");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function failMessage(err) {
|
|
32
|
+
if (err instanceof DoubaoError) {
|
|
33
|
+
return `豆包生图失败:${err.message}${err.hint ? `\n建议:${err.hint}` : ""}`;
|
|
34
|
+
}
|
|
35
|
+
return `豆包生图失败:${err?.message || String(err)}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const DoubaoTools = async (input, options) => {
|
|
39
|
+
const pluginOptions = options && typeof options === "object" ? options : {};
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
tool: {
|
|
43
|
+
generate_image: {
|
|
44
|
+
description:
|
|
45
|
+
"用用户自己的豆包账号生成图片(文生图/图生图,免费额度,Seedream 系列)。" +
|
|
46
|
+
"当用户想要画图、生成图片、出插画/海报/示意图,且希望走豆包(豆包桌面端/网页版同账号)时使用。" +
|
|
47
|
+
"输入画面描述 prompt(中英文皆可),可选画幅比例与模型档位;" +
|
|
48
|
+
"给出参考图 ref_images 时按参考图风格/内容出图(图生图)。" +
|
|
49
|
+
"返回已保存到本地的图片文件路径(含 Markdown 引用)。",
|
|
50
|
+
args: {
|
|
51
|
+
type: "object",
|
|
52
|
+
properties: {
|
|
53
|
+
prompt: {
|
|
54
|
+
type: "string",
|
|
55
|
+
description: "画面描述,越具体越好(主体/场景/风格/光线/构图)",
|
|
56
|
+
},
|
|
57
|
+
ratio: {
|
|
58
|
+
type: "string",
|
|
59
|
+
enum: RATIOS,
|
|
60
|
+
description: `画幅比例,默认 1:1,auto 交给豆包决定`,
|
|
61
|
+
},
|
|
62
|
+
model: {
|
|
63
|
+
type: "string",
|
|
64
|
+
description:
|
|
65
|
+
`图片模型档位,默认 ${DEFAULT_MODEL}(免费档:${FREE_MODELS.join(" / ")})。` +
|
|
66
|
+
"Pro 档需要豆包订阅,免费账号会自动回退免费档",
|
|
67
|
+
},
|
|
68
|
+
ref_images: {
|
|
69
|
+
type: "array",
|
|
70
|
+
items: { type: "string" },
|
|
71
|
+
description: "参考图(图生图):本地文件路径或图片 URL,可多张",
|
|
72
|
+
},
|
|
73
|
+
out_dir: {
|
|
74
|
+
type: "string",
|
|
75
|
+
description: "图片保存目录,默认当前项目下的 generated-images/",
|
|
76
|
+
},
|
|
77
|
+
remove_watermark: {
|
|
78
|
+
type: "boolean",
|
|
79
|
+
description:
|
|
80
|
+
"去除右下角“豆包AI生成”水印:生成后自动以原图为参考追加“往下扩图四分之一”再生成一次," +
|
|
81
|
+
"并把底部扩出部分裁掉(多花一次生成额度,画面边缘会被重新演绎)。" +
|
|
82
|
+
"不传则跟随配置 removeWatermark(默认关闭)",
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
required: ["prompt"],
|
|
86
|
+
},
|
|
87
|
+
async execute(args, context) {
|
|
88
|
+
const cfg = loadConfig(pluginOptions);
|
|
89
|
+
const removeWm =
|
|
90
|
+
args.remove_watermark !== undefined
|
|
91
|
+
? Boolean(args.remove_watermark)
|
|
92
|
+
: Boolean(cfg.removeWatermark);
|
|
93
|
+
try {
|
|
94
|
+
const genOpts = {
|
|
95
|
+
prompt: String(args.prompt || ""),
|
|
96
|
+
ratio: args.ratio,
|
|
97
|
+
model: args.model,
|
|
98
|
+
refImages: args.ref_images,
|
|
99
|
+
account: args.account,
|
|
100
|
+
};
|
|
101
|
+
const result = await generate(cfg, genOpts);
|
|
102
|
+
const dir = resolveOutDir(args, context, cfg);
|
|
103
|
+
const stamp = fileStamp();
|
|
104
|
+
const saved = [];
|
|
105
|
+
for (let i = 0; i < result.images.length; i++) {
|
|
106
|
+
const { file, bytes } = await downloadImage(
|
|
107
|
+
result.images[i].url,
|
|
108
|
+
dir,
|
|
109
|
+
`doubao-${stamp}-${i + 1}`,
|
|
110
|
+
);
|
|
111
|
+
saved.push({ file, bytes });
|
|
112
|
+
}
|
|
113
|
+
// 去水印(扩图裁剪法):仅对文生图生效;图生图保留用户原图语义,不自动扩图
|
|
114
|
+
let wmNote = "";
|
|
115
|
+
if (removeWm && !args.ref_images?.length) {
|
|
116
|
+
for (const s of saved) {
|
|
117
|
+
try {
|
|
118
|
+
const r = await removeWatermarkByExtend(cfg, {
|
|
119
|
+
file: s.file,
|
|
120
|
+
prompt: genOpts.prompt,
|
|
121
|
+
ratio: args.ratio,
|
|
122
|
+
account: args.account,
|
|
123
|
+
});
|
|
124
|
+
wmNote += `\n[去水印] 已扩图${Math.round(r.extendFrac * 100)}%并裁剪:${r.width}×${r.height}(扩图前 ${r.width}×${r.origHeight})`;
|
|
125
|
+
} catch (err) {
|
|
126
|
+
wmNote += `\n[去水印] 失败,已保留带水印原图:${err?.message || err}`;
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
for (const s of saved) s.bytes = await fileSize(s.file);
|
|
132
|
+
const lines = saved.map(
|
|
133
|
+
(s, i) => `${i + 1}. ${s.file}(${(s.bytes / 1024).toFixed(0)} KB)`,
|
|
134
|
+
);
|
|
135
|
+
return (
|
|
136
|
+
`已通过豆包生成 ${saved.length} 张图片。\n` +
|
|
137
|
+
`账号 ${result.account} · ${result.channel === "web" ? "网页通道" : "客户端通道"}${
|
|
138
|
+
result.conversationId ? ` · 会话 ${result.conversationId}` : ""
|
|
139
|
+
}${result.text ? `\n${result.text.slice(0, 200)}` : ""}${wmNote}\n` +
|
|
140
|
+
`文件:\n${lines.join("\n")}\n\n` +
|
|
141
|
+
saved.map((s, i) => `[生成图 ${i + 1}](${s.file})`).join(" · ")
|
|
142
|
+
);
|
|
143
|
+
} catch (err) {
|
|
144
|
+
return failMessage(err);
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
check_login: {
|
|
149
|
+
description:
|
|
150
|
+
"检查豆包生图插件的账号配置与登录态:列出每个配置账号是否存活。生图报登录失效/风控时先跑这个。",
|
|
151
|
+
args: { type: "object", properties: {} },
|
|
152
|
+
async execute() {
|
|
153
|
+
const cfg = loadConfig(pluginOptions);
|
|
154
|
+
if (!cfg.accounts.length) {
|
|
155
|
+
return (
|
|
156
|
+
"尚未配置任何豆包账号。请让用户运行 `node scripts/login.mjs` 扫码登录,或把 doubao.com 网页版 Cookie 里的 sessionid 写入插件目录的 " +
|
|
157
|
+
"config.json(参考 config.example.json),或设置环境变量 DOUBAO_ACCOUNTS=\"主号:sessionid=xxx\"。"
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
const statuses = await checkLogin(cfg);
|
|
161
|
+
const lines = statuses.map((s) =>
|
|
162
|
+
s.live
|
|
163
|
+
? `✅ ${s.name} — 登录有效${s.userName ? `(${s.userName})` : ""}${s.userId ? `,user_id=${s.userId}` : ""}`
|
|
164
|
+
: `❌ ${s.name} — 登录无效${s.error ? `(${s.error})` : ",请重新扫码登录或更新 sessionid"}`,
|
|
165
|
+
);
|
|
166
|
+
return `豆包账号共 ${statuses.length} 个:\n${lines.join("\n")}`;
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
// 扫码登录:magpie plugin login doubao-image-plugin 时触发。
|
|
171
|
+
// 豆包没有 OAuth,采用与 doubao2api 相同的二维码扫码流:
|
|
172
|
+
// 弹出二维码图片 → 用户手机豆包 App 扫码确认 → 轮询拿到 sessionid 写入 config.json。
|
|
173
|
+
auth: {
|
|
174
|
+
provider: PLUGIN_ID,
|
|
175
|
+
methods: [
|
|
176
|
+
{
|
|
177
|
+
type: "oauth",
|
|
178
|
+
label: "豆包扫码登录",
|
|
179
|
+
authorize: async () => ({
|
|
180
|
+
url: "",
|
|
181
|
+
method: "auto",
|
|
182
|
+
instructions:
|
|
183
|
+
"将弹出豆包登录二维码(系统看图程序)。请用手机豆包 App 扫码并在手机上确认,登录态会自动写入插件 config.json。",
|
|
184
|
+
callback: async () => {
|
|
185
|
+
try {
|
|
186
|
+
const result = await qrLogin({
|
|
187
|
+
onStatus: (stage, detail) => {
|
|
188
|
+
if (stage === "ready" && Buffer.isBuffer(detail)) openInViewer(saveQrPng(detail));
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
const file = writeAccountToConfig(result, "扫码登录");
|
|
192
|
+
return { type: "success", accountId: `豆包 · ${result.sessionid.slice(0, 6)}…`, configFile: file };
|
|
193
|
+
} catch (err) {
|
|
194
|
+
return { type: "failed", error: `豆包扫码登录失败:${err?.message || err}` };
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
}),
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
export default DoubaoTools;
|
package/package.json
CHANGED
|
@@ -1,6 +1,40 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeppeli777/tools-doubao",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Doubao (豆包) image generation relay for Magpie: text-to-image and image-to-image with the user's own Doubao web session (QR-code sign-in), served to every agent via /mcp/magpie/tools",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./index.mjs",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"author": "Zeppeli777",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/Zeppeli777/doubao-image-plugin.git"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/Zeppeli777/doubao-image-plugin#readme",
|
|
14
|
+
"bugs": "https://github.com/Zeppeli777/doubao-image-plugin/issues",
|
|
15
|
+
"files": [
|
|
16
|
+
"index.mjs",
|
|
17
|
+
"src",
|
|
18
|
+
"scripts",
|
|
19
|
+
"config.example.json",
|
|
20
|
+
"README.md",
|
|
21
|
+
"LICENSE"
|
|
22
|
+
],
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"magpie",
|
|
28
|
+
"magpie-tools",
|
|
29
|
+
"magpie-plugin",
|
|
30
|
+
"opencode-plugin",
|
|
31
|
+
"mcp",
|
|
32
|
+
"doubao",
|
|
33
|
+
"seedream",
|
|
34
|
+
"image-generation",
|
|
35
|
+
"text-to-image"
|
|
36
|
+
],
|
|
37
|
+
"magpie": {
|
|
38
|
+
"name": "豆包生图中转"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// 扫码登录:弹出豆包登录二维码(终端 ASCII + 系统看图程序),手机确认后自动把
|
|
3
|
+
// 登录态写入插件目录 config.json。等价于其他反代项目的 "QR 扫码登录"。
|
|
4
|
+
// node scripts/login.mjs [--name 主号]
|
|
5
|
+
import { qrLogin, qrPngToAscii, saveQrPng, openInViewer, writeAccountToConfig } from "../src/qlogin.mjs";
|
|
6
|
+
|
|
7
|
+
const argv = process.argv.slice(2);
|
|
8
|
+
const nameIdx = argv.indexOf("--name");
|
|
9
|
+
const accountName = nameIdx >= 0 ? argv[nameIdx + 1] : "扫码登录";
|
|
10
|
+
|
|
11
|
+
console.log("正在获取豆包登录二维码…");
|
|
12
|
+
try {
|
|
13
|
+
const result = await qrLogin({
|
|
14
|
+
onStatus: (stage, detail) => {
|
|
15
|
+
if (stage === "ready" && Buffer.isBuffer(detail)) {
|
|
16
|
+
const file = saveQrPng(detail);
|
|
17
|
+
const opened = openInViewer(file);
|
|
18
|
+
console.log(`二维码已保存: ${file}${opened ? "(已用看图程序打开)" : "(请手动打开)"}\n`);
|
|
19
|
+
const ascii = qrPngToAscii(detail);
|
|
20
|
+
if (ascii) console.log(ascii);
|
|
21
|
+
console.log("请用手机豆包 App 扫描二维码,并在手机上确认登录…");
|
|
22
|
+
} else if (stage === "scanned") {
|
|
23
|
+
console.log("已扫码,请在手机上点「确认」…");
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
const file = writeAccountToConfig(result, accountName);
|
|
28
|
+
console.log(`\n✅ 登录成功,账号「${accountName}」已写入 ${file}`);
|
|
29
|
+
console.log(`sessionid: ${result.sessionid.slice(0, 10)}…(完整值已保存,不会打印)`);
|
|
30
|
+
console.log("现在可以直接在任意 Agent 里调用 doubao_generate_image 了。");
|
|
31
|
+
} catch (err) {
|
|
32
|
+
console.error(`\n❌ 登录失败: ${err.message}`);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|