diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index d902cefe2..0eb69c829 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -1,5 +1,5 @@ name: Bug report / 报告 bug -description: Something in DS Bot does not work as it should. / DS Bot 有功能不正常。 +description: Something in FlyBot does not work as it should. / FlyBot 有功能不正常。 labels: [bug] body: - type: markdown @@ -8,11 +8,11 @@ body: **Please add a screenshot.** A screenshot of the whole window tells us more than a paragraph. A report with only one sentence cannot be fixed and may be closed. **请附截图。** 一张整窗截图比一段文字有用得多。只有一句话的报告修不了,可能会被直接关闭。 - Fastest: in DS Bot, **Settings → Feedback → Report on GitHub** fills in the diagnostics. - 最快:在 DS Bot 里点 **设置 → 反馈 → 在 GitHub 上报告**,诊断信息会自动填好。 + Fastest: in FlyBot, **Settings → Feedback → Report on GitHub** fills in the diagnostics. + 最快:在 FlyBot 里点 **设置 → 反馈 → 在 GitHub 上报告**,诊断信息会自动填好。 - Security problems: **Security** tab → **Report a vulnerability**. Questions and ideas: [Discussions](https://github.com/Fei2411/DeepSeek-Bot/discussions). - 安全问题请在 **Security** 页私下报告;提问和建议请发到 [Discussions](https://github.com/Fei2411/DeepSeek-Bot/discussions)。 + Security problems: **Security** tab → **Report a vulnerability**. Questions and ideas: [Discussions](https://github.com/ThinkFlowLab/FlyBot/discussions). + 安全问题请在 **Security** 页私下报告;提问和建议请发到 [Discussions](https://github.com/ThinkFlowLab/FlyBot/discussions)。 - type: textarea id: what attributes: @@ -39,7 +39,7 @@ body: id: diagnostics attributes: label: Diagnostics / 诊断信息 - description: In DS Bot, Settings → Feedback → Copy, then paste here. / 在 DS Bot 里点 设置 → 反馈 → 复制,粘贴到这里。 + description: In FlyBot, Settings → Feedback → Copy, then paste here. / 在 FlyBot 里点 设置 → 反馈 → 复制,粘贴到这里。 validations: required: true - type: checkboxes diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 8debed771..3444de6c0 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,8 +1,8 @@ blank_issues_enabled: false contact_links: - name: Questions & help - url: https://github.com/Fei2411/DeepSeek-Bot/discussions/new?category=q-a + url: https://github.com/ThinkFlowLab/FlyBot/discussions/new?category=q-a about: Installation, setup and usage questions — ask in Discussions, not Issues. - name: Feature requests & ideas - url: https://github.com/Fei2411/DeepSeek-Bot/discussions/new?category=ideas + url: https://github.com/ThinkFlowLab/FlyBot/discussions/new?category=ideas about: Suggest features and discuss anything that is not a bug — in Discussions. diff --git a/AGENTS.md b/AGENTS.md index 273bce9ed..ae24c258e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -22,7 +22,7 @@ FlyBot agent work uses a small, visible loop. Adapt the *habits* below; do not c ## What this project is -DS Bot is a team of long-lived Bots, built on DeepSeek Harness (DSH). It has two parts: +FlyBot is a team of long-lived Bots, built on DeepSeek Harness (DSH). It has two parts: - `ds-bot/`: the Bot features, written as a DSH plugin. The plugin runs in DSH Desktop, in DSH Web and in the desktop app. - `app/`: the desktop app for Windows and macOS. It runs a pinned DSH with the `ds-bot` plugin, in its own profile and its own `DSH_HOME`, and imports an existing team from the user's DSH. diff --git a/CHANGELOG.md b/CHANGELOG.md index c8b2fec5c..1dc3ec4a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ All notable changes to DS Bot are listed here. The format follows [Keep a Change - Bots no longer use DeepSeek Harness's own question card, which held the Bot until you answered it while messages from other Bots waited. - A Bot that messaged a Bot stuck on earlier work was told "Delivered" every time, with no sign that nothing was read. It is now told how many earlier messages wait unread and when the other Bot waits for your approval, and after 10 unread messages `message_bot` fails and tells it to report the stuck Bot to you. - On model providers that need every request to name its conversation, such as OpenCode Go, the memory panel, automatic memory updates, the context check of long chats and image reading for text-only models failed. They now work there too. -- The memory panel's summary, its "ask or update" and the memory review no longer stop when the Bot's model refuses the call or runs out of output budget: they try once more on the profile's default model. After you change the Bot's model, a failed summary is written again automatically. ([#116](https://github.com/Fei2411/DeepSeek-Bot/issues/116)) +- The memory panel's summary, its "ask or update" and the memory review no longer stop when the Bot's model refuses the call or runs out of output budget: they try once more on the profile's default model. After you change the Bot's model, a failed summary is written again automatically. ([#116](https://github.com/ThinkFlowLab/FlyBot/issues/116)) - With the `ptc` agent preset, the team view and a Bot's character show the tool the Bot runs from its code, an approval card says what that tool does, and a question card asked from code is kept in chat search and in the next part of the chat. In a group chat, a member that only reads the chat or checks the team no longer leaves the round as if it had gone off to work. - Switching from Bot mode back to Agent mode no longer leaves parts of the Bot interface on screen, such as the chat header or Bot messages without their styles, when one part fails to close. The other parts still close, and Bot mode opens normally again. - DS Bot no longer sets DeepSeek Harness's work-details mode (Settings → General) to the most detailed one, and no longer hides that setting. Bot chats stay flat in every mode, and your choice applies again outside them. If you had chosen another mode before, choose it again once: DS Bot cannot tell which one it was. @@ -146,10 +146,10 @@ If you used the legacy `dsh-bot` package, remove it before you install `ds-bot`. - Only one DSH can change a team at a time; a second DSH on the same DSH home opens it read-only ([Data and backups](ds-bot/README.md#data-and-backups)). - Group chats use many tokens: each member reads the group and answers in turn. -[Unreleased]: https://github.com/Fei2411/DeepSeek-Bot/compare/v0.1.5...HEAD -[0.1.5]: https://github.com/Fei2411/DeepSeek-Bot/compare/v0.1.4...v0.1.5 -[0.1.4]: https://github.com/Fei2411/DeepSeek-Bot/compare/v0.1.3...v0.1.4 -[0.1.3]: https://github.com/Fei2411/DeepSeek-Bot/compare/v0.1.2...v0.1.3 -[0.1.2]: https://github.com/Fei2411/DeepSeek-Bot/compare/v0.1.1...v0.1.2 -[0.1.1]: https://github.com/Fei2411/DeepSeek-Bot/compare/v0.1.0...v0.1.1 -[0.1.0]: https://github.com/Fei2411/DeepSeek-Bot/releases/tag/v0.1.0 +[Unreleased]: https://github.com/ThinkFlowLab/FlyBot/compare/v0.1.5...HEAD +[0.1.5]: https://github.com/ThinkFlowLab/FlyBot/compare/v0.1.4...v0.1.5 +[0.1.4]: https://github.com/ThinkFlowLab/FlyBot/compare/v0.1.3...v0.1.4 +[0.1.3]: https://github.com/ThinkFlowLab/FlyBot/compare/v0.1.2...v0.1.3 +[0.1.2]: https://github.com/ThinkFlowLab/FlyBot/compare/v0.1.1...v0.1.2 +[0.1.1]: https://github.com/ThinkFlowLab/FlyBot/compare/v0.1.0...v0.1.1 +[0.1.0]: https://github.com/ThinkFlowLab/FlyBot/releases/tag/v0.1.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9b1d12d4d..e8129a6a7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Contributing to DS Bot +# Contributing to FlyBot Thank you for helping. This file explains how to set up the plugin, how to test a change and which rules a change must follow. For what the plugin does and how users install it, see [README.md](README.md). @@ -47,7 +47,7 @@ You need: - For the test bench only: pnpm `11` and a checkout of DeepSeek Harness at the DSH version that the plugin is tested with. ```sh -git clone https://github.com/Fei2411/DeepSeek-Bot.git ds-bot-repo +git clone https://github.com/ThinkFlowLab/FlyBot.git ds-bot-repo cd ds-bot-repo/ds-bot # The plugin's own build tool (esbuild only). @@ -124,21 +124,21 @@ For a change to the Bot browser, check it in DSH Desktop. On Linux, the scripts ## Report a problem -The easiest way is from DS Bot itself: open **Settings → Feedback**, write what happened and select **Report on GitHub**. It opens a new issue with the DS Bot and DSH versions, your system, the models and the recent errors already filled in. **Copy** puts the same text on the clipboard, and **Download** saves the full error log (`/bot/logs/diagnostics.jsonl`). +The easiest way is from FlyBot itself: open **Settings → Feedback**, write what happened and select **Report on GitHub**. It opens a new issue with the core (`ds-bot`) and DSH versions, your system, the models and the recent errors already filled in. **Copy** puts the same text on the clipboard, and **Download** saves the full error log (`/bot/logs/diagnostics.jsonl`). For now the core's **Report on GitHub** button opens the issue form of the upstream `ds-bot` repository (Fei2411/DeepSeek-Bot). Problems with FlyBot or its plugins belong in [ThinkFlowLab/FlyBot issues](https://github.com/ThinkFlowLab/FlyBot/issues): copy the report there instead (see #33 / #34). If you open an issue by hand, include: - The DSH version (DSH Desktop or `@deepseek-ai/dsh`), your operating system, and whether you use DSH Desktop or DSH Web. -- How you installed DS Bot (npm, release file, Git address or command line) and its version. +- How you installed FlyBot (npm, release file, Git address or command line) and its version. - What you did, what you expected and what happened, with the exact error text. - A screenshot of the whole window, with the chat and the error. - The steps that make it happen again. -DS Bot removes API keys, tokens, saved secrets and your home folder from its diagnostics. Read the text before you post it, and remove personal data from screenshots. +FlyBot removes API keys, tokens, saved secrets and your home folder from its diagnostics. Read the text before you post it, and remove personal data from screenshots. For a security problem, do not open a public issue. Use **Report a vulnerability** on the repository's **Security** tab. -Issues are for bug reports. Questions, ideas and feature requests belong in [Discussions](https://github.com/Fei2411/DeepSeek-Bot/discussions). +Issues are for bug reports. Questions, ideas and feature requests belong in [Discussions](https://github.com/ThinkFlowLab/FlyBot/discussions). ## Agent docs map @@ -152,4 +152,4 @@ Issues are for bug reports. Questions, ideas and feature requests belong in [Dis ## License -DS Bot is licensed under the [Apache License 2.0](LICENSE). When you submit a contribution, you agree that it is licensed under the same license, as section 5 of the license says. +FlyBot is licensed under the [Apache License 2.0](LICENSE). When you submit a contribution, you agree that it is licensed under the same license, as section 5 of the license says. diff --git a/README.md b/README.md index 961e424e2..8f89d9209 100644 --- a/README.md +++ b/README.md @@ -1,44 +1,149 @@

- DeepSeek Bot + FlyBot

-# DeepSeek Bot +

FlyBot

-English | [中文](README.zh.md) +

+ A team of long-lived AI Bots on top of DeepSeek Harness, and they can talk to your friends' Bots. +

+ +

+ Test + DSH + Node + License +

-![version](https://img.shields.io/badge/version-v0.1_preview-blue) ![DSH](https://img.shields.io/badge/DSH-0.2.0--rc.2-4D6BFE) ![license](https://img.shields.io/badge/license-Apache_2.0-green) +

English | 中文

-DeepSeek Bot (DS Bot) is a bot plugin for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (DSH). It turns DSH into bots. You create your own bots, then talk and work with them. +FlyBot turns [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (DSH) into a +team of Bots. Each Bot keeps its own chat, model, memory and tools for as long as you want. +A Main Bot, **Chief**, sets up the rest of the team for you. Bots hand work to each other, +meet in group chats, and with the plugins they grow into a tree of delegates and chat with +the Bots of your friends over [Bot Social](https://github.com/Birfy/bot-social). ![Say one sentence to Chief, and it sets up the bots and the group chat](docs/images/team.png) +## Highlights + +- **Long-lived Bots**: one chat per Bot that never runs out of context. FlyBot condenses and hands off on its own. +- **Chief, your Main Bot**: talk to it and it creates Bots, group chats and settings for you. +- **Group chats**: an admin answers by default and calls in the others; `@` any Bot to ask it directly. +- **Memory, capabilities, usage**: per-Bot memory, a shared library of MCP servers and skills, token usage by Bot and model. +- **Friends' Bots** (`@flybot/social`): chat with Bots in other FlyBot homes; everything they send is fenced as untrusted, and what your Bots send can wait for your approval. +- **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. + ## Install -First install DSH and set up at least one model in it. DS Bot uses the models from DSH. +### 1. Prerequisites + +- **Node.js 22.19 or later** and **pnpm** (`npm install -g pnpm`, or `corepack enable`). +- **Git**, to install from GitHub. +- An API key for at least one model provider (for example DeepSeek). FlyBot uses the models you set up in DSH. + +### 2. Add FlyBot and its plugins to a DSH profile + +DSH keeps plugins per profile, under `$DSH_HOME/profiles/` (`~/.dsh/profiles/web` for the +`web` profile). `dsh plugin --profile …` runs pnpm in that folder. + +```sh +npx -y @deepseek-ai/dsh@0.2.0-rc.2 plugin --profile web add "github:ThinkFlowLab/FlyBot#main&path:/ds-bot" +npx -y @deepseek-ai/dsh@0.2.0-rc.2 plugin --profile web add "github:ThinkFlowLab/FlyBot#main&path:/ds-bot/plugins/social" +npx -y @deepseek-ai/dsh@0.2.0-rc.2 plugin --profile web add "github:ThinkFlowLab/FlyBot#main&path:/ds-bot/plugins/tree" +``` + +The first line is FlyBot itself (package `ds-bot`); the plugins are optional. Each one is added +to the profile's `package.json` and to its bundle list, so nothing else is needed to mount it. +The released core is also on npm: `… plugin --profile web add ds-bot`. + +**DSH Desktop (Windows, macOS):** **Plugins** → **Add plugin**, enter `ds-bot` (or one of the +`github:` addresses above), then **Install** and **Enable now**. Quit and reopen DSH if it asks. + +### 3. Run the web app + +```sh +npx -y @deepseek-ai/dsh@0.2.0-rc.2 web --port 3080 +``` + +It prints `dsh web: http://127.0.0.1:3080/?token=…` and opens it. Add `--no-open` to skip the +browser, `--host` to bind another address. The token in the link is what lets the browser in. + +### 4. Set up a model -**DSH Desktop (Windows, macOS)** +The first time you open Chief's chat it asks for a model: choose **Enter a DeepSeek API key**, +or **Open settings to set up a custom model** for another provider. Later, use a Bot's model +menu → **Models and providers…**, or **Settings → Models, providers, and the rest of DeepSeek +Harness**. DSH stores the key with its credentials service; you can instead export it in the +environment that starts DSH (for DeepSeek: `DEEPSEEK_API_KEY`). Each Bot can use its own model. -1. In the sidebar, select **Plugins** → **Add plugin**. -2. In **Package name or address**, enter `ds-bot`. In mainland China, set **Registry** to **Mainland China mirror**. -3. Select **Install**, then **Enable now**. If DSH says the change takes effect at the next start, quit DSH completely and open it again. +### 5. Connect Bot Social (for friends' Bots) -If npm is not reachable, in step 2 you can also enter the full path of `ds-bot-0.1.5.tgz` downloaded from [Releases](https://github.com/Fei2411/DeepSeek-Bot/releases), or `github:Fei2411/DeepSeek-Bot#v0.1.5&path:/ds-bot` (needs Git). +`@flybot/social` stays idle until it knows a Bot Social server. Add this to +`~/.dsh/profiles/web/cordis.patch.yml`, then restart DSH: + +```yaml +- id: flybot-social + config: + server: https://your-bot-social.example + displayName: Your Name +``` -**DSH Web (Windows, macOS, Linux)** +On first start it registers your **home** (e.g. `@yourname.home`) and every local Bot as a +member, and saves its credentials in `~/.dsh/bot/flybot-social.json` (mode 600). To add a +friend, right-click one of your Bots → **Copy invite link** (or **Copy invite with +instructions**) and send it; your friend pastes it in **Friends' Bots → + Add**. + +## Update + +```sh +cd ~/.dsh/profiles/web && pnpm update ds-bot @flybot/social @flybot/tree +``` -Needs Node.js `22.19` or later, and pnpm. +or, without changing directory, `npx -y @deepseek-ai/dsh@0.2.0-rc.2 plugin --profile web update ds-bot @flybot/social @flybot/tree`. +Then restart DSH (or the launchd service below). Your Bots and chats are kept. + +## Run as a macOS service (launchd) + +Save as `~/Library/LaunchAgents/com.flybot.web.plist` (replace `YOU` with your user name): + +```xml + + + + Labelcom.flybot.web + ProgramArguments + /bin/zsh-lc + export PATH=/opt/homebrew/bin:$PATH; cd $HOME && exec npx -y @deepseek-ai/dsh@0.2.0-rc.2 web --no-open --port 3080 + + RunAtLoad + KeepAlive + StandardOutPath/Users/YOU/flybot-web.log + StandardErrorPath/Users/YOU/flybot-web.log + +``` ```sh -npx @deepseek-ai/dsh@0.2.0-rc.2 plugin --profile web add ds-bot -npx @deepseek-ai/dsh@0.2.0-rc.2 web +launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.flybot.web.plist # start now and at login +launchctl kickstart -k gui/$(id -u)/com.flybot.web # restart (e.g. after an update) +launchctl bootout gui/$(id -u)/com.flybot.web # stop and unload +open "$(grep -o 'http://127.0.0.1:3080/?token=[^ ]*' ~/flybot-web.log | tail -1)" # open the UI ``` -## Setup +Each start prints a fresh token link to the log; the last one is the current one. + +## Troubleshooting -I strongly recommend that you set up your other bots and group chats by just talking to a bot. +- **The page says unauthorized (401):** open the newest `?token=` link from the console or log; old links stop working after a restart. +- **"No API key for provider route …":** set the key on DSH's Models page (Bot model menu → **Models and providers…**), or export it in the environment that starts DSH (for launchd, in the plist command). +- **A Bot shows "… is not available anymore":** its model is no longer served; pick another model in the chat or in the Bot's details. +- **Friends' Bots stays empty:** check `server` in `cordis.patch.yml` and the log for `flybot-social` lines; without a server the plugin stays idle by design. +- **A plugin fails to start:** FlyBot logs one warning, the plugin's UI simply does not show, and the rest keeps working. Run the update above and restart. +- **Something else:** **Settings → Feedback** collects diagnostics; see also [known issues](docs/known-issues.md). ## Features @@ -52,13 +157,13 @@ Bots also have memory. A bot remembers what you like and uses it in later work. ### Main bot -The main bot is your chief. It has very high rights: it manages the other bots and the group chats. The first time you open DS Bot, there is already a main bot called Chief. Talk to it, and it walks you through the setup. +The main bot is your chief. It has very high rights: it manages the other bots and the group chats. The first time you open FlyBot, there is already a main bot called Chief. Talk to it, and it walks you through the setup. In short: have a question? Ask the main bot. ### Group chats -DS Bot has group chats. A group chat is a great way to get several bots to work together, but it clearly costs more. So DS Bot does this: every group chat has an admin. The admin answers your messages by default and calls in the other bots for you. You can also @ a bot directly, and it answers you directly: type @ and pick the bot from the list. To give the bots another chat as context, use + → Reference a chat in the message box. +FlyBot has group chats. A group chat is a great way to get several bots to work together, but it clearly costs more. So FlyBot does this: every group chat has an admin. The admin answers your messages by default and calls in the other bots for you. You can also @ a bot directly, and it answers you directly: type @ and pick the bot from the list. To give the bots another chat as context, use + → Reference a chat in the message box. Any bot can hand work to another bot with @. That bot does the work on its own, and the group goes on. While it works, the group, the sidebar and the bot's own chat show what it is doing. When it is done, it posts the result in the group, and the bot that asked for the work answers. To stop the work, open the group's details. @@ -76,7 +181,8 @@ In **Settings** → **Usage**, you can see token usage by time, by bot and by mo ## Data -All data stays on your own computer, in `bot/` and `sessions/` in the DSH home folder (`~/.dsh` by default). Uninstalling the plugin does not delete this data. +Everything stays on your computer, in `bot/` and `sessions/` in the DSH home folder (`~/.dsh` +by default). Uninstalling the plugin does not delete it. ## Contributing (FlyBot) @@ -85,20 +191,24 @@ Agent and maintainer loop: see [AGENTS.md](AGENTS.md), [LESSONS.md](LESSONS.md), ## Development -The plugin source is in [`ds-bot/`](ds-bot/). For options and internals, see [`ds-bot/README.md`](ds-bot/README.md). For development and tests, see [CONTRIBUTING.md](CONTRIBUTING.md). For changes between versions, see [CHANGELOG.md](CHANGELOG.md). +The core lives in [`ds-bot/`](ds-bot/) and the plugins in [`ds-bot/plugins/`](ds-bot/plugins/). +See [`ds-bot/README.md`](ds-bot/README.md) for options and internals, [CONTRIBUTING.md](CONTRIBUTING.md) +for development, and [CHANGELOG.md](CHANGELOG.md) for history. CI +([`.github/workflows/test.yml`](.github/workflows/test.yml)) runs the tests of the core and every +plugin, the client build check and lint on Ubuntu and Windows. ## Acknowledgments -- [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness): DS Bot is built on the DSH plugin system. Every bot is a DSH session and uses the DSH tools and models. +- [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness): FlyBot is built on the DSH plugin system. Every bot is a DSH session and uses the DSH tools and models. - [esbuild](https://esbuild.github.io/): bundles the plugin's interface code. -- Thanks to these projects for the ideas they published. DS Bot learned from their designs and does not contain their code: +- Thanks to these projects for the ideas they published. FlyBot learned from their designs and does not contain their code: - [AgentMemoryRepo](https://github.com/AgentMemoryRepo/agentmemoryrepo): the Markdown format of Bot memory. - [OpenAI Codex](https://github.com/openai/codex): checkpoints that keep the user's messages next to the summary, and message anchors for chat search. - [OpenMausBot](https://github.com/milind-soni/OpenMausBot): group chat rounds and @mention relays. - [Hermes Agent](https://github.com/NousResearch/hermes-agent) and [Hermes Bot Mode](https://github.com/NousResearch/Hermes-Bot-Mode): one session per member per group chat, and handoffs that carry the earlier user messages. - [OpenCode](https://github.com/sst/opencode), [goose](https://github.com/block/goose), [pi](https://github.com/badlogic/pi-mono) and [Gemini CLI](https://github.com/google-gemini/gemini-cli): when to condense a long chat, and what a handoff note holds. - [obelisk](https://github.com/tommy0103/obelisk): chat search with a SQLite full-text index. -- Everyone who tries DS Bot, asks questions and reports bugs. +- Everyone who tries FlyBot, asks questions and reports bugs. ## License @@ -106,29 +216,24 @@ The plugin source is in [`ds-bot/`](ds-bot/). For options and internals, see [`d ## Disclaimer -DS Bot 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. +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. -## FlyBot plugin architecture +## License -FlyBot is DS Bot (`ds-bot/`) plus feature plugins. ds-bot itself provides the extension -API, so plugins never patch it. +[Apache License 2.0](LICENSE). Provided "as is", without any warranty. Third-party code and its license are listed in [NOTICE](NOTICE). -### Install FlyBot (clean profile) +## Disclaimer -Needs Node.js >= 22.19 and pnpm. Add ds-bot and the two plugins to a DSH profile, then -enable them in that profile's `cordis.patch.yml` (with the Bot Social server URL for social): +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. -```sh -npx @deepseek-ai/dsh plugin --profile web add "github:Birfy/FlyBot#flybot-plugins&path:/ds-bot" -npx @deepseek-ai/dsh plugin --profile web add "github:Birfy/FlyBot#flybot-plugins&path:/ds-bot/plugins/social" -npx @deepseek-ai/dsh plugin --profile web add "github:Birfy/FlyBot#flybot-plugins&path:/ds-bot/plugins/tree" -``` +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 -Update later with `pnpm update ds-bot @flybot/social @flybot/tree` inside the profile -folder, then restart DSH. A plugin that fails to start logs one warning; its UI slots -draw nothing and the rest of ds-bot keeps working. +FlyBot is the core (`ds-bot/`) plus feature plugins. The core provides the extension API, so +plugins never patch it. ### Host API (`ctx.flybot`) diff --git a/README.zh.md b/README.zh.md index 976d6fb53..c7331170b 100644 --- a/README.zh.md +++ b/README.zh.md @@ -1,23 +1,23 @@

- DeepSeek Bot + FlyBot

-# DeepSeek Bot +# FlyBot [English](README.md) | 中文 ![版本](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) -DeepSeek Bot(简称 DS Bot)是一个基于 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(简称 dsh)的 bot 插件,插件将 dsh 改造为 bot 形式。你可以与自己创建的 bot 交流、工作。 +FlyBot 是一个基于 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(简称 dsh)的 bot 插件,插件将 dsh 改造为 bot 形式。你可以与自己创建的 bot 交流、工作。 -![和 Chief 说一句话,它就把 bot 和群聊建好](docs/images/team.zh.png) +![和 Chief 说一句话,它就把 bot 和群聊建好](docs/images/team.png) ## 安装 -先装好 dsh,并在 dsh 里配好至少一个模型。ds bot 直接使用 dsh 里的模型。 +先装好 dsh,并在 dsh 里配好至少一个模型。FlyBot 直接使用 dsh 里的模型。 **dsh Desktop(Windows、macOS)** @@ -25,7 +25,7 @@ DeepSeek Bot(简称 DS Bot)是一个基于 [DeepSeek Harness](https://github 2. **包名或地址** 填 `ds-bot`。在中国大陆,**安装源** 选 **中国大陆镜像源**。 3. 点 **安装**,再点 **立即启用**。如果提示“下次启动生效”,完全退出 dsh 再打开。 -连不上 npm 时,第 2 步也可以填 [Releases](https://github.com/Fei2411/DeepSeek-Bot/releases) 里下载的 `ds-bot-0.1.5.tgz` 的完整路径,或者 `github:Fei2411/DeepSeek-Bot#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#v0.1.5&path:/ds-bot`(需要装 Git)。 **dsh Web(Windows、macOS、Linux)** @@ -48,21 +48,21 @@ npx @deepseek-ai/dsh@0.2.0-rc.2 web 另外,bot 拥有记忆,它会记住你的喜好并在下次的工作中应用。你可以在 bot 的详情页管理记忆,也可以直接和 bot 对话来进行管理。 -![bot 详情页:模型、记忆、用量](docs/images/details.zh.png) +![bot 详情页:模型、记忆、用量](docs/images/details.png) ### Main bot -这个 bot 作为你的 chief,它拥有非常高的权限,可以管理其他 bot 和群聊。第一次进入 ds bot 时,默认会有一个 main bot 叫 Chief。你可以直接和它对话,让它引导你完成配置。 +这个 bot 作为你的 chief,它拥有非常高的权限,可以管理其他 bot 和群聊。第一次进入 FlyBot 时,默认会有一个 main bot 叫 Chief。你可以直接和它对话,让它引导你完成配置。 总之,有问题,找 main bot。 ### 群聊 -ds bot 拥有群聊功能。群聊是非常好的多 bot 协作方式,但它会带来非常明显的费用问题。ds bot 为此特意做了优化:在一个群聊中,你需要设置一个管理员。你的消息默认由管理员回复,它会帮你调用其他 bot。你也可以直接 @ 你需要沟通的 bot,它会直接回应你:输入 @,从列表里选 bot 就行。想让 bot 参考另一段对话,点输入框的 + → 引用对话。 +FlyBot 拥有群聊功能。群聊是非常好的多 bot 协作方式,但它会带来非常明显的费用问题。FlyBot 为此特意做了优化:在一个群聊中,你需要设置一个管理员。你的消息默认由管理员回复,它会帮你调用其他 bot。你也可以直接 @ 你需要沟通的 bot,它会直接回应你:输入 @,从列表里选 bot 就行。想让 bot 参考另一段对话,点输入框的 + → 引用对话。 任何 bot 都可以用 @ 把活派给另一个 bot。接活的 bot 自己去做,群聊照常进行。它干活时,群聊、侧边栏和它自己的聊天里都能看到它在做什么。做完后,它把结果发到群里,由派活的 bot 接着回应。要停下它的工作,打开群聊详情。 -![群聊:管理员 Chief 把活派给写手和程序员](docs/images/group.zh.png) +![群聊:管理员 Chief 把活派给写手和程序员](docs/images/group.png) ### 能力 @@ -72,7 +72,7 @@ ds bot 拥有群聊功能。群聊是非常好的多 bot 协作方式,但它 在 设置 → 用量 里,可以按时间、按 bot、按模型查看 Token 用量。 -![用量页](docs/images/usage.zh.png) +![用量页](docs/images/usage.png) ## 数据 @@ -84,16 +84,16 @@ ds bot 拥有群聊功能。群聊是非常好的多 bot 协作方式,但它 ## 致谢 -- [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness):ds bot 建在 dsh 的插件系统上。每个 bot 都是一个 dsh 会话,用的是 dsh 的工具和模型。 +- [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness):FlyBot 建在 dsh 的插件系统上。每个 bot 都是一个 dsh 会话,用的是 dsh 的工具和模型。 - [esbuild](https://esbuild.github.io/):用来打包插件的界面代码。 -- 感谢下面这些项目公开的设计思路。ds bot 参考了它们的思路,没有使用它们的代码: +- 感谢下面这些项目公开的设计思路。FlyBot 参考了它们的思路,没有使用它们的代码: - [AgentMemoryRepo](https://github.com/AgentMemoryRepo/agentmemoryrepo):bot 记忆的 Markdown 格式。 - [OpenAI Codex](https://github.com/openai/codex):压缩时把用户原话留在摘要旁边,以及搜索聊天记录用的消息锚点。 - [OpenMausBot](https://github.com/milind-soni/OpenMausBot):群聊轮流发言和 @ 接力。 - [Hermes Agent](https://github.com/NousResearch/hermes-agent) 和 [Hermes Bot Mode](https://github.com/NousResearch/Hermes-Bot-Mode):每个成员在每个群里一个会话,以及交接时带上用户之前的原话。 - [OpenCode](https://github.com/sst/opencode)、[goose](https://github.com/block/goose)、[pi](https://github.com/badlogic/pi-mono)、[Gemini CLI](https://github.com/google-gemini/gemini-cli):长对话什么时候压缩,交接笔记写哪些内容。 - [obelisk](https://github.com/tommy0103/obelisk):用 SQLite 全文索引搜索聊天记录。 -- 每一位试用 ds bot、提问题、报 bug 的人。 +- 每一位试用 FlyBot、提问题、报 bug 的人。 ## 许可 @@ -101,13 +101,13 @@ ds bot 拥有群聊功能。群聊是非常好的多 bot 协作方式,但它 ## 声明 -ds bot 是独立的社区项目,**不是 DeepSeek 官方产品**,与 DeepSeek 没有隶属、认可或赞助关系。“DeepSeek”“DeepSeek Harness”等名称是各自所有者的商标,这里只用来说明本插件和什么一起用。 +FlyBot 是独立的社区项目,**不是 DeepSeek 官方产品**,与 DeepSeek 没有隶属、认可或赞助关系。“DeepSeek”“DeepSeek Harness”等名称是各自所有者的商标,这里只用来说明本插件和什么一起用。 bot 会消耗你自己的模型额度,也会按你的要求读写文件、运行命令。重要的操作请自己检查。 ## FlyBot 插件架构 -FlyBot = DS Bot(`ds-bot/`)+ 功能插件。扩展接口由 ds-bot 自己提供,插件无需改动 ds-bot。 +FlyBot = FlyBot(`ds-bot/`)+ 功能插件。扩展接口由 ds-bot 自己提供,插件无需改动 ds-bot。 ### 安装 FlyBot(全新 profile) @@ -115,9 +115,9 @@ FlyBot = DS Bot(`ds-bot/`)+ 功能插件。扩展接口由 ds-bot 自己提 `cordis.patch.yml` 里启用(social 需要填 Bot Social 服务器地址): ```sh -npx @deepseek-ai/dsh plugin --profile web add "github:Birfy/FlyBot#flybot-plugins&path:/ds-bot" -npx @deepseek-ai/dsh plugin --profile web add "github:Birfy/FlyBot#flybot-plugins&path:/ds-bot/plugins/social" -npx @deepseek-ai/dsh plugin --profile web add "github:Birfy/FlyBot#flybot-plugins&path:/ds-bot/plugins/tree" +npx @deepseek-ai/dsh plugin --profile web add "github:ThinkFlowLab/FlyBot#main&path:/ds-bot" +npx @deepseek-ai/dsh plugin --profile web add "github:ThinkFlowLab/FlyBot#main&path:/ds-bot/plugins/social" +npx @deepseek-ai/dsh plugin --profile web add "github:ThinkFlowLab/FlyBot#main&path:/ds-bot/plugins/tree" ``` 之后在 profile 目录里运行 `pnpm update ds-bot @flybot/social @flybot/tree` 更新,再重启 DSH。 diff --git a/docs/images/details.png b/docs/images/details.png index 5e7f3b7c5..240f8c8c1 100644 Binary files a/docs/images/details.png and b/docs/images/details.png differ diff --git a/docs/images/details.zh.png b/docs/images/details.zh.png deleted file mode 100644 index 9277c381f..000000000 Binary files a/docs/images/details.zh.png and /dev/null differ diff --git a/docs/images/group.png b/docs/images/group.png index ba816c0ff..538a3d071 100644 Binary files a/docs/images/group.png and b/docs/images/group.png differ diff --git a/docs/images/group.zh.png b/docs/images/group.zh.png deleted file mode 100644 index c02958778..000000000 Binary files a/docs/images/group.zh.png and /dev/null differ diff --git a/docs/images/logo-16.png b/docs/images/logo-16.png new file mode 100644 index 000000000..6a2a3947c Binary files /dev/null and b/docs/images/logo-16.png differ diff --git a/docs/images/logo-180.png b/docs/images/logo-180.png new file mode 100644 index 000000000..7dab53bad Binary files /dev/null and b/docs/images/logo-180.png differ diff --git a/docs/images/logo-192.png b/docs/images/logo-192.png new file mode 100644 index 000000000..d00f0bf05 Binary files /dev/null and b/docs/images/logo-192.png differ diff --git a/docs/images/logo-32.png b/docs/images/logo-32.png new file mode 100644 index 000000000..83e0fc23c Binary files /dev/null and b/docs/images/logo-32.png differ diff --git a/docs/images/logo-dark.png b/docs/images/logo-dark.png new file mode 100644 index 000000000..df6342bae Binary files /dev/null and b/docs/images/logo-dark.png differ diff --git a/docs/images/logo-dark.svg b/docs/images/logo-dark.svg index f3db3abea..ae8d986aa 100644 --- a/docs/images/logo-dark.svg +++ b/docs/images/logo-dark.svg @@ -1 +1 @@ - + diff --git a/docs/images/logo.png b/docs/images/logo.png new file mode 100644 index 000000000..f7f798406 Binary files /dev/null and b/docs/images/logo.png differ diff --git a/docs/images/logo.svg b/docs/images/logo.svg index f77dc7592..46b11f99c 100644 --- a/docs/images/logo.svg +++ b/docs/images/logo.svg @@ -1 +1 @@ - + diff --git a/docs/images/team.png b/docs/images/team.png index ab889b6b0..52d92ac73 100644 Binary files a/docs/images/team.png and b/docs/images/team.png differ diff --git a/docs/images/team.zh.png b/docs/images/team.zh.png deleted file mode 100644 index 75e145d10..000000000 Binary files a/docs/images/team.zh.png and /dev/null differ diff --git a/docs/images/usage.png b/docs/images/usage.png index 121c399d7..104cc7f06 100644 Binary files a/docs/images/usage.png and b/docs/images/usage.png differ diff --git a/docs/images/usage.zh.png b/docs/images/usage.zh.png deleted file mode 100644 index 68c592b09..000000000 Binary files a/docs/images/usage.zh.png and /dev/null differ diff --git a/docs/known-issues.md b/docs/known-issues.md index 4fe39a2e9..edd74b688 100644 --- a/docs/known-issues.md +++ b/docs/known-issues.md @@ -14,29 +14,29 @@ English | [中文](known-issues.zh.md) | `@Anvil你看下` (no space after the name) | a file "Anvil你看下" | | `@Anvil,帮我查` (punctuation after the name) | a file "Anvil,帮我查" | -**What is not affected:** who answers. DS Bot reads "@Name" by its own rule, so the Bot you named is called. When the message is sent, it shows the Bot's mark and name as usual. +**What is not affected:** who answers. FlyBot reads "@Name" by its own rule, so the Bot you named is called. When the message is sent, it shows the Bot's mark and name as usual. **Tip:** put a space after the name (`@Anvil 你看下`). Then a name without a space shows as the Bot while it waits, too. **Why:** DeepSeek Harness draws these waiting rows itself and reads "@" up to the next space. A plugin cannot change how they are drawn yet. -## DS Bot does not load: "disabling profile plugin row "bot"" +## FlyBot does not load: "disabling profile plugin row "bot"" -**Where:** DSH Web (or Desktop) starts, but DS Bot is not there. The log has this line: +**Where:** DSH Web (or Desktop) starts, but FlyBot is not there. The log has this line: ```text dsh: disabling profile plugin row "bot": its declared peer dependencies cannot be validated: Cannot read properties of undefined (reading 'startsWith') ``` -Installing another version of DS Bot does not help. +Installing another version of FlyBot does not help. -**Why:** before it loads a plugin, DeepSeek Harness checks each plugin row of the profile. This message means a row has the id `bot` but no package name. DS Bot adds its own row with the name `ds-bot`, so the row without a name most likely comes from a file in the profile folder, such as an override `- id: bot` in `cordis.patch.yml` that no longer matches DS Bot's row. We could not reproduce this, so we do not know yet how such a row is left behind. +**Why:** before it loads a plugin, DeepSeek Harness checks each plugin row of the profile. This message means a row has the id `bot` but no package name. FlyBot adds its own row with the name `ds-bot`, so the row without a name most likely comes from a file in the profile folder, such as an override `- id: bot` in `cordis.patch.yml` that no longer matches FlyBot's row. We could not reproduce this, so we do not know yet how such a row is left behind. **What to do:** the profile folder is `~/.dsh/profiles/web/` (Windows: `C:\Users\\.dsh\profiles\web\`), or `profiles/web/` in `DSH_HOME` when it is set. For Desktop, use `profiles/desktop/` instead. 1. Quit DSH. 2. Open `cordis.patch.yml` in the profile folder. Look for a row with `id: bot` that has no `name: ds-bot` line, and remove that row. Keep a copy of the file first. -3. Check that `package.json` in the same folder lists `ds-bot` under `dependencies`. If it does not, add DS Bot again: `npx @deepseek-ai/dsh@0.2.0-rc.2 plugin --profile web add ds-bot`. +3. Check that `package.json` in the same folder lists `ds-bot` under `dependencies`. If it does not, add FlyBot again: `npx @deepseek-ai/dsh@0.2.0-rc.2 plugin --profile web add ds-bot`. 4. Start DSH again. To see where the row comes from, run `npx @deepseek-ai/dsh@0.2.0-rc.2 --profile web --dump-config`. It prints every plugin row with the file that supplied it. If you report the problem, copy only the rows with `id: bot` and the file names; leave out keys and paths you do not want to share. diff --git a/docs/known-issues.zh.md b/docs/known-issues.zh.md index 09fb484e5..45c3d4841 100644 --- a/docs/known-issues.zh.md +++ b/docs/known-issues.zh.md @@ -14,29 +14,29 @@ | `@Anvil你看下`(名字后面没空格) | 文件「Anvil你看下」 | | `@Anvil,帮我查`(名字后面直接跟标点) | 文件「Anvil,帮我查」 | -**不受影响的:** 谁来回答。DS Bot 用自己的规则读「@名字」,被点名的 Bot 照样会被叫到。消息发出以后,会正常显示 Bot 的头像和名字。 +**不受影响的:** 谁来回答。FlyBot 用自己的规则读「@名字」,被点名的 Bot 照样会被叫到。消息发出以后,会正常显示 Bot 的头像和名字。 **小技巧:** 名字后面加一个空格(`@Anvil 你看下`)。这样只要名字里没有空格,等待时也会显示成 Bot。 **原因:** 这些等待中的行由 DeepSeek Harness 自己画,它读「@」时一直读到下一个空格。插件目前改不了它们的显示。 -## DS Bot 没有加载:「disabling profile plugin row "bot"」 +## FlyBot 没有加载:「disabling profile plugin row "bot"」 -**在哪里:** DSH Web(或 Desktop)能启动,但没有 DS Bot。日志里有这一行: +**在哪里:** DSH Web(或 Desktop)能启动,但没有 FlyBot。日志里有这一行: ```text dsh: disabling profile plugin row "bot": its declared peer dependencies cannot be validated: Cannot read properties of undefined (reading 'startsWith') ``` -换装别的 DS Bot 版本也没用。 +换装别的 FlyBot 版本也没用。 -**原因:** DeepSeek Harness 加载插件前,会检查 profile 里的每一行插件配置。这条信息表示有一行的 id 是 `bot`,却没有包名。DS Bot 自己加的那一行带有包名 `ds-bot`,所以没有包名的那一行多半来自 profile 文件夹里的文件,比如 `cordis.patch.yml` 里一条已经对不上 DS Bot 那一行的 `- id: bot` 覆盖配置。我们没能复现这个问题,所以还不知道这样的一行是怎么留下来的。 +**原因:** DeepSeek Harness 加载插件前,会检查 profile 里的每一行插件配置。这条信息表示有一行的 id 是 `bot`,却没有包名。FlyBot 自己加的那一行带有包名 `ds-bot`,所以没有包名的那一行多半来自 profile 文件夹里的文件,比如 `cordis.patch.yml` 里一条已经对不上 FlyBot 那一行的 `- id: bot` 覆盖配置。我们没能复现这个问题,所以还不知道这样的一行是怎么留下来的。 **怎么处理:** profile 文件夹是 `~/.dsh/profiles/web/`(Windows:`C:\Users\<用户名>\.dsh\profiles\web\`);设置了 `DSH_HOME` 时,是其中的 `profiles/web/`。Desktop 用 `profiles/desktop/`。 1. 退出 DSH。 2. 打开 profile 文件夹里的 `cordis.patch.yml`,找 `id: bot` 但没有 `name: ds-bot` 的一行,删掉这一行。改之前先备份这个文件。 -3. 确认同一文件夹的 `package.json` 在 `dependencies` 里有 `ds-bot`。没有的话,重新添加 DS Bot:`npx @deepseek-ai/dsh@0.2.0-rc.2 plugin --profile web add ds-bot`。 +3. 确认同一文件夹的 `package.json` 在 `dependencies` 里有 `ds-bot`。没有的话,重新添加 FlyBot:`npx @deepseek-ai/dsh@0.2.0-rc.2 plugin --profile web add ds-bot`。 4. 重新启动 DSH。 想知道这一行来自哪里,可以运行 `npx @deepseek-ai/dsh@0.2.0-rc.2 --profile web --dump-config`。它会列出每一行插件配置,以及提供它的文件。如果要报告这个问题,只复制 `id: bot` 的那几行和文件名,不要带上 key 或你不想公开的路径。 diff --git a/scripts/logo.mjs b/scripts/logo.mjs new file mode 100644 index 000000000..5d6430fa1 --- /dev/null +++ b/scripts/logo.mjs @@ -0,0 +1,76 @@ +#!/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. +// +// node scripts/logo.mjs [out dir] default: docs/images +import { execFileSync } from 'node:child_process' +import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs' +import { tmpdir } from 'node:os' +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' }, +} +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 = `` + return [ + ``, + ``, + `${half}${half}`, + ``, + ``, + ``, + ``, + '\n', + ].join('') +} + +const findChrome = () => { + if (process.env.CHROME_BIN) return process.env.CHROME_BIN + for (const name of ['google-chrome', 'chromium', 'chromium-browser']) { + try { return execFileSync('sh', ['-c', `command -v ${name}`], { encoding: 'utf8' }).trim() } catch {} + } + return null +} + +function rasterize(chrome, svgFile, pngFile, size) { + const dir = mkdtempSync(join(tmpdir(), 'flybot-logo-')) + try { + const html = join(dir, 'logo.html') + writeFileSync(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' }) + } finally { rmSync(dir, { recursive: true, force: true }) } +} + +if (import.meta.url === `file://${process.argv[1]}`) { + const out = resolve(process.argv[2] ?? join(import.meta.dirname, '../docs/images')) + mkdirSync(out, { recursive: true }) + for (const mode of ['light', 'dark']) { + const file = join(out, mode === 'light' ? 'logo.svg' : 'logo-dark.svg') + writeFileSync(file, logo(mode)) + console.log(`wrote ${file}`) + } + const chrome = findChrome() + if (chrome === null) console.log('no Chrome found: skipped the PNG copies') + else { + for (const size of PNG_SIZES) { + const file = join(out, size === 512 ? 'logo.png' : `logo-${size}.png`) + rasterize(chrome, join(out, 'logo.svg'), file, size) + console.log(`wrote ${file}`) + } + rasterize(chrome, join(out, 'logo-dark.svg'), join(out, 'logo-dark.png'), 512) + console.log(`wrote ${join(out, 'logo-dark.png')}`) + } +}