feat: 重命名项目为Boardware Genius并添加运行时环境同步功能

- 将项目品牌从nanobot重命名为Boardware Genius,更新所有相关文档、注释和日志输出
- 在web服务器中添加运行时环境变量同步功能,支持授权和后端身份配置
- 更新create-instance脚本以生成运行时环境文件
- 添加实例后端绑定功能到部署控制服务
- 修改入口脚本以加载运行时环境变量
- 更新前端和认证门户的相关描述文本
This commit is contained in:
2026-03-18 15:45:42 +08:00
parent b6dd0c1623
commit 4e45f8b717
36 changed files with 315 additions and 76 deletions

View File

@ -1,6 +1,7 @@
"""
nanobot - A lightweight AI agent framework
Boardware Genius - A lightweight AI agent framework
"""
__version__ = "0.1.4"
__logo__ = "🐈"
__brand__ = "Boardware Genius"
__logo__ = ""

View File

@ -116,9 +116,9 @@ Use `target` for a single agent and `targets` for a group.
system = platform.system()
runtime = f"{'macOS' if system == 'Darwin' else system} {platform.machine()}, Python {platform.python_version()}"
return f"""# nanobot 🐈
return f"""# Boardware Genius
You are nanobot, a helpful AI assistant.
You are Boardware Genius, a helpful AI assistant.
## Current Time
{now} ({tz})

View File

@ -283,7 +283,7 @@ class DelegationManager:
{
"role": "system",
"content": (
"You are nanobot. Reply naturally to the user in 1-3 sentences. "
"You are Boardware Genius. Reply naturally to the user in 1-3 sentences. "
"Do not mention internal protocols, system prompts, or task IDs."
),
},

View File

