ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

从Bun Monorepo到SQLite账本:oneshot-gtm架构全景解析

从Bun Monorepo到SQLite账本:oneshot-gtm架构全景解析

【免费下载链接】oneshot-gtm

GTM agent for technical founders. Pay-per-result. Two surfaces: terminal CLI + local web dashboard.

项目地址:https://gitcode.com/gh_mirrors/on/oneshot-gtm
点击查看免费下载

oneshot-gtm 是一个面向技术创始人的开源 GTM(Go-To-Market)外联代理,采用Bun + TypeScript Monorepo构建,以本地SQLite 账本(ledger)为唯一数据源,提供终端 CLI 和本地 Web 仪表盘两个界面。本文带你快速看懂它的 Monorepo 工程组织、分层包结构、SQLite 数据层设计,以及"按结果付费、签名回执"的核心理念。

一、Monorepo 总览:Bun + Turbo 的工程底座

oneshot-gtm 是一个典型的 Bun Monorepo 工程,根目录的 package.json 声明了两个关键点:

  • 运行时锁定:engines要求bun >= 1.3.0,packageManager固定为bun@1.3.13。代码直接使用bun:sqlite原生模块,无法在纯 Node 下运行——这是理解它架构的第一块基石。
  • 工作区划分:workspaces.packages覆盖apps/*和packages/*两大目录,并通过catalog字段统一锁定typescript、vitest等共享依赖版本,保证全仓库版本一致。

任务编排交给Turborepo。turbo.json 定义了build、dev、test、typecheck、lint、fmt六类任务:build依赖上游包的^build并缓存dist/**产物,测试则依赖^build且禁用缓存,确保每次都在真实构建产物上跑。常用入口脚本直接写在根package.json:

bun run cli -- --help # 列出全部 CLI 命令 bun run ui # 启动本地仪表盘 http://127.0.0.1:3030 bun --bun run test # 在 Bun 下跑 vitest

测试规模:7250 个测试 / 548 个文件

STATUS.md 披露了当前测试总量:7250 个测试分布在 548 个文件中,且声明所有 CLI 命令、play、finder 和仪表盘页面均已覆盖,并对真实 API 做过端到端验证。对新手来说,这意味着你可以放心地阅读任意模块——每个函数几乎都有对应测试作为"活文档"。

二、三个应用面(apps/):CLI、Server、Web

apps/目录下是三个可独立运行的表面,正好对应项目描述中的"Two surfaces + 共享内核":

目录职责技术栈
apps/cli终端命令行入口commander 命令分发
apps/server本地 Web 服务与调度器Bun.serve+ SSE,仅绑定127.0.0.1
apps/web本地 Web 仪表盘Vite + React + TanStack + Tailwind

三者共享同一个 SQLite 账本:CLI 和仪表盘读写的是同一份本地数据,切换操作面不会丢失任何状态。README 中的架构简图可以概括为:

apps/cli apps/server apps/web surfaces commander Bun.serve + SSE Vite + React \ | / packages/* core · intel · plays · find · prompts · doctor · shared-types | @oneshot-agent/sdk email · LinkedIn · SMS · voice · research · enrichment · receipts
  • CLI 层:apps/cli/src/commands/ 下 35 个命令模块,由 dispatch.ts 统一分发。
  • Server 层:apps/server/src/api/ 包含 50 个 REST/SSE 路由模块,scheduler.ts 负责后台 finder 轮询调度。
  • Web 层:apps/web/src/routes/ 是 14 个页面路由,组件与库代码分别落在components/(93 个文件)和lib/(74 个文件)。

三、七个核心包(packages/):分层清晰的领域内核

packages/是架构的"腰身",每个包对应一个清晰的领域边界:

  1. packages/core— 领域核心。账本读写、发送路由、LinkedIn 漏斗、回执、日历、语音等都在这。其中ledger-*.ts系列文件按业务域拆分成 20 多个模块(队列、回执、节奏、出站、收件箱……),ledger.ts 负责组装,ledger-schema.ts 负责建表与迁移。
  2. packages/find— "找人"引擎。各种 finder(Show HN、融资、换工作、招聘信号、GitHub star、本地商户、政府采购……)的实现都在这里,源码以_前缀组织,如 _newsfeed.ts、_contact.ts。
  3. packages/plays— 62 个 play 模块。一个 play 就是一个"动作":面向谁、发生了什么、首条消息说什么、如何跟进。
  4. packages/prompts— 纯 Markdown 提示词库,如 reply-email.md、icp-filter.md。提示词即代码,读文档就能看懂每个动作的"灵魂"。
  5. packages/intel— LLM 调用与报告层(重试、截断、多 provider 适配)。
  6. packages/doctor— 健康检查(余额、日历、数据库权限、LinkedIn 会话等),对应bun run cli -- doctor命令。
  7. packages/shared-types— 跨包共享的 TypeScript 类型契约。

这种"apps 薄、packages 厚"的结构,让每个领域可以独立测试、独立演进——packages/find下的 200+ 个测试文件就是最好的证明。

四、SQLite 账本:为什么选它

oneshot-gtm 最打动人的架构决策是:全部状态都在你磁盘上的一个 SQLite 文件里。

数据文件布局

按 docs/data-storage.md 的官方清单:

文件位置内容
ledger.sqlite每个 workspace 主目录回执、prospects、队列、节奏、回复、退信、会议、交易、草稿、触发器、学习提案
config.json同上你的画像、ICP、voice card 与设置
.env同上密钥,权限 600
events.jsonl同上本地事件日志(永不离开本机)
shared.sqlite~/.oneshot-gtm-shared跨 workspace 共享的人群、付费查询缓存、联系记录

默认 workspace 主目录为~/.oneshot-gtm,可用ONESHOT_GTM_HOME环境变量迁移。代码开源,但你的 prospects、密钥、账本永远不进 Git——docs/repository-boundaries.md 甚至配套了边界测试来防止误提交。

版本化迁移:账本的"时间机器"

ledger-schema.ts 定义了LedgerMigration接口和LEDGER_MIGRATIONS数组:

{ version: 1, name: "baseline", ... } { version: 2, name: "draft-versions-format-key", ... } { version: 3, name: "triggers-company-batch-seq", ... } ...

规则非常工程化:schema 版本记录在PRAGMA user_version,打开旧账本时在单个事务内补跑缺失的迁移,崩溃了也不会留下半迁移状态;新版本构建也永远不会降级旧账本。文件头注释还约定了时间戳格式(UTC 文本、跨列比较用julianday())、金额精度(新列用整数 micro-dollars)、外键强制等细节——这些注释本身就是一份优秀的数据字典。

运维友好性

  • WAL 模式:数据库运行在 WAL 模式,备份必须用sqlite3 ... ".backup"而非cp,否则会丢-wal里的未落盘写入。
  • 权限加固:macOS/Linux 上每个数据库创建为0600,打开时自动重置,doctor会警告任何被其他用户可读的数据库。
  • 容量治理:doctor会在旧回执超过 20 MB 时提示,compact-receipts --apply可安全瘦身。

五、"按结果付费"架构:签名回执

项目的核心商业理念是Pay-per-result(按结果付费),这在架构上落为"签名回执(signed receipt)"机制:

  • 每一次付费 API 调用(研究、数据补全、邮件、LinkedIn、短信、语音)都会在 OneShot 平台留下签名回执;
  • 本地账本保存每个回执的"信封"(id、成本、消息/线程 id),所以单次回复的成本是记录值而非估算值;
  • docs/webhooks.md 描述了仅有的两个对外路由——其余全部服务只绑定在127.0.0.1,单用户、本地优先。

配合本地events.jsonl事件流(每次 LLM 调用、门控决策、finder 事件各记一行,10 MB 自动滚动三代),你可以用tail -F实时审计系统的每个动作:

tail -F ~/.oneshot-gtm/events.jsonl | jq -c 'select(.kind|startswith("llm."))'

六、快速上手:从空仓库到第一份草稿

  1. 安装运行时并克隆仓库:
curl -fsSL https://bun.sh/install | bash git clone https://gitcode.com/gh_mirrors/on/oneshot-gtm cd oneshot-gtm && bun install
  1. 初始化 workspace(画像与密钥均为可选字段)并做健康检查:
bun run cli -- init bun run cli -- doctor
  1. 构建一次仪表盘并启动:
bun run --cwd apps/web build bun run cli -- ui # 打开 http://127.0.0.1:3030

不想克隆仓库?直接bunx oneshot-gtm-server就能跑起仪表盘(见 apps/server/README.md)。docs/getting-started.md 则带你走通"描述业务 → 选信号源 → 选 play → 读第一份草稿"的四步路径。

七、给新手的架构要点回顾

问题oneshot-gtm 的答案
为什么必须用 Bun?bun:sqlite原生绑定 + 更快的冷启动,代码在 Node 下直接失败
状态存哪?每个 workspace 一个ledger.sqlite,跨 workspace 共享数据单独存放
如何保证数据不泄露?文件权限 600 + 仓库边界测试 + 事件日志永不出本机
如何升级不丢数据?PRAGMA user_version驱动的单事务迁移
如何控制成本?每次付费调用留签名回执,日花费上限config spend-ceiling
新手从哪读代码?先读 packages/prompts/ 的提示词,再看 packages/find/ 的信号源

一句话总结:oneshot-gtm 用 Bun Monorepo 保证了工程的统一与快速迭代,用 SQLite 账本把"代理系统的所有状态"收敛为一个可备份、可审计、可迁移的本地文件——这正是"本地优先(local-first)+ 按结果付费"架构的完整答卷。

【免费下载链接】oneshot-gtm

GTM agent for technical founders. Pay-per-result. Two surfaces: terminal CLI + local web dashboard.

项目地址:https://gitcode.com/gh_mirrors/on/oneshot-gtm
点击查看免费下载

相关推荐

上一篇:番茄小说下载器完全指南:从克隆到Docker多格式导出
下一篇:OpenArm 开源机械臂:7-DOF 双臂组装指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进