Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 19 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="docs/images/logo-dark.svg">
<img src="docs/images/logo.svg" alt="FlyBot" width="260">
<source media="(prefers-color-scheme: dark)" srcset="docs/images/logo-wordmark-dark.svg">
<img src="docs/images/logo-wordmark.svg" alt="FlyBot" width="420">
</picture>
</p>

<h1 align="center">FlyBot</h1>

<p align="center">
<b>A team of long-lived AI Bots on top of DeepSeek Harness, and they can talk to your friends' Bots.</b>
</p>

<p align="center">
<a href="https://github.com/ThinkFlowLab/FlyBot/actions/workflows/test.yml"><img alt="Test" src="https://github.com/ThinkFlowLab/FlyBot/actions/workflows/test.yml/badge.svg?branch=main"></a>
<img alt="DSH" src="https://img.shields.io/badge/DSH-0.2.0--rc.2-4D6BFE">
<a href="https://github.com/ThinkFlowLab/FlyBot/actions/workflows/nightly.yml"><img alt="Nightly" src="https://github.com/ThinkFlowLab/FlyBot/actions/workflows/nightly.yml/badge.svg?branch=main"></a>
<img alt="DSH" src="https://img.shields.io/badge/DSH-0.2.0--rc.2-3B5BFD">
<img alt="Node" src="https://img.shields.io/badge/node-%E2%89%A522.19-339933?logo=node.js&logoColor=white">
<a href="LICENSE"><img alt="License" src="https://img.shields.io/badge/license-Apache_2.0-green"></a>
</p>
Expand All @@ -38,6 +37,20 @@ the Bots of your friends over [Bot Social](https://github.com/Birfy/bot-social).
- **Bot tree** (`@flybot/tree`): Chief becomes the root of a tree of child Bots that delegate level by level.
- **Local first**: all data stays on your computer.

## How FlyBot is built

FlyBot has three layers ([#34](https://github.com/ThinkFlowLab/FlyBot/issues/34)):

1. **Upstream core** (`ds-bot/`): the [DS Bot](https://github.com/Fei2411/DeepSeek-Bot) plugin for
DeepSeek Harness, pinned to one upstream commit (`ds-bot.pin.json`) and kept identical to it.
2. **FlyBot patch layer** (`patches/`): a short, ordered series of FlyBot changes applied on top
of the pinned core (extension API, fixes not yet upstream). The result is `core/`.
3. **Plugins** (`plugins/`): FlyBot features as separate DSH plugins that use only the
extension API: `@flybot/social` (friends' Bots over Bot Social) and `@flybot/tree`
(a tree of delegate Bots).

That is why the core's own UI still says "DS Bot" in places: it is upstream's code.

## Install

### 1. Prerequisites
Expand Down Expand Up @@ -222,16 +235,6 @@ FlyBot is an independent community project. **It is not an official DeepSeek pro

Bots use your own model quota, and they read and write files and run commands when you ask them to. Check important actions yourself.

## License

[Apache License 2.0](LICENSE). Provided "as is", without any warranty. Third-party code and its license are listed in [NOTICE](NOTICE).

## Disclaimer

FlyBot is an independent community project. **It is not an official DeepSeek product**, and it is not affiliated with, endorsed by or sponsored by DeepSeek. "DeepSeek", "DeepSeek Harness" and the other names are trademarks of their owners. They are used here only to say what this plugin works with.

Bots use your own model quota, and they read and write files and run commands when you ask them to. Check important actions yourself.

## Plugin architecture

FlyBot is the core (`core/`) plus feature plugins. The core provides the extension API, so
Expand Down Expand Up @@ -269,7 +272,7 @@ plugins never patch it.
- Plugins: `plugins/social` (friends' Bots via Bot Social) and `plugins/tree`
(recursive Bot tree, maintained by recursive-bot; own `npm test` against `core/`).

CI (`.github/workflows/test.yml`) runs ds-bot's tests, which include every plugin's tests,
CI (`.github/workflows/test.yml`) runs the core's tests, which include every plugin's tests,
plus the client build check and lint.

### Social: homes, members and friends
Expand Down
20 changes: 14 additions & 6 deletions README.zh.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="docs/images/logo-dark.svg">
<img src="docs/images/logo.svg" alt="FlyBot" width="260">
<source media="(prefers-color-scheme: dark)" srcset="docs/images/logo-wordmark-dark.svg">
<img src="docs/images/logo-wordmark.svg" alt="FlyBot" width="420">
</picture>
</p>

# FlyBot
<p align="center"><b>基于 DeepSeek Harness 的长期 AI Bot 团队,还能和朋友的 Bot 聊天。</b></p>

[English](README.md) | 中文
<p align="center">
<a href="https://github.com/ThinkFlowLab/FlyBot/actions/workflows/test.yml"><img alt="Test" src="https://github.com/ThinkFlowLab/FlyBot/actions/workflows/test.yml/badge.svg?branch=main"></a>
<img alt="DSH" src="https://img.shields.io/badge/DSH-0.2.0--rc.2-3B5BFD">
<a href="LICENSE"><img alt="许可" src="https://img.shields.io/badge/许可-Apache_2.0-green"></a>
</p>

![版本](https://img.shields.io/badge/版本-v0.1_预览-blue) ![DSH](https://img.shields.io/badge/DSH-0.2.0--rc.2-4D6BFE) ![许可](https://img.shields.io/badge/许可-Apache_2.0-green)
<p align="center"><a href="README.md">English</a> | 中文</p>

FlyBot 是一个基于 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(简称 dsh)的 bot 插件,插件将 dsh 改造为 bot 形式。你可以与自己创建的 bot 交流、工作。

![和 Chief 说一句话,它就把 bot 和群聊建好](docs/images/team.png)

## 架构

FlyBot 分三层(见 [#34](https://github.com/ThinkFlowLab/FlyBot/issues/34)):上游 [DS Bot](https://github.com/Fei2411/DeepSeek-Bot) 核心(固定到某个上游提交,保持不改)、FlyBot 补丁层(按顺序应用的少量改动)、以及插件(`plugins/`:`@flybot/social`、`@flybot/tree`)。所以核心界面里有些地方仍显示 “DS Bot”。

## 安装

先装好 dsh,并在 dsh 里配好至少一个模型。FlyBot 直接使用 dsh 里的模型。
Expand All @@ -25,7 +33,7 @@ FlyBot 是一个基于 [DeepSeek Harness](https://github.com/deepseek-ai/deepsee
2. **包名或地址** 填 `ds-bot`。在中国大陆,**安装源** 选 **中国大陆镜像源**。
3. 点 **安装**,再点 **立即启用**。如果提示“下次启动生效”,完全退出 dsh 再打开。

连不上 npm 时,第 2 步也可以填 [Releases](https://github.com/ThinkFlowLab/FlyBot/releases) 里下载的 `ds-bot-0.1.5.tgz` 的完整路径,或者 `github:ThinkFlowLab/FlyBot#v0.1.5&path:/ds-bot`(需要装 Git)。
连不上 npm 时,第 2 步也可以填 [Releases](https://github.com/ThinkFlowLab/FlyBot/releases) 里下载的 `ds-bot-0.1.5.tgz` 的完整路径,或者 `github:ThinkFlowLab/FlyBot#main&path:/core`(需要装 Git)。

**dsh Web(Windows、macOS、Linux)**

Expand Down
Binary file modified docs/images/logo-16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/logo-180.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/logo-192.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/logo-32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/logo-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/images/logo-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/logo-wordmark-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/images/logo-wordmark-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/logo-wordmark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/images/logo-wordmark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/images/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
60 changes: 36 additions & 24 deletions scripts/logo.mjs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
#!/usr/bin/env node
// The FlyBot logo: a stylized fly seen from above, two swept wings over a rounded body
// with a pair of eye dots. Writes a light and a dark SVG, and, when a Chrome or Chromium
// is found (CHROME_BIN, else google-chrome, chromium or chromium-browser on PATH), PNG
// copies at the sizes the docs and app icons use.
// The FlyBot logo, "Wingbot": a chat-bubble Bot face with two swept wings on each side
// (a Bot that talks and flies). Flat colors on a 512 grid, so it reads at 16 px. Writes a
// light and a dark mark, a wordmark lockup of each, and, when a Chrome or Chromium is found
// (CHROME_BIN, else google-chrome, chromium or chromium-browser on PATH), PNG copies at the
// sizes the docs and app icons use.
//
// node scripts/logo.mjs [out dir] default: docs/images
import { execFileSync } from 'node:child_process'
Expand All @@ -12,30 +13,34 @@ import { join, resolve } from 'node:path'

const BOX = 512
const PALETTE = {
light: { wingA: '#4D6BFE', wingB: '#7FD3FF', body: '#1B2340', eye: '#FFFFFF', vein: '#FFFFFF' },
dark: { wingA: '#6A84FF', wingB: '#9BE0FF', body: '#E9EDFF', eye: '#141A30', vein: '#141A30' },
light: { brand: '#3B5BFD', wing: '#8FB2FF', face: '#FFFFFF', text: '#0F1530' },
dark: { brand: '#6E87FF', wing: '#A9C2FF', face: '#0F1530', text: '#EEF1FF' },
}
const PNG_SIZES = [16, 32, 180, 192, 512]

export function logo(mode = 'light') {
const c = PALETTE[mode]
// One wing, drawn on the right; the left one is its mirror.
const wing = 'M268 214 C 330 120, 452 92, 470 150 C 486 204, 404 268, 288 262 Z'
const vein = 'M282 236 C 340 200, 400 170, 448 152'
const lower = 'M276 262 C 340 270, 420 300, 410 346 C 400 388, 330 360, 280 300 Z'
const half = `<path d="${wing}" fill="url(#wing-${mode})"/><path d="${lower}" fill="url(#wing-${mode})" opacity=".55"/><path d="${vein}" fill="none" stroke="${c.vein}" stroke-opacity=".55" stroke-width="7" stroke-linecap="round"/>`
function mark(c) {
// The two wings on the left; the right pair is their mirror.
const wings = '<path d="M150 238 C 96 200, 40 196, 22 214 C 8 230, 40 262, 150 300 Z"/>'
+ '<path d="M150 318 C 104 300, 62 306, 54 322 C 48 338, 84 352, 150 352 Z"/>'
return [
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${BOX} ${BOX}" width="${BOX}" height="${BOX}" role="img" aria-label="FlyBot">`,
`<defs><linearGradient id="wing-${mode}" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="${c.wingA}"/><stop offset="1" stop-color="${c.wingB}"/></linearGradient></defs>`,
`<g>${half}</g><g transform="translate(${BOX} 0) scale(-1 1)">${half}</g>`,
`<rect x="214" y="168" width="84" height="236" rx="42" fill="${c.body}"/>`,
`<circle cx="256" cy="150" r="56" fill="${c.body}"/>`,
`<circle cx="234" cy="144" r="12" fill="${c.eye}"/><circle cx="278" cy="144" r="12" fill="${c.eye}"/>`,
`<path d="M232 300 H280 M236 340 H276" stroke="${c.eye}" stroke-opacity=".35" stroke-width="8" stroke-linecap="round"/>`,
'</svg>\n',
`<g fill="${c.wing}">${wings}</g><g fill="${c.wing}" transform="translate(${BOX} 0) scale(-1 1)">${wings}</g>`,
`<path d="M196 132 H316 A80 80 0 0 1 396 212 V300 A80 80 0 0 1 316 380 H250 L188 432 V374 A80 80 0 0 1 116 300 V212 A80 80 0 0 1 196 132 Z" fill="${c.brand}"/>`,
`<rect x="196" y="222" width="34" height="58" rx="17" fill="${c.face}"/><rect x="282" y="222" width="34" height="58" rx="17" fill="${c.face}"/>`,
`<circle cx="256" cy="100" r="22" fill="${c.brand}"/><rect x="248" y="104" width="16" height="34" fill="${c.brand}"/>`,
].join('')
}

export function logo(mode = 'light') {
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${BOX} ${BOX}" width="${BOX}" height="${BOX}" role="img" aria-label="FlyBot">${mark(PALETTE[mode])}</svg>\n`
}

export function wordmark(mode = 'light') {
const c = PALETTE[mode]
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1240 360" width="1240" height="360" role="img" aria-label="FlyBot">`
+ `<g transform="translate(24 24) scale(0.609)">${mark(c)}</g>`
+ `<text x="384" y="236" font-family="Inter, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif" font-weight="800" font-size="190" letter-spacing="-6" fill="${c.text}">Fly<tspan fill="${c.brand}">Bot</tspan></text></svg>\n`
}

const findChrome = () => {
if (process.env.CHROME_BIN) return process.env.CHROME_BIN
for (const name of ['google-chrome', 'chromium', 'chromium-browser']) {
Expand All @@ -44,13 +49,13 @@ const findChrome = () => {
return null
}

function rasterize(chrome, svgFile, pngFile, size) {
function rasterize(chrome, svgFile, pngFile, size, height = size) {
const dir = mkdtempSync(join(tmpdir(), 'flybot-logo-'))
try {
const html = join(dir, 'logo.html')
writeFileSync(html, `<html><body style="margin:0;background:transparent"><img src="file://${svgFile}" width="${size}" height="${size}" style="display:block"></body></html>`)
writeFileSync(html, `<html><body style="margin:0;background:transparent"><img src="file://${svgFile}" width="${size}" height="${height}" style="display:block"></body></html>`)
execFileSync(chrome, ['--headless', '--no-sandbox', '--disable-gpu', '--hide-scrollbars', '--default-background-color=00000000',
`--user-data-dir=${join(dir, 'profile')}`, `--window-size=${size},${size}`, `--screenshot=${pngFile}`, `file://${html}`], { stdio: 'ignore' })
`--user-data-dir=${join(dir, 'profile')}`, `--window-size=${size},${height}`, `--screenshot=${pngFile}`, `file://${html}`], { stdio: 'ignore' })
} finally { rmSync(dir, { recursive: true, force: true }) }
}

Expand All @@ -61,6 +66,9 @@ if (import.meta.url === `file://${process.argv[1]}`) {
const file = join(out, mode === 'light' ? 'logo.svg' : 'logo-dark.svg')
writeFileSync(file, logo(mode))
console.log(`wrote ${file}`)
const words = join(out, mode === 'light' ? 'logo-wordmark.svg' : 'logo-wordmark-dark.svg')
writeFileSync(words, wordmark(mode))
console.log(`wrote ${words}`)
}
const chrome = findChrome()
if (chrome === null) console.log('no Chrome found: skipped the PNG copies')
Expand All @@ -72,5 +80,9 @@ if (import.meta.url === `file://${process.argv[1]}`) {
}
rasterize(chrome, join(out, 'logo-dark.svg'), join(out, 'logo-dark.png'), 512)
console.log(`wrote ${join(out, 'logo-dark.png')}`)
for (const name of ['logo-wordmark', 'logo-wordmark-dark']) {
rasterize(chrome, join(out, `${name}.svg`), join(out, `${name}.png`), 1240, 360)
console.log(`wrote ${join(out, `${name}.png`)}`)
}
}
}
Loading