Rowboat Code Mode 引擎托管架构解析:基于 ACP 适配器的 Claude Code / Codex 引擎按需供给方案
【免费下载链接】rowboatOpen-source AI coworker, with memory项目地址: https://gitcode.com/GitHub_Trending/rowb/rowboat
本文以仓库中 CODE_MODE_ENGINES_PLAN.md 设计文档为主体,结合
packages/core中已落地的工程实现(engine-provisioner.ts、engine-manifest.ts、agents.ts、status.ts)展开成文。Rowboat 的 Code Mode 通过 ACP(Agent Client Protocol)适配器驱动Claude Code与Codex两个原生编码代理,本文完整还原其"引擎托管(Managed Engine Provisioning)"架构:引擎二进制归应用管理、按需从 npm 下载、SHA 校验、版本锁定;认证则复用用户既有凭证。读完你将掌握一套"安装包不膨胀、打包版开箱即用、离线不挂起"的编码代理引擎供给方案的设计与实现细节。
一、问题背景:打包版 Code Mode 为什么"一直在坏"
Rowboat 的 Code Mode 同时运行两个编码代理:Claude Code与Codex。实现方式是先拉起各自的ACP 适配器(@agentclientprotocol/claude-agent-acp、@agentclientprotocol/codex-acp),再由适配器孵化一个体积庞大的原生引擎二进制(Claude 约 205 MB、Codex 约 194 MB)。设计文档给出的核心目标是:
让 Code Mode 在打包发布版中对两个代理都能达到约 99% 的可靠运行,同时又不把约 400 MB 的引擎塞进安装包。
现状:打包版每次发布都是坏的
计划文档明确指出(其描述基线为 #614 的回滚之后的状态):
- 打包构建不暂存(stage)ACP 适配器,且
forge.config.cjs中的ignore: /^\/node_modules\//规则会把这些依赖剥掉; - 运行时
agents.ts通过require.resolve(...)解析适配器再 spawn,于是直接抛出Cannot find module '@agentclientprotocol/...'; - 结论是:打包版 Code Mode 在每次发布中都是坏的,只有在 dev 下能跑(因为 pnpm symlink 存在)。当下既没有 400 MB 的膨胀,也没有任何功能。
两个候选方案的不足
| 方案 | 思路 | 致命缺陷 |
|---|---|---|
| A. 打包引擎(Bundle engines) | 每个安装包内置一套 claude + codex 原生二进制 | 每个 OS/arch 的安装包增加约 400 MB,体积不可接受 |
| B. 复用用户本机安装(#614 曾采用后被回滚) | 依赖用户已装好两个 CLI、已登录、版本正确且在 PATH 上 | 强依赖用户机器环境——版本错位、GUI 启动 PATH 被剥离、未安装等,结构性无法达到 99% 可靠性 |
正是 B 方案的"版本错位 + PATH 陷阱"问题,使 #614 被回滚。这两条路径的失败,直接催生了"引擎托管"这一第三条路线。
二、核心架构:Engine 归应用所有,Auth 复用用户
设计文档给出的解决方案把问题拆成engine与auth两件事,区别对待:
- Engine 由应用拥有:将版本锁定的引擎二进制供给(provision)到应用支持目录
~/.rowboat/engines/<agent>/<version>/,首次使用时按需下载,sha256 校验,symlink/路径固定。不使用用户的全局 npm,也不依赖用户的 PATH → 无版本错位、无 PATH 怪癖,这是 99% 可靠性的来源。 - Auth 复用用户:引擎读取用户已有的凭证(
~/.claude的 API key / Pro / Max 订阅、~/.codex的 auth.json),无需二次登录。status.ts已具备对这些凭证的检查能力。
实证:Conductor 的落地形态
计划文档记录了在开发机上对 Conductor(conductor.build)的观察作为实证:
- 其 DMG 仅123 MB,远小于约 400 MB 的引擎体积 → 引擎不在安装包里,而是安装后下载;
- 在
~/Library/Application Support/com.conductor.app/观察到的目录布局:
agent-binaries/claude/2.1.170/claude (222 MB, single Mach-O arm64) agent-binaries/codex/0.138.0/codex (205 MB, single Mach-O arm64) bin/claude -> agent-binaries/claude/2.1.170/claude (symlink to active version) bin/codex -> agent-binaries/codex/0.138.0/codex agent-binaries/.meta/claude-2.1.170.json = {sha256, size, downloaded_at_unix_ms, ...}即版本化目录 + 稳定 symlink + sha256.meta台账 + 按需下载四件套。Rowboat 的方案镜像了这套形态(详见第五节)。
三、关键事实基础:适配器通过环境变量接受外部引擎
方案成立的前提是:两个 ACP 适配器都原生支持通过环境变量指定外部引擎,无需改动适配器一行代码。计划文档给出了源码级证据(当时锁定的适配器版本):
- Claude:
@agentclientprotocol/claude-agent-acp@0.39.0,其dist/acp-agent.js:39有if (process.env.CLAUDE_CODE_EXECUTABLE) return it;,第 1552 行pathToClaudeCodeExecutable: process.env.CLAUDE_CODE_EXECUTABLE ?? ...。若未设置且无内置原生依赖,会直接抛出 "set CLAUDE_CODE_EXECUTABLE"。 - Codex:
@agentclientprotocol/codex-acp@0.0.44,其dist/index.js:20900:const codexPath = process.env["CODEX_PATH"] ?? "codex";→spawn(codexPath, ["app-server"])。
推论:供给引擎 + 设置这两个环境变量,就是引擎侧的全部工作。
同时,Rowboat 依赖的引擎包本身就是单一自包含二进制:
@anthropic-ai/claude-agent-sdk-darwin-arm64@0.3.156→ 只含一个claude可执行文件(外加 LICENSE/README);@openai/codex@0.128.0-darwin-arm64→ 内含vendor/<target>/codex/codex原生二进制,还捆绑了一个rg(ripgrep),位于vendor/<target>/path/rg—— 这是风险 R1 的来源(见第七节)。
版本锁定与平台包命名
计划文档从已安装的适配器依赖树中读出的固定版本映射:
- Claude:适配器
claude-agent-acp@0.39.0→ 引擎@anthropic-ai/claude-agent-sdk@0.3.156;平台可选依赖@anthropic-ai/claude-agent-sdk-<platform>@0.3.156,覆盖darwin-arm64、darwin-x64、linux-x64、linux-arm64、linux-x64-musl、linux-arm64-musl、win32-x64、win32-arm64八个平台。 - Codex:适配器
codex-acp@0.0.44→@openai/codex@^0.128.0(pnpmpatched,见 R2);平台依赖以别名形式@openai/codex-<platform>→npm:@openai/codex@0.128.0-<platform>发布,覆盖darwin-arm64、darwin-x64、linux-x64、linux-arm64、win32-x64、win32-arm64六个平台。
需要说明的是,计划文档中的 0.3.156 / 0.128.0 是动笔时的固定值;当前仓库由构建脚本自动生成的 engine-manifest.ts 中,实际锁定的是claude
0.3.257与codex0.153.4(codex 按0.153.4-<platform>发布)。这正体现了"清单由构建生成、与适配器永不脱节"的设计意图(对应风险 R8)。
四、分发源决策:npm 平台包 + 适配器锁定版本(已定案)
方案决定:从 npm registry 拉取 ACP 适配器所依赖的、精确版本对应的分平台引擎包,解出原生二进制,供给到~/.rowboat/engines/...。不自建托管、不用 curl 安装器、不做 fallback。
Tarball 地址模式
https://registry.npmjs.org/<pkg>/-/<file>-<version>.tgz- claude:
@anthropic-ai/claude-agent-sdk-<platform>@0.3.156→ 解出其中的claude二进制; - codex:
@openai/codex@0.128.0-<platform>→ 解出vendor/<target>/codex/codex,并且保留vendor/<target>/path/rg(见 R1)。
为什么是"适配器锁定的 npm 包"而不是 curl 安装器 / 发布桶 / 自建托管
计划文档给出了四个核心理由:
- 握手保证:二进制是"适配器构建/测试所针对的精确版本"→ ACP 握手必然成功 → 这是约 99% 可靠性的关键;
- 零基础设施的完整性校验:npm registry 高可用、按版本不可变,packument 提供
dist.integrity(sha512)+dist.shasum(sha1),无需任何自建基础设施即可校验; - 规避官方安装器的行为:官方
curl | bash安装器面向终端用户,会全局安装到~/.local/bin且后台自动更新——这恰恰是要避免的。Rowboat 需要一份隔离、固定、由应用管理、绝不会在适配器脚下漂移的副本(Conductor 同样只取原始二进制而不运行安装器); - 版本永不漂移:版本在构建时从 lockfile 读取并内嵌到
engine-manifest.json,因此清单与随包发布的适配器永不失步。
与 Conductor / 官方安装器的关系
- Conductor 在
storage.conductor.build自托管同类原生二进制(raw/.gz/.zst+{url,gzipUrl,zstdUrl,sha256}清单),将适配器与较新的独立 CLI版本(claude 2.1.x、codex 0.138)配对。这证明了较新版本可用;但 Rowboat 出于确定性,刻意锁定适配器自己的引擎版本。 - 官方 Claude 发布也会在
downloads.claude.ai/claude-code-releases/<ver>/提供 GPG 签名的manifest.json(SHA256/platform)。当前不用(npm 更简单且与适配器匹配),但这是未来接入最新 CLI 线的升级路径。 - 若日后需要控制权/可用性/压缩,可以把这些 npm tarball 镜像到自己桶里——运行时完全不变,只改清单中的 URL。
已锁定的决策(user 确认)
- 源:npm 平台包,适配器锁定版本;
- 不自建托管;
- 总是供给,无 fallback:不回退到用户预装的
claude/codex,被回滚的路径 B 彻底退役; - Codex pnpm patch 无关紧要(用户确认):patch 针对的是 JS launcher,而方案把
CODEX_PATH直接指向原生二进制,patch 不生效(风险 R2 移除)。
五、构建期:生成引擎清单 + 暂存适配器 JS
5.1 引擎清单engine-manifest.json(内嵌进应用)
构建脚本读取已安装的适配器依赖树,按代理生成清单,计划文档给出的结构示例(JSONC):
{ "claude": { "version": "0.3.156", "platforms": { "darwin-arm64": { "pkg": "@anthropic-ai/claude-agent-sdk-darwin-arm64", "tarball": "https://registry.npmjs.org/.../-/...-0.3.156.tgz", "integrity": "sha512-...", "binRelPath": "claude" }, "...": {} } }, "codex": { "version": "0.128.0", "platforms": { "darwin-arm64": { "pkg": "@openai/codex-darwin-arm64", "tarball": "https://registry.npmjs.org/...", "integrity": "sha512-...", "binRelPath": "vendor/aarch64-apple-darwin/codex/codex", "extraPaths": ["vendor/aarch64-apple-darwin/path/rg"] } } } }- 版本、tarball URL、integrity 全部取自 lockfile / npm packument,保证清单与随包适配器始终同步,避免硬编码易碎的包名;
binRelPath/extraPaths记录可执行文件(以及 codex 的rg)在 tarball 内的位置。
实现佐证:仓库中的构建脚本 gen-engine-manifest.mjs 完整实现了这一逻辑:
- 通过
pinnedDep()读取@agentclientprotocol/claude-agent-acp对@anthropic-ai/claude-agent-sdk、codex-acp对@openai/codex的依赖规格;对^/~范围会用已安装版本的package.json解析出实际安装版本,使供给引擎与 lockfile 严格一致; - 通过
distFor()请求https://registry.npmjs.org/<pkg>/<version>拿dist.tarball与dist.integrity,404 则跳过该平台并告警; - 输出写入
src/code-mode/acp/engine-manifest.ts(提交入库)。选择提交的 .ts 而非构建时拉取的 .json,是为了:应用构建无需网络、dev 离线可用、PR 可评审、且 esbuild 会将其内联进打包后的主 bundle。
当前 engine-manifest.ts 中可看到完整的 8 平台 claude 条目与 6 平台 codex 条目,每条都含pkg、pkgVersion、tarball、integrity(sha512 前缀),文件头明确标注"AUTO-GENERATED ... Regenerate after bumping the @agentclientprotocol/*-acp adapter (engine) versions"。
5.2 暂存 ACP 适配器 JS(#614 中保留的部分)
适配器本身很小(含非原生 JS 依赖总共约 15 MB),但打包版必须让它们在磁盘上存在,agents.ts才能解析并 spawn。方案:
- 在
forge.config.cjs的generateAssets中,把两个适配器及其非原生生产依赖闭包暂存到.package/acp/node_modules(npm 风格嵌套布局); - 将
.package从node_modules的 ignore 规则中豁免; - #614 的原生引擎暂存整体放弃——引擎来自供给,不来自 bundle。
实现佐证:当前 forge.config.cjs 中有stageAcpAdapters逻辑(将@agentclientprotocol/claude-agent-acp、@agentclientprotocol/codex-acp及其依赖闭包复制进.package/acp/node_modules,并验证解析成功),且 ignore 规则对/.package及/.package/*显式放行(if (p === '/.package' || p.startsWith('/.package/')) return false;)。
六、运行期:引擎供给器(engine-provisioner)核心实现
计划文档规划的新模块落在packages/core/src/code-mode/acp/engine-provisioner.ts,其算法伪码为:
ensureEngine(agent): Promise<{ executablePath: string }> 1. 读取 (agent, currentPlatform) 对应的清单条目,不支持则明确报错。 2. dir = ~/.rowboat/engines/<agent>/<version>/ 3. 若目录存在 且 .meta/<agent>-<version>.json 的 sha256 匹配 → 直接返回 binPath。 4. 否则获取跨进程锁(避免双重下载),然后: a. 流式下载 tarball 到临时文件,带进度事件。 b. 校验完整性(清单中的 sha512/sha256)。 c. 解压到临时目录,原子 rename 进 <version>/(tar gzip)。 d. unix 下 chmod +x 二进制(以及 codex 的 rg)。 e. 写 .meta/<agent>-<version>.json {sha256, size, downloaded_at_unix_ms}。 5. 返回引擎可执行文件的绝对路径。- 进度 + 取消通过 IPC 上抛,支撑首次运行的 "Downloading engine…" UI;
- 离线/失败→ 抛出带清晰可操作信息的类型化错误,绝不静默挂起;
- 原子性:先下载到临时文件 → 校验 → rename,绝不留下"半解压但通过了存在性检查"的版本目录。
源码级细节(当前实现)
对照 engine-provisioner.ts,实现比计划更细:
- 平台判定
platformKey():darwin/win32直接映射darwin-<arch>/win32-<arch>;linux上先检测 musl(Alpine)优先linux-<arch>-musl,否则回退 glibc 的linux-<arch>。musl 检测复用 Node 原生插件加载器的启发式:process.report的 header 中出现glibcVersionRuntime即非 musl。 - 定位可执行文件
locateExecutable():claude 直接找claude/claude.exe;codex 在vendor/<triple>/下同时探测bin/与codex/子目录(源码注释说明 codex 布局从codex/(≤0.128)迁到了bin/(≥0.142)),兼容新旧版本。 - 完整性校验
verifyIntegrity():按 SRI 字符串(sha512-<base64>)拆分算法与期望值,用crypto.createHash重算比对,不匹配即抛错(对应风险 R4)。 - Windows 解压细节
extractTarball():优先用System32\tar.exe(bsdtar);找不到时改为从归档所在目录运行并只传文件名,规避 GNU tar 把C:\...绝对路径误读为远程host:path的问题。 - 版本清理
pruneOldVersions():新版本供给成功后,删除该代理下除活跃版本外的全部旧版本目录与.meta条目(对应风险 R5),并跳过.tmp-前缀的在途临时目录;best-effort,绝不因清理失败破坏一次成功的安装。 - 幂等并发:
ensureEngine()每次用独立的mkdtempSync(.tmp-<version>-)临时目录下载/校验/解压,最后renameSync原子换入;并发调用者各用各的临时目录,最终 rename 幂等。finally中必清理临时根目录。
关键设计:下载只发生在 Settings 里
实现中getProvisionedEnginePath(agent)(在 engine-provisioner.ts)故意不下载:聊天/运行路径调用它时,若引擎缺失会抛出明确的"Open Settings → Code Mode and click Enable to download it"错误,保证用户不会在对话中途遭遇约 200 MB 的意外下载。带下载能力的ensureEngine()只由 Settings 的 "Enable" 动作驱动——这实际上把计划文档中"Provision timing: lazy vs eager"的开放问题落定为了设置页驱动的 lazy + 缓存。
七、接入启动链路:agents.ts 与 client.ts
计划要求改造agents.ts的getAgentLaunchSpec()(原先只设置CLAUDE_CODE_EXECUTABLE):
- 先
await ensureEngine(agent),然后设置:- claude →
env.CLAUDE_CODE_EXECUTABLE = <provisioned claude> - codex →
env.CODEX_PATH = <provisioned codex>(并保证其rg兄弟文件可解析,保持 vendor 目录布局,让 codex 能找到../path/rg)
- claude →
- 适配器 spawn 保持
ELECTRON_RUN_AS_NODE=1不变。
当前实现(agents.ts)比计划更进一步,提供多个实战细节:
- 适配器解析:
resolveAdapterPkgJson()先在暂存的.package/acp位置解析(打包版),失败再回退普通 node_modules 解析(dev);resolveAdapterEntry()读取包的bin字段拿到入口脚本绝对路径。 - 登录 shell PATH 嫁接:
loginShellPath()探测用户登录 shell 的 PATH 并合并进引擎环境变量——因为 GUI(Finder)启动的应用继承的是 launchd 剥离过的 PATH,引擎 spawn 的 git/gh/rg/bash 会报 "command not found",即使终端里可用;Windows 或探测失败时为空操作。 - 调试可观测性:对 claude 额外设置
DEBUG_CLAUDE_AGENT_SDK=1,让 SDK 把精确的 spawn 命令与 claude 的 stderr 记录到~/.claude/debug/sdk-*.txt,使启动失败/挂起有迹可查。 - spawn 方式:命令固定为
process.execPath(Electron 主进程内即 Electron 二进制)+ELECTRON_RUN_AS_NODE=1,使其以纯 Node 运行时行为执行适配器入口;否则子进程不会以 node 运行,ACP stdio 流会立即关闭("ACP connection closed")。同时规避了 Windows 上.cmd的 EINVAL 问题。
调用链上,client.ts 在 spawn 适配器前取getAgentLaunchSpec(this.agent),保证每次启动都拿到指向已供给引擎的完整环境。
八、状态检测与 UX:status.ts + IPC
8.1checkCodeModeAgentStatus():引擎=已供给?认证=凭证存在?
计划要求把状态检查从"PATH 上是否安装"改为"引擎是否已供给"。当前 status.ts 的实现:
installed字段 =isEngineProvisioned(agent),即~/.rowboat/engines/<agent>/<version>下可执行文件存在且.meta台账存在——不再查找 PATH 上的全局 claude/codex CLI;signedIn/account走"引擎探针优先、文件启发式兜底":- claude:对供给的引擎执行
claude auth status,解析输出 JSON 中的loggedIn(真实引擎如何解析凭证——Keychain、CLAUDE_CONFIG_DIR、企业托管——就用同一套,是 ground truth),15 秒超时(ENGINE_PROBE_TIMEOUT_MS,覆盖引擎冷启动与 macOS 首次 Keychain 授权弹窗);探针失败再回退到~/.claude/.credentials.json/ Keychain 检查; - codex:执行
codex login status(退出码 0=已登录);身份信息从~/.codex/auth.json的id_tokenJWT claims 解码(email、chatgpt_plan_type),仅用于展示不作认证决策; - 身份补充:claude 从
~/.claude.json的oauthAccount.emailAddress读取。
- claude:对供给的引擎执行
8.2 首次运行流与 IPC 进度
- 首次运行:用户选择代理 → 若引擎缺失,显示 "Downloading engine (~200 MB), one time…" 进度条 → 完成后继续;后续使用瞬时完成。
- 清晰的错误态:下载失败 / 离线 / 不支持平台 / 缺认证。
实现佐证:主进程 ipc.ts 注册了codeMode:provisionEnginehandler,调用ensureEngine(args.agent, { onProgress }),把download/verify/extract/done各阶段以及receivedBytes/totalBytes通过codeMode:engineProgress事件流式回传给设置窗口,用于实时进度条;错误则以{ success: false, error }返回。另有codeMode:checkAgentStatushandler 暴露checkCodeModeAgentStatus()。
九、边缘情况与风险清单(R1–R8)
计划文档列出的风险在实现中逐一回应:
- R1 — Codex 依赖
rg:codex 平台包在vendor/<target>/path/rg捆绑 ripgrep。必须解压并保留整个 vendor 布局(不能只取裸二进制)。当前实现:locateExecutable()探测bin/与codex/,makeExecutable()对codex-path/与path/下的rg都chmod 755,兼容新旧布局(≤0.128 用path/,≥0.142 用codex-path/)。 - R2 — Codex pnpm patch(已解决/无关):patch 针对 JS launcher;
CODEX_PATH指向原生二进制,patch 不生效。 - R3 — 平台/架构矩阵:清单须覆盖 darwin x64/arm64、linux x64/arm64(claude 另有 musl 变体)、win32 x64/arm64;Windows 引擎是
claude.exe/codex.exe。当前engine-manifest.ts中 claude 8 平台、codex 6 平台齐全。 - R4 — 完整性与供应链:chmod/执行前必须校验清单中的完整性哈希,哈希不匹配是硬失败。实现中
verifyIntegrity()即此职责。 - R5 — 磁盘与升级:版本化目录会累积,需清理策略只保留每个代理的活跃固定版本。实现中
pruneOldVersions()已落地。 - R6 — 首次运行网络:每个代理只需一次,之后永久缓存;必须是清晰、可取消的 UX,绝不静默挂起(复用 #614 启动期限的教训)。
- R7 — macOS 代码签名 / Gatekeeper:下载的原生二进制不在应用签名覆盖内。需验证其在 Gatekeeper 下可运行(二进制本身已由 Anthropic/OpenAI 签名公证,quarantine 属性可能需要清除)。Conductor 能从 app-support 正常运行,Rowboat 需同样确认(计划中列为 P2 期间验证项)。
- R8 —
engine-manifest陈旧:适配器/引擎版本变更时必须重新生成清单;把生成绑进构建流程,使其不可能漂移。当前gen-engine-manifest.mjs与提交入库的engine-manifest.ts即此机制。
十、实施阶段划分(P1–P5)
| 阶段 | 内容 | 验收标准 |
|---|---|---|
| P1 打包修复 | 适配器 JS 暂存进.package,解析器优先查暂存路径 | 打包版 Code Mode 至少能 spawn 适配器(与供给无关,是 #614 值得保留的部分) |
| P2 供给器 | ensureEngine()+ 清单 + 接线环境变量 | macOS arm64 上两个引擎都能供给并从~/.rowboat/engines启动 |
| P3 UX + 状态 | 首次运行下载 UI、状态面板、错误态 | 用户可见的完整下载与状态体验 |
| P4 跨平台 + CI 冒烟 | 矩阵清单,mac/linux/win 冒烟 | 多平台可用 |
| P5 打磨 | 版本清理、取消、离线提示、可选本机安装 fallback | 全场景健壮 |
十一、决策与遗留问题
已解决(锁定):
- 源 = npm 平台包、适配器锁定版本(非自建托管、非 curl 安装器、非发布桶);
- 总是供给,无本机安装 fallback;
- Codex pnpm patch 无关。
实现期间可再定(其中部分已被当前实现落定):
- 供给时机:首次 Code Mode使用时(lazy)vs 首次应用启动(eager 后台下载)。当前实现取lazy + 缓存,且下载只由 Settings 的 Enable 动作触发(见第六节);
- 版本目录清理:每个代理只保留活跃固定版本——
pruneOldVersions()已实现; - P2 期间验证 R1(codex
rg)与R7(Gatekeeper)。
十二、总结
Rowboat 的 Code Mode 引擎托管方案,本质是一次清晰的职责切分:引擎由应用以版本锁定的方式按需供给,认证复用用户既有凭证。它同时绕开了"安装包膨胀 400 MB"与"依赖用户本机环境"两个死结,靠"适配器支持环境变量指定引擎 + npm 平台包天然带完整性校验 + 构建期自动生成永不漂移的清单"三个事实落地。从当前仓库的实现看,计划中的供给器、清单生成、启动接线、状态检测、IPC 进度 UI 均已落地,且实现细节(musl 探测、Windows bsdtar、vendor 布局双版本兼容、旧版本清理)比计划文档更进一步,可视为该设计在真实工程中的完整验证。
如需深入,可继续阅读 CODE_MODE_ENGINES_PLAN.md(设计原文)、engine-provisioner.ts(供给器实现)、agents.ts(启动接线)、status.ts(状态检测)、gen-engine-manifest.mjs(清单生成脚本)与 forge.config.cjs(打包暂存)。
【免费下载链接】rowboatOpen-source AI coworker, with memory项目地址: https://gitcode.com/GitHub_Trending/rowb/rowboat
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考