Skip to content

Commit b3dc58f

Browse files
committed
feat(adapter): 新增五大浏览器 LLM 站点 typed 客户端——kimi/qwen/doubao/grok/yuanbao
对齐 opencli 上游 cli-manifest.json(173 站点/1277 命令)中主力 LLM 站点, 沿用 chatgpt/claude/deepseek 既有模式: - KimiOpenCliClient(29 命令全覆盖):标准 ask/send/read/detail/history/ new/status/whoami/login + 特有 mode/model(list|set)/react/regenerate/ share/copy-message/history-rename/storage-get/storage-keys/templates/ account/cookies/idb-list/settings/sidebar-toggle/dismiss-banner/ upgrade/usage/view-all-history/sign-out - GrokOpenCliClient(15):标准 + delete/pin/unpin/image/export/export-all - DoubaoOpenCliClient(11):标准 + meeting-summary/meeting-transcript - QwenOpenCliClient(10):标准 + image(AI 生图) - YuanbaoOpenCliClient(9):标准九件套 argv 契约按上游 manifest 逐项对齐(位置参数在前旗标在后,如 kimi ask <text> --timeout、grok delete <id> --yes);askTyped 统一追加全局 -f json。 BrowserClient/OpenCliClient 门面接线;每个客户端配 options 单测 + RecordingOpenCliExecutor argv 全链路测试(18 个新测试,总计 584 全绿)。 README 双语同步 typed 封装清单。
1 parent a089dfb commit b3dc58f

19 files changed

Lines changed: 1842 additions & 4 deletions

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ Multi-adapter CLI integration SDK for OpenCLI: browser / desktop / public-API ad
5656
| Local subprocess execution | Available | `OpenCliExecutor` (Commons Exec), unified exceptions (`OpenCliNonZeroExitException`, `OpenCliTimeoutException`, ...) |
5757
| Adapter channel | Available | `OpenCliAdapterChannel` (`invoke(List)` / varargs) |
5858
| Adapter registry | Available | `OpenCliAdapterIds` + `OpenCliAdapterTaxonomy` — 173 adapter ids (163 browser + 10 desktop) generated from the upstream manifest |
59-
| Typed wrappers | Available | `codex`, `cursor`, `gemini`, `claude`, `chatgpt`, `jimeng`, `deepseek`, `arxiv`, `npm`, `pypi`, `binance`, `wikipedia` |
59+
| Typed wrappers | Available | `codex`, `cursor`, `gemini`, `claude`, `chatgpt`, `jimeng`, `deepseek`, `kimi`, `qwen`, `doubao`, `grok`, `yuanbao`, `arxiv`, `npm`, `pypi`, `binance`, `wikipedia` |
6060
| Categorized facades | Available | `PublicApiClient`, `BrowserClient`, `DesktopClient` (or `publicApis()` / `browsers()` / `desktops()`) |
6161
| Meta commands | Available | `cli.meta()`: `list`, `validate`, `plugin`, `daemon`, `profile`, `completion`, `skills`, `auth`, `antigravity`, ... |
6262
| Built-in browser session API | Available | `cli.browser()`: `wait` (ms timeout), `extract`, `screenshot`, `getHtml`, ... |
@@ -115,7 +115,7 @@ Single-module library (packaging `jar`). Package layout:
115115
|---|---|
116116
| `io.github.easy4j.opencli` | Facade `OpenCliClient`, `OpenCliProperties`, `OpenCliExecutionTarget` |
117117
| `io.github.easy4j.opencli.core` | `OpenCliExecutor`, `OpenCliAdapterChannel`, results, availability |
118-
| `io.github.easy4j.opencli.adapter` | Typed adapter clients (browser: chatgpt/claude/deepseek/gemini/jimeng; desktop: codex/cursor; publicapi: arxiv/binance/npm/pypi/wikipedia) |
118+
| `io.github.easy4j.opencli.adapter` | Typed adapter clients (browser: chatgpt/claude/deepseek/gemini/jimeng/kimi/qwen/doubao/grok/yuanbao; desktop: codex/cursor; publicapi: arxiv/binance/npm/pypi/wikipedia) |
119119
| `io.github.easy4j.opencli.browser` | Built-in browser session client + options |
120120
| `io.github.easy4j.opencli.facade` | `PublicApiClient` / `BrowserClient` / `DesktopClient` |
121121
| `io.github.easy4j.opencli.meta` | Meta clients (`list`, `plugin`, `daemon`, `profile`, `skills`, `auth`, ...) |