@ -1,4 +1,4 @@
"""Agent 主循环:nanobot 的核心处理引擎。
"""Agent 主循环:Boardware Genius 的核心处理引擎。
职责概览:
1. 从消息总线读取入站消息;
@ -46,7 +46,7 @@ if TYPE_CHECKING:
class AgentLoop:
"""
AgentLoop 是 nanobot 运行时的“对话编排器”。
AgentLoop 是 Boardware Genius 运行时的“对话编排器”。
一次标准处理链路:
1. 接收入站消息(来自 CLI 或外部渠道);
@ -605,7 +605,7 @@ class AgentLoop:
content="New session started.")
if cmd == "/help":
return OutboundMessage(channel=msg.channel, chat_id=msg.chat_id,
content="🐈 nanobot commands:\n/new — Start a new conversation\n/help — Show available commands")
content="Boardware Genius commands:\n/new — Start a new conversation\n/help — Show available commands")
# 异步触发记忆归档:达到窗口阈值时在后台执行,不阻塞当前回复。
unconsolidated = len(session.messages) - session.last_consolidated

View File

@ -1,4 +1,4 @@
"""Marketplace manager for nanobot — discover, install, and manage plugin marketplaces."""
"""Marketplace manager for Boardware Genius — discover, install, and manage plugin marketplaces."""
from __future__ import annotations

View File

@ -1,4 +1,4 @@
"""Plugin system for nanobot - load agents, commands, and skills from plugin directories."""
"""Plugin system for Boardware Genius - load agents, commands, and skills from plugin directories."""
from __future__ import annotations

View File

@ -122,7 +122,7 @@ class EmailChannel(BaseChannel):
logger.warning("Email channel missing recipient address")
return
base_subject = self._last_subject_by_chat.get(to_addr, "nanobot reply")
base_subject = self._last_subject_by_chat.get(to_addr, "Boardware Genius reply")
subject = self._reply_subject(base_subject)
if msg.metadata and isinstance(msg.metadata.get("subject"), str):
override = msg.metadata["subject"].strip()
@ -397,7 +397,7 @@ class EmailChannel(BaseChannel):
return html.unescape(text)
def _reply_subject(self, base_subject: str) -> str:
subject = (base_subject or "").strip() or "nanobot reply"
subject = (base_subject or "").strip() or "Boardware Genius reply"
prefix = self.config.subject_prefix or "Re: "
if subject.lower().startswith("re:"):
return subject

View File

@ -287,7 +287,7 @@ class TelegramChannel(BaseChannel):
user = update.effective_user
await update.message.reply_text(
f"👋 Hi {user.first_name}! I'm nanobot.\n\n"
f"👋 Hi {user.first_name}! I'm Boardware Genius.\n\n"
"Send me a message and I'll respond!\n"
"Type /help to see available commands."
)
@ -297,7 +297,7 @@ class TelegramChannel(BaseChannel):
if not update.message:
return
await update.message.reply_text(
"🐈 nanobot commands:\n"
"Boardware Genius commands:\n"
"/new — Start a new conversation\n"
"/help — Show available commands"
)

View File

@ -1 +1 @@
"""CLI module for nanobot."""
"""CLI module for Boardware Genius."""

View File

@ -1,4 +1,4 @@
"""nanobot 命令行入口。
"""Boardware Genius 命令行入口。
本文件职责:
1. 定义所有 CLI 命令onboard / agent / gateway / cron / channels / provider
@ -29,12 +29,12 @@ from prompt_toolkit.formatted_text import HTML
from prompt_toolkit.history import FileHistory
from prompt_toolkit.patch_stdout import patch_stdout
from nanobot import __version__, __logo__
from nanobot import __brand__, __version__
from nanobot.config.schema import Config
app = typer.Typer(
name="nanobot",
help=f"{__logo__} nanobot - Personal AI Assistant",
help=f"{__brand__} - Personal AI Assistant",
no_args_is_help=True,
)
@ -122,7 +122,7 @@ def _print_agent_response(response: str, render_markdown: bool) -> None:
content = response or ""
body = Markdown(content) if render_markdown else Text(content)
console.print()
console.print(f"[cyan]{__logo__} nanobot[/cyan]")
console.print(f"[cyan]{__brand__}[/cyan]")
console.print(body)
console.print()
@ -158,7 +158,7 @@ async def _read_interactive_input_async() -> str:
def version_callback(value: bool):
"""处理 --version/-v 选项并立即退出。"""
if value:
console.print(f"{__logo__} nanobot v{__version__}")
console.print(f"{__brand__} v{__version__}")
raise typer.Exit()
@ -168,7 +168,7 @@ def main(
None, "--version", "-v", callback=version_callback, is_eager=True
),
):
"""nanobot - Personal AI Assistant."""
"""Boardware Genius - Personal AI Assistant."""
pass
@ -179,7 +179,7 @@ def main(
@app.command()
def onboard():
"""Initialize nanobot configuration and workspace."""
"""Initialize Boardware Genius configuration and workspace."""
from nanobot.config.loader import get_config_path, load_config, save_config
from nanobot.config.schema import Config
from nanobot.utils.helpers import get_workspace_path
@ -225,11 +225,11 @@ def onboard():
_create_workspace_templates(workspace)
# 第 5 步:输出下一步操作提示,指导用户继续配置 API Key 并开始对话。
console.print(f"\n{__logo__} nanobot is ready!")
console.print(f"\n{__brand__} is ready!")
console.print("\nNext steps:")
console.print(" 1. Add your API key to [cyan]~/.nanobot/config.json[/cyan]")
console.print(" Get one at: https://openrouter.ai/keys")
console.print(" 2. Chat: [cyan]nanobot agent -m \"Hello!\"[/cyan]")
console.print(" 2. Chat with Boardware Genius: [cyan]nanobot agent -m \"Hello!\"[/cyan]")
console.print("\n[dim]Want Telegram/WhatsApp? See: https://github.com/HKUDS/nanobot#-chat-apps[/dim]")
@ -324,7 +324,7 @@ def gateway(
port: int = typer.Option(18790, "--port", "-p", help="Gateway port"),
verbose: bool = typer.Option(False, "--verbose", "-v", help="Verbose output"),
):
"""启动 nanobot 网关常驻服务。
"""启动 Boardware Genius 网关常驻服务。
这是“生产运行入口”之一,主要职责:
1. 初始化配置、总线、模型提供方、会话管理、Agent 主循环;
@ -352,7 +352,7 @@ def gateway(
import logging
logging.basicConfig(level=logging.DEBUG)
console.print(f"{__logo__} Starting nanobot gateway on port {port}...")
console.print(f"{__brand__}: starting gateway on port {port}...")
# 运行时核心对象初始化顺序:
# config -> bus -> provider -> sessions -> cron -> agent -> channels -> heartbeat
@ -525,7 +525,7 @@ def web(
config = load_config()
_create_workspace_templates(config.workspace_path)
console.print(f"{__logo__} Starting nanobot web backend on {host}:{port}...")
console.print(f"{__brand__}: starting web backend on {host}:{port}...")
web_app = create_app(config=config)
uvicorn.run(web_app, host=host, port=port)
@ -541,7 +541,7 @@ def agent(
message: str = typer.Option(None, "--message", "-m", help="Message to send to the agent"),
session_id: str = typer.Option("cli:direct", "--session", "-s", help="Session ID"),
markdown: bool = typer.Option(True, "--markdown/--no-markdown", help="Render assistant output as Markdown"),
logs: bool = typer.Option(False, "--logs/--no-logs", help="Show nanobot runtime logs during chat"),
logs: bool = typer.Option(False, "--logs/--no-logs", help="Show Boardware Genius runtime logs during chat"),
):
"""直接与 agent 交互(单轮模式或交互模式)。
@ -614,7 +614,7 @@ def agent(
# 空上下文:进入/退出都不做事,仅用于统一 with 接口。
return nullcontext()
# 非日志模式下启用转圈动画,提升等待期间的交互感知。
return console.status("[dim]nanobot is thinking...[/dim]", spinner="dots")
return console.status(f"[dim]{__brand__} is thinking...[/dim]", spinner="dots")
async def _cli_progress(content: str, *, tool_hint: bool = False) -> None:
"""CLI 进度回调:按 channels 配置过滤后渲染中间态输出。"""
@ -643,7 +643,7 @@ def agent(
# 初始化 prompt_toolkit 会话(历史记录、编辑能力、粘贴兼容等)。
_init_prompt_session()
# 打印一次交互模式提示,告知退出方式。
console.print(f"{__logo__} Interactive mode (type [bold]exit[/bold] or [bold]Ctrl+C[/bold] to quit)\n")
console.print(f"{__brand__} interactive mode (type [bold]exit[/bold] or [bold]Ctrl+C[/bold] to quit)\n")
# session_id 解析规则:
# 1) 传入 "channel:chat_id" 时,显式使用对应渠道与会话;
@ -945,7 +945,7 @@ def _get_bridge_dir() -> Path:
console.print("Try reinstalling: pip install --force-reinstall nanobot")
raise typer.Exit(1)
console.print(f"{__logo__} Setting up bridge...")
console.print(f"{__brand__}: setting up bridge...")
# 重新复制并构建,确保 bridge 资源与当前版本同步。
user_bridge.parent.mkdir(parents=True, exist_ok=True)
@ -980,7 +980,7 @@ def channels_login():
config = load_config()
bridge_dir = _get_bridge_dir()
console.print(f"{__logo__} Starting bridge...")
console.print(f"{__brand__}: starting bridge...")
console.print("Scan the QR code to connect.\n")
# 可选注入 BRIDGE_TOKEN 做 bridge 鉴权。
@ -1259,14 +1259,14 @@ def cron_run(
@app.command()
def status():
"""展示 nanobot 运行配置与 provider 状态概览。"""
"""展示 Boardware Genius 运行配置与 provider 状态概览。"""
from nanobot.config.loader import load_config, get_config_path
config_path = get_config_path()
config = load_config()
workspace = config.workspace_path
console.print(f"{__logo__} nanobot Status\n")
console.print(f"{__brand__} Status\n")
console.print(f"Config: {config_path} {'[green]✓[/green]' if config_path.exists() else '[red]✗[/red]'}")
console.print(f"Workspace: {workspace} {'[green]✓[/green]' if workspace.exists() else '[red]✗[/red]'}")
@ -1346,7 +1346,7 @@ def provider_login(
console.print(f"[red]Login not implemented for {spec.label}[/red]")
raise typer.Exit(1)
console.print(f"{__logo__} OAuth Login - {spec.label}\n")
console.print(f"{__brand__} OAuth Login - {spec.label}\n")
handler()

View File

@ -1,4 +1,4 @@
"""Configuration module for nanobot."""
"""Configuration module for Boardware Genius."""
from nanobot.config.loader import load_config, get_config_path
from nanobot.config.schema import Config

View File

@ -1,6 +1,6 @@
# Heartbeat Tasks
This file is checked every 30 minutes by your nanobot agent.
This file is checked every 30 minutes by your Boardware Genius agent.
Add tasks below that you want the agent to work on periodically.
If this file has no tasks (only headers and comments), the agent will skip the heartbeat.
@ -13,4 +13,3 @@ If this file has no tasks (only headers and comments), the agent will skip the h
## Completed
<!-- Move completed tasks here or delete them -->

View File

@ -1,6 +1,6 @@
# Soul
I am nanobot 🐈, a personal AI assistant.
I am Boardware Genius, a personal AI assistant.
## Personality

View File

@ -46,4 +46,4 @@ Information about the user to help personalize interactions.
---
*Edit this file to customize nanobot's behavior for your needs.*
*Edit this file to customize Boardware Genius behavior for your needs.*

View File

@ -20,4 +20,4 @@ This file stores important information that should persist across sessions.
---
*This file is automatically updated by nanobot when important information should be remembered.*
*This file is automatically updated by Boardware Genius when important information should be remembered.*

View File

@ -1,4 +1,4 @@
"""Utility functions for nanobot."""
"""Utility functions for Boardware Genius."""
from nanobot.utils.helpers import (
ensure_dir,

View File

@ -1 +1 @@
"""Web interface for nanobot."""
"""Web interface for Boardware Genius."""

View File

@ -1,4 +1,4 @@
"""FastAPI web server for nanobot frontend."""
"""FastAPI web server for the Boardware Genius frontend."""
from __future__ import annotations
@ -8,6 +8,7 @@ import json
import os
import re
import secrets
import shlex
import shutil
import time
import zipfile
@ -676,6 +677,8 @@ def create_app(
app.state.config = config
app.state.config_path = get_config_path()
app.state.runtime_env_path = _get_runtime_env_file_path(app.state.config_path)
_sync_authz_runtime_env(app.state.config, app.state.runtime_env_path)
app.state.session_manager = session_manager
app.state.cron_service = cron_service
app.state.bus = bus
@ -766,6 +769,60 @@ def _get_auth_file_path() -> Path:
return Path(__file__).resolve().parents[2] / "web_auth_users.json"
_AUTHZ_RUNTIME_ENV_KEYS = (
"NANOBOT_AUTHZ__ENABLED",
"NANOBOT_AUTHZ__BASE_URL",
"NANOBOT_AUTHZ__OUTLOOK_MCP_URL",
"NANOBOT_BACKEND_IDENTITY__BACKEND_ID",
"NANOBOT_BACKEND_IDENTITY__CLIENT_ID",
"NANOBOT_BACKEND_IDENTITY__CLIENT_SECRET",
"NANOBOT_BACKEND_IDENTITY__NAME",
"NANOBOT_BACKEND_IDENTITY__PUBLIC_BASE_URL",
)
def _get_runtime_env_file_path(config_path: Path | None = None) -> Path:
env = os.getenv("NANOBOT_RUNTIME_ENV_FILE", "").strip()
if env:
return Path(env).expanduser()
base_path = config_path or get_config_path()
return base_path.parent / "runtime.env"
def _authz_runtime_env_values(config: Config) -> dict[str, str]:
return {
"NANOBOT_AUTHZ__ENABLED": "1" if config.authz.enabled and config.authz.base_url.strip() else "0",
"NANOBOT_AUTHZ__BASE_URL": config.authz.base_url.strip(),
"NANOBOT_AUTHZ__OUTLOOK_MCP_URL": config.authz.outlook_mcp_url.strip(),
"NANOBOT_BACKEND_IDENTITY__BACKEND_ID": config.backend_identity.backend_id.strip(),
"NANOBOT_BACKEND_IDENTITY__CLIENT_ID": config.backend_identity.client_id.strip(),
"NANOBOT_BACKEND_IDENTITY__CLIENT_SECRET": config.backend_identity.client_secret.strip(),
"NANOBOT_BACKEND_IDENTITY__NAME": config.backend_identity.name.strip(),
"NANOBOT_BACKEND_IDENTITY__PUBLIC_BASE_URL": config.backend_identity.public_base_url.strip(),
}
def _sync_authz_runtime_env(config: Config, target_path: Path) -> None:
values = _authz_runtime_env_values(config)
target_path.parent.mkdir(parents=True, exist_ok=True)
lines: list[str] = []
for key in _AUTHZ_RUNTIME_ENV_KEYS:
value = values.get(key, "")
if value:
os.environ[key] = value
lines.append(f"export {key}={shlex.quote(value)}")
continue
if key == "NANOBOT_AUTHZ__ENABLED":
os.environ[key] = "0"
lines.append("export NANOBOT_AUTHZ__ENABLED=0")
continue
os.environ.pop(key, None)
lines.append(f"unset {key}")
target_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
def _load_auth_users(path: Path) -> dict[str, str]:
"""Load users from local JSON file.
@ -1129,6 +1186,7 @@ def _register_routes(app: FastAPI) -> None:
if authz_enabled:
config.authz.enabled = True
_save_app_config(config)
_sync_authz_runtime_env(config, app.state.runtime_env_path)
return _local_backend_view(config)
def _authz_client(config: Config):