README.zh-CN.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
| 本地子进程执行 | 可用 | `OpenCliExecutor`(Commons Exec),统一异常(`OpenCliNonZeroExitException``OpenCliTimeoutException` 等) |
5656
| 适配器通道 | 可用 | `OpenCliAdapterChannel``invoke(List)` / 可变参数) |
5757
| 适配器注册表 | 可用 | `OpenCliAdapterIds` + `OpenCliAdapterTaxonomy`——共 173 个 adapter id(163 browser + 10 desktop),由上游清单生成 |
58-
| 强类型封装 | 可用 | `codex``cursor``gemini``claude``chatgpt``jimeng``deepseek``arxiv``npm``pypi``binance``wikipedia` |
58+
| 强类型封装 | 可用 | `codex``cursor``gemini``claude``chatgpt``jimeng``deepseek``kimi``qwen``doubao``grok``yuanbao``arxiv``npm``pypi``binance``wikipedia` |
5959
| 分类门面 | 可用 | `PublicApiClient``BrowserClient``DesktopClient`(或 `publicApis()` / `browsers()` / `desktops()`|
6060
| 元命令 | 可用 | `cli.meta()``list``validate``plugin``daemon``profile``completion``skills``auth``antigravity`|
6161
| 内置 browser 会话 API | 可用 | `cli.browser()``wait`(毫秒超时)、`extract``screenshot``getHtml`|
@@ -114,7 +114,7 @@
114114
|---|---|
115115
| `io.github.easy4j.opencli` | 门面 `OpenCliClient``OpenCliProperties``OpenCliExecutionTarget` |
116116
| `io.github.easy4j.opencli.core` | `OpenCliExecutor``OpenCliAdapterChannel`、结果对象、可用性探测 |
117-
| `io.github.easy4j.opencli.adapter` | 强类型适配器客户端(browser:chatgpt/claude/deepseek/gemini/jimeng;desktop:codex/cursor;publicapi:arxiv/binance/npm/pypi/wikipedia) |
117+
| `io.github.easy4j.opencli.adapter` | 强类型适配器客户端(browser:chatgpt/claude/deepseek/gemini/jimeng/kimi/qwen/doubao/grok/yuanbao;desktop:codex/cursor;publicapi:arxiv/binance/npm/pypi/wikipedia) |
118118
| `io.github.easy4j.opencli.browser` | 内置 browser 会话客户端与选项 |
119119
| `io.github.easy4j.opencli.facade` | `PublicApiClient` / `BrowserClient` / `DesktopClient` |
120120
| `io.github.easy4j.opencli.meta` | 元命令客户端(`list``plugin``daemon``profile``skills``auth` 等) |

src/main/java/io/github/easy4j/opencli/OpenCliClient.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,13 @@
33
import io.github.easy4j.opencli.adapter.browser.chatgpt.ChatgptOpenCliClient;
44
import io.github.easy4j.opencli.adapter.browser.claude.ClaudeOpenCliClient;
55
import io.github.easy4j.opencli.adapter.browser.deepseek.DeepseekOpenCliClient;
6+
import io.github.easy4j.opencli.adapter.browser.doubao.DoubaoOpenCliClient;
67
import io.github.easy4j.opencli.adapter.browser.gemini.GeminiOpenCliClient;
8+
import io.github.easy4j.opencli.adapter.browser.grok.GrokOpenCliClient;
79
import io.github.easy4j.opencli.adapter.browser.jimeng.JimengOpenCliClient;
10+
import io.github.easy4j.opencli.adapter.browser.kimi.KimiOpenCliClient;
11+
import io.github.easy4j.opencli.adapter.browser.qwen.QwenOpenCliClient;
12+
import io.github.easy4j.opencli.adapter.browser.yuanbao.YuanbaoOpenCliClient;
813
import io.github.easy4j.opencli.adapter.desktop.codex.CodexOpenCliClient;
914
import io.github.easy4j.opencli.adapter.desktop.cursor.CursorOpenCliClient;
1015
import io.github.easy4j.opencli.adapter.publicapi.arxiv.ArxivOpenCliClient;
@@ -117,6 +122,33 @@ public DeepseekOpenCliClient deepseek() {
117122
return new DeepseekOpenCliClient(executor);
118123
}
119124

125+
// ----- 浏览器强类型门面(新增五大站点) -----
126+
127+
/** @return Kimi Web 适配器 typed 客户端 */
128+
public KimiOpenCliClient kimi() {
129+
return new KimiOpenCliClient(executor);
130+
}
131+
132+
/** @return 通义千问 Web 适配器 typed 客户端 */
133+
public QwenOpenCliClient qwen() {
134+
return new QwenOpenCliClient(executor);
135+
}
136+
137+
/** @return 豆包 Web 适配器 typed 客户端 */
138+
public DoubaoOpenCliClient doubao() {
139+
return new DoubaoOpenCliClient(executor);
140+
}
141+
142+
/** @return Grok Web 适配器 typed 客户端 */
143+
public GrokOpenCliClient grok() {
144+
return new GrokOpenCliClient(executor);
145+
}
146+
147+
/** @return 腾讯元宝 Web 适配器 typed 客户端 */
148+
public YuanbaoOpenCliClient yuanbao() {
149+
return new YuanbaoOpenCliClient(executor);
150+
}
151+
120152
// ----- 参考强类型门面(Public API) -----
121153

122154
/** @return arXiv 公共 API 客户端 */
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
package io.github.easy4j.opencli.adapter.browser.doubao;
2+
3+
import tools.jackson.databind.JsonNode;
4+
import io.github.easy4j.opencli.core.OpenCliAdapterChannel;
5+
import io.github.easy4j.opencli.core.OpenCliArgSupport;
6+
import io.github.easy4j.opencli.core.OpenCliExecutor;
7+
import io.github.easy4j.opencli.core.OpenCliResult;
8+
import io.github.easy4j.opencli.core.OpenCliTypedResult;
9+
import io.github.easy4j.opencli.parser.OpenCliStdoutJson;
10+
import io.github.easy4j.opencli.registry.OpenCliAdapterIds;
11+
import io.github.easy4j.opencli.util.OpenCliLists;
12+
import java.util.ArrayList;
13+
import java.util.List;
14+
import lombok.Builder;
15+
import lombok.Data;
16+
import lombok.RequiredArgsConstructor;
17+
18+
/**
19+
* OpenCLI {@code doubao} 浏览器适配器 typed 客户端。
20+
*
21+
* <p>命令面与上游 {@code cli-manifest.json} 逐项对齐;位置参数在前、
22+
* 旗标在后;全部方法带 {@code more} 原生参数透传。</p>
23+
*
24+
* @author <a href="https://github.com/loong10k">Loong Wan</a>
25+
* @since 3.0.0
26+
*/
27+
@RequiredArgsConstructor
28+
public final class DoubaoOpenCliClient {
29+
30+
private final OpenCliExecutor executor;
31+
32+
private OpenCliAdapterChannel ch() {
33+
return new OpenCliAdapterChannel(executor, OpenCliAdapterIds.DOUBAO);
34+
}
35+
/** {@code doubao ask} 选项块。 */
36+
@Data
37+
@Builder(toBuilder = true)
38+
public static class DoubaoAskOptions {
39+
40+
private Integer timeoutSeconds;
41+
42+
/** 追加 {@code -f json}(全局格式旗标)。 */
43+
private Boolean jsonOutput;
44+
45+
public void appendTo(List<String> target) {
46+
if (timeoutSeconds != null) {
47+
OpenCliArgSupport.addOptionPair(target, "--timeout", String.valueOf(timeoutSeconds));
48+
}
49+
if (Boolean.TRUE.equals(jsonOutput)) {
50+
target.add("-f");
51+
target.add("json");
52+
}
53+
}
54+
}
55+
56+
57+
/** {@code doubao ask <text>}。 */
58+
public OpenCliResult ask(String text, DoubaoAskOptions options, List<String> more) {
59+
List<String> args = new ArrayList<>();
60+
args.add("ask");
61+
args.add(text);
62+
if (options != null) {
63+
options.appendTo(args);
64+
}
65+
return ch().invoke(OpenCliArgSupport.merge(args, more));
66+
}
67+
68+
/** {@code doubao ask <text> -f json},解析为 typed 结果。 */
69+
public OpenCliTypedResult<JsonNode> askTyped(String text, DoubaoAskOptions options, List<String> more) {
70+
DoubaoAskOptions withJson = options;
71+
if (withJson == null) {
72+
withJson = DoubaoAskOptions.builder().jsonOutput(true).build();
73+
} else if (!Boolean.TRUE.equals(withJson.getJsonOutput())) {
74+
withJson = withJson.toBuilder().jsonOutput(true).build();
75+
}
76+
return OpenCliStdoutJson.typed(ask(text, withJson, more));
77+
}
78+
79+
/** {@code doubao send <text>}(fire-and-forget)。 */
80+
public OpenCliResult send(String text, List<String> more) {
81+
List<String> args = new ArrayList<>();
82+
args.add("send");
83+
args.add(text);
84+
return ch().invoke(OpenCliArgSupport.merge(args, more));
85+
}
86+
87+
/** {@code doubao read}。 */
88+
public OpenCliResult read(List<String> more) {
89+
return ch().invoke(OpenCliArgSupport.merge(OpenCliLists.of("read"), more));
90+
}
91+
92+
/** {@code doubao detail <id>}。 */
93+
public OpenCliResult detail(String conversationId, List<String> more) {
94+
List<String> args = new ArrayList<>();
95+
args.add("detail");
96+
args.add(conversationId);
97+
return ch().invoke(OpenCliArgSupport.merge(args, more));
98+
}
99+
100+
/** {@code doubao history [--limit N]}。 */
101+
public OpenCliResult history(String limit, List<String> more) {
102+
List<String> args = new ArrayList<>();
103+
args.add("history");
104+
if (limit != null) {
105+
OpenCliArgSupport.addOptionPair(args, "--limit", limit);
106+
}
107+
return ch().invoke(OpenCliArgSupport.merge(args, more));
108+
}
109+
110+
/** {@code doubao meeting-summary <id>}。 */
111+
public OpenCliResult meetingSummary(String conversationId, String chapters, List<String> more) {
112+
List<String> args = new ArrayList<>();
113+
args.add("meeting-summary");
114+
args.add(conversationId);
115+
if (chapters != null) {
116+
OpenCliArgSupport.addOptionPair(args, "--chapters", chapters);
117+
}
118+
return ch().invoke(OpenCliArgSupport.merge(args, more));
119+
}
120+
121+
/** {@code doubao meeting-transcript <id>}。 */
122+
public OpenCliResult meetingTranscript(String conversationId, String download, List<String> more) {
123+
List<String> args = new ArrayList<>();
124+
args.add("meeting-transcript");
125+
args.add(conversationId);
126+
if (download != null) {
127+
OpenCliArgSupport.addOptionPair(args, "--download", download);
128+
}
129+
return ch().invoke(OpenCliArgSupport.merge(args, more));
130+
}
131+
132+
/** {@code doubao login [--timeout N]}。 */
133+
public OpenCliResult login(Integer timeoutSeconds, List<String> more) {
134+
List<String> args = new ArrayList<>();
135+
args.add("login");
136+
if (timeoutSeconds != null) {
137+
OpenCliArgSupport.addOptionPair(args, "--timeout", String.valueOf(timeoutSeconds));
138+
}
139+
return ch().invoke(OpenCliArgSupport.merge(args, more));
140+
}
141+
142+
/** {@code doubao new}。 */
143+
public OpenCliResult newChat(List<String> more) {
144+
return ch().invoke(OpenCliArgSupport.merge(OpenCliLists.of("new"), more));
145+
}
146+
147+
/** {@code doubao status}。 */
148+
public OpenCliResult status(List<String> more) {
149+
return ch().invoke(OpenCliArgSupport.merge(OpenCliLists.of("status"), more));
150+
}
151+
152+
/** {@code doubao whoami}。 */
153+
public OpenCliResult whoami(List<String> more) {
154+
return ch().invoke(OpenCliArgSupport.merge(OpenCliLists.of("whoami"), more));
155+
}
156+
}

0 commit comments

Comments
 (0)