diff --git a/README.md b/README.md index b610e22..6a0ef5a 100644 --- a/README.md +++ b/README.md @@ -271,7 +271,7 @@ docker run -d \ - 注册接口超时 - `app-instance` 容器反复重启 -- 日志里出现 `Missing nanobot config: /root/.nanobot/config.json` +- 日志里出现 `Missing Boardware Genius config: /root/.nanobot/config.json` 当前版本里,新实例的默认大模型配置就是从这里分发的: diff --git a/app-instance/backend/README.md b/app-instance/backend/README.md index 13f7699..c161703 100644 --- a/app-instance/backend/README.md +++ b/app-instance/backend/README.md @@ -1,6 +1,6 @@ -# nanobot-backend +# Boardware Genius Backend -基于 `nanobot` 的后端服务仓库,当前重点不是上游通用介绍,而是这套实际可运行的后端能力: +这是 `Boardware Genius` 的后端服务仓库;当前技术命令和包名仍沿用 `nanobot`,但产品品牌按 `Boardware Genius` 表述: - `nanobot web`:单用户 FastAPI 后端,供独立前端或 `/docs` 调试使用 - `nanobot gateway`:常驻 worker,负责渠道接入、cron、heartbeat @@ -183,7 +183,7 @@ pip install -e . bw-outlook-mcp --help ``` -这样 nanobot 就会直接用 PATH 里的 `bw-outlook-mcp`,不依赖额外挂载路径。 +这样 Boardware Genius 就会直接用 PATH 里的 `bw-outlook-mcp`,不依赖额外挂载路径。 #### 方案 B:把 `BW_Outlook_Mcp` 作为外部目录挂进来 @@ -204,7 +204,7 @@ python3 -m venv .venv pip install -e . ``` -然后给 nanobot 设置: +然后给 Boardware Genius 设置: ```bash export NANOBOT_OUTLOOK_MCP_ROOT=/srv/BW_Outlook_Mcp diff --git a/app-instance/backend/SECURITY.md b/app-instance/backend/SECURITY.md index 405ce52..6e0831d 100644 --- a/app-instance/backend/SECURITY.md +++ b/app-instance/backend/SECURITY.md @@ -2,7 +2,7 @@ ## Reporting a Vulnerability -If you discover a security vulnerability in nanobot, please report it by: +If you discover a security vulnerability in Boardware Genius, please report it by: 1. **DO NOT** open a public GitHub issue 2. Create a private security advisory on GitHub or contact the repository maintainers (xubinrencs@gmail.com) @@ -67,7 +67,7 @@ The `exec` tool can execute shell commands. While dangerous command patterns are - ✅ Review all tool usage in agent logs - ✅ Understand what commands the agent is running - ✅ Use a dedicated user account with limited privileges -- ✅ Never run nanobot as root +- ✅ Never run Boardware Genius as root - ❌ Don't disable security checks - ❌ Don't run on systems with sensitive data without careful review @@ -82,7 +82,7 @@ The `exec` tool can execute shell commands. While dangerous command patterns are File operations have path traversal protection, but: -- ✅ Run nanobot with a dedicated user account +- ✅ Run Boardware Genius with a dedicated user account - ✅ Use filesystem permissions to protect sensitive directories - ✅ Regularly audit file operations in logs - ❌ Don't give unrestricted access to sensitive files @@ -123,7 +123,7 @@ npm audit fix - Keep `litellm` updated to the latest version for security fixes - We've updated `ws` to `>=8.17.1` to fix DoS vulnerability - Run `pip-audit` or `npm audit` regularly -- Subscribe to security advisories for nanobot and its dependencies +- Subscribe to security advisories for Boardware Genius and its dependencies ### 7. Production Deployment @@ -238,7 +238,7 @@ If you suspect a security breach: ## Security Checklist -Before deploying nanobot: +Before deploying Boardware Genius: - [ ] API keys stored securely (not in code) - [ ] Config file permissions set to 0600 diff --git a/app-instance/backend/bridge/package.json b/app-instance/backend/bridge/package.json index e91517c..8b7f53a 100644 --- a/app-instance/backend/bridge/package.json +++ b/app-instance/backend/bridge/package.json @@ -1,7 +1,7 @@ { "name": "nanobot-whatsapp-bridge", "version": "0.1.0", - "description": "WhatsApp bridge for nanobot using Baileys", + "description": "WhatsApp bridge for Boardware Genius using Baileys", "type": "module", "main": "dist/index.js", "scripts": { diff --git a/app-instance/backend/bridge/src/index.ts b/app-instance/backend/bridge/src/index.ts index e8f3db9..56eb24e 100644 --- a/app-instance/backend/bridge/src/index.ts +++ b/app-instance/backend/bridge/src/index.ts @@ -1,8 +1,8 @@ #!/usr/bin/env node /** - * nanobot WhatsApp Bridge + * Boardware Genius WhatsApp Bridge * - * This bridge connects WhatsApp Web to nanobot's Python backend + * This bridge connects WhatsApp Web to the Boardware Genius Python backend * via WebSocket. It handles authentication, message forwarding, * and reconnection logic. * @@ -27,7 +27,7 @@ const PORT = parseInt(process.env.BRIDGE_PORT || '3001', 10); const AUTH_DIR = process.env.AUTH_DIR || join(homedir(), '.nanobot', 'whatsapp-auth'); const TOKEN = process.env.BRIDGE_TOKEN || undefined; -console.log('🐈 nanobot WhatsApp Bridge'); +console.log('Boardware Genius WhatsApp Bridge'); console.log('========================\n'); const server = new BridgeServer(PORT, AUTH_DIR, TOKEN); diff --git a/app-instance/backend/guide.md b/app-instance/backend/guide.md index 79d21a0..ad5c79f 100644 --- a/app-instance/backend/guide.md +++ b/app-instance/backend/guide.md @@ -1,4 +1,4 @@ -# nanobot 前后端分离启动指南(单用户直连) +# Boardware Genius 前后端分离启动指南(单用户直连) 本指南对应当前仓库: `/home/ivan/xuan/steven_project/nanobot` @@ -16,7 +16,7 @@ cd /home/ivan/xuan/steven_project/nanobot uv sync ``` -如果你第一次使用 nanobot,需要先初始化: +如果你第一次使用 Boardware Genius,需要先初始化: ```bash ./.venv/bin/python -m nanobot onboard diff --git a/app-instance/backend/nanobot/__init__.py b/app-instance/backend/nanobot/__init__.py index a68777c..4663bf5 100644 --- a/app-instance/backend/nanobot/__init__.py +++ b/app-instance/backend/nanobot/__init__.py @@ -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__ = "" diff --git a/app-instance/backend/nanobot/agent/context.py b/app-instance/backend/nanobot/agent/context.py index e925b01..c260674 100644 --- a/app-instance/backend/nanobot/agent/context.py +++ b/app-instance/backend/nanobot/agent/context.py @@ -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}) diff --git a/app-instance/backend/nanobot/agent/delegation.py b/app-instance/backend/nanobot/agent/delegation.py index 3000b03..68c3965 100644 --- a/app-instance/backend/nanobot/agent/delegation.py +++ b/app-instance/backend/nanobot/agent/delegation.py @@ -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." ), }, diff --git a/app-instance/backend/nanobot/agent/loop.py b/app-instance/backend/nanobot/agent/loop.py index 0f30ad7..312f7d0 100644 --- a/app-instance/backend/nanobot/agent/loop.py +++ b/app-instance/backend/nanobot/agent/loop.py @@ -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 diff --git a/app-instance/backend/nanobot/agent/marketplace.py b/app-instance/backend/nanobot/agent/marketplace.py index b454041..254e2b8 100644 --- a/app-instance/backend/nanobot/agent/marketplace.py +++ b/app-instance/backend/nanobot/agent/marketplace.py @@ -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 diff --git a/app-instance/backend/nanobot/agent/plugins.py b/app-instance/backend/nanobot/agent/plugins.py index 8785684..d5c6e79 100644 --- a/app-instance/backend/nanobot/agent/plugins.py +++ b/app-instance/backend/nanobot/agent/plugins.py @@ -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 diff --git a/app-instance/backend/nanobot/channels/email.py b/app-instance/backend/nanobot/channels/email.py index 5dc05fb..556d835 100644 --- a/app-instance/backend/nanobot/channels/email.py +++ b/app-instance/backend/nanobot/channels/email.py @@ -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 diff --git a/app-instance/backend/nanobot/channels/telegram.py b/app-instance/backend/nanobot/channels/telegram.py index 6cd98e7..3a672e0 100644 --- a/app-instance/backend/nanobot/channels/telegram.py +++ b/app-instance/backend/nanobot/channels/telegram.py @@ -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" ) diff --git a/app-instance/backend/nanobot/cli/__init__.py b/app-instance/backend/nanobot/cli/__init__.py index b023cad..ed95a83 100644 --- a/app-instance/backend/nanobot/cli/__init__.py +++ b/app-instance/backend/nanobot/cli/__init__.py @@ -1 +1 @@ -"""CLI module for nanobot.""" +"""CLI module for Boardware Genius.""" diff --git a/app-instance/backend/nanobot/cli/commands.py b/app-instance/backend/nanobot/cli/commands.py index a8e9d71..ea36767 100644 --- a/app-instance/backend/nanobot/cli/commands.py +++ b/app-instance/backend/nanobot/cli/commands.py @@ -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() diff --git a/app-instance/backend/nanobot/config/__init__.py b/app-instance/backend/nanobot/config/__init__.py index 88e8e9b..5e53932 100644 --- a/app-instance/backend/nanobot/config/__init__.py +++ b/app-instance/backend/nanobot/config/__init__.py @@ -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 diff --git a/app-instance/backend/nanobot/templates/HEARTBEAT.md b/app-instance/backend/nanobot/templates/HEARTBEAT.md index 322dbeb..ee7d5c7 100644 --- a/app-instance/backend/nanobot/templates/HEARTBEAT.md +++ b/app-instance/backend/nanobot/templates/HEARTBEAT.md @@ -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 - diff --git a/app-instance/backend/nanobot/templates/SOUL.md b/app-instance/backend/nanobot/templates/SOUL.md index 59403e7..2f5d8b3 100644 --- a/app-instance/backend/nanobot/templates/SOUL.md +++ b/app-instance/backend/nanobot/templates/SOUL.md @@ -1,6 +1,6 @@ # Soul -I am nanobot 🐈, a personal AI assistant. +I am Boardware Genius, a personal AI assistant. ## Personality diff --git a/app-instance/backend/nanobot/templates/USER.md b/app-instance/backend/nanobot/templates/USER.md index 671ec49..ce82631 100644 --- a/app-instance/backend/nanobot/templates/USER.md +++ b/app-instance/backend/nanobot/templates/USER.md @@ -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.* diff --git a/app-instance/backend/nanobot/templates/memory/MEMORY.md b/app-instance/backend/nanobot/templates/memory/MEMORY.md index fd2ca96..2a7705f 100644 --- a/app-instance/backend/nanobot/templates/memory/MEMORY.md +++ b/app-instance/backend/nanobot/templates/memory/MEMORY.md @@ -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.* diff --git a/app-instance/backend/nanobot/utils/__init__.py b/app-instance/backend/nanobot/utils/__init__.py index a28f837..80ff7dd 100644 --- a/app-instance/backend/nanobot/utils/__init__.py +++ b/app-instance/backend/nanobot/utils/__init__.py @@ -1,4 +1,4 @@ -"""Utility functions for nanobot.""" +"""Utility functions for Boardware Genius.""" from nanobot.utils.helpers import ( ensure_dir, diff --git a/app-instance/backend/nanobot/web/__init__.py b/app-instance/backend/nanobot/web/__init__.py index 3b40cf9..8ade76d 100644 --- a/app-instance/backend/nanobot/web/__init__.py +++ b/app-instance/backend/nanobot/web/__init__.py @@ -1 +1 @@ -"""Web interface for nanobot.""" +"""Web interface for Boardware Genius.""" diff --git a/app-instance/backend/nanobot/web/server.py b/app-instance/backend/nanobot/web/server.py index 2b14faf..480919f 100644 --- a/app-instance/backend/nanobot/web/server.py +++ b/app-instance/backend/nanobot/web/server.py @@ -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): diff --git a/app-instance/backend/workflow.md b/app-instance/backend/workflow.md index 99f49b9..7495af0 100644 --- a/app-instance/backend/workflow.md +++ b/app-instance/backend/workflow.md @@ -1,6 +1,6 @@ -# nanobot Workflow +# Boardware Genius Workflow -本文按当前仓库代码,整理 nanobot 的主要运行链路,重点说明: +本文按当前仓库代码,整理 Boardware Genius 的主要运行链路。下文的技术命令名仍沿用 `nanobot`,重点说明: 1. 用户执行 `nanobot agent -m "你好"` 时,CLI 单轮模式到底走了什么路径。 2. `nanobot gateway` 常驻模式下,外部渠道、cron、heartbeat 如何进入同一套工作流。 @@ -106,7 +106,7 @@ │ │ └─ NO -> return OutboundMessage(final_content) │ ├─ process_direct() 拿到 OutboundMessage.content -├─ console.print("🐈 ...") +├─ console.print("Boardware Genius ...") └─ await agent_loop.close_mcp() -> 程序退出 ``` diff --git a/app-instance/create-instance.sh b/app-instance/create-instance.sh index 723d185..a7e2b6a 100755 --- a/app-instance/create-instance.sh +++ b/app-instance/create-instance.sh @@ -48,7 +48,7 @@ Required: --instance-id Unique instance id. --auth-username Initial web login username. --auth-password Initial web login password. - --api-key Provider API key for nanobot. + --api-key Provider API key for Boardware Genius. Optional: --image Docker image tag. Default: nano/app-instance:latest @@ -248,6 +248,57 @@ target.write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n", encodin PY } +render_runtime_env_file() { + local target_path="$1" + + TARGET_PATH="$target_path" \ + AUTHZ_BASE_URL="$AUTHZ_BASE_URL" \ + AUTHZ_OUTLOOK_MCP_URL="$AUTHZ_OUTLOOK_MCP_URL" \ + BACKEND_ID="$BACKEND_ID" \ + CLIENT_ID="$CLIENT_ID" \ + CLIENT_SECRET="$CLIENT_SECRET" \ + BACKEND_NAME="$BACKEND_NAME" \ + PUBLIC_URL="$PUBLIC_URL" \ + python3 - <<'PY' +import os +import shlex +from pathlib import Path + +target = Path(os.environ["TARGET_PATH"]) +values = { + "NANOBOT_AUTHZ__ENABLED": "1" if os.environ["AUTHZ_BASE_URL"].strip() else "0", + "NANOBOT_AUTHZ__BASE_URL": os.environ["AUTHZ_BASE_URL"].strip(), + "NANOBOT_AUTHZ__OUTLOOK_MCP_URL": os.environ["AUTHZ_OUTLOOK_MCP_URL"].strip(), + "NANOBOT_BACKEND_IDENTITY__BACKEND_ID": os.environ["BACKEND_ID"].strip(), + "NANOBOT_BACKEND_IDENTITY__CLIENT_ID": os.environ["CLIENT_ID"].strip(), + "NANOBOT_BACKEND_IDENTITY__CLIENT_SECRET": os.environ["CLIENT_SECRET"].strip(), + "NANOBOT_BACKEND_IDENTITY__NAME": os.environ["BACKEND_NAME"].strip(), + "NANOBOT_BACKEND_IDENTITY__PUBLIC_BASE_URL": os.environ["PUBLIC_URL"].strip(), +} +ordered_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", +] +lines: list[str] = [] +for key in ordered_keys: + value = values.get(key, "") + if value: + lines.append(f"export {key}={shlex.quote(value)}") + continue + if key == "NANOBOT_AUTHZ__ENABLED": + lines.append("export NANOBOT_AUTHZ__ENABLED=0") + else: + lines.append(f"unset {key}") +target.write_text("\n".join(lines) + "\n", encoding="utf-8") +PY +} + image_exists() { docker image inspect "$IMAGE_NAME" >/dev/null 2>&1 } @@ -457,12 +508,14 @@ INSTANCE_ROOT="${INSTANCES_ROOT}/${INSTANCE_SLUG}" NANOBOT_HOME="${INSTANCE_ROOT}/nanobot-home" CONFIG_PATH="${NANOBOT_HOME}/config.json" AUTH_USERS_PATH="${NANOBOT_HOME}/web_auth_users.json" +RUNTIME_ENV_PATH="${NANOBOT_HOME}/runtime.env" WORKSPACE_PATH="${NANOBOT_HOME}/workspace" mkdir -p "$NANOBOT_HOME" "$WORKSPACE_PATH" render_config_json "$CONFIG_PATH" render_auth_users_json "$AUTH_USERS_PATH" +render_runtime_env_file "$RUNTIME_ENV_PATH" if [[ "$FORCE_BUILD" -eq 1 ]] || ! image_exists; then log "building image ${IMAGE_NAME}" @@ -539,6 +592,7 @@ instance_root=${INSTANCE_ROOT} nanobot_home=${NANOBOT_HOME} config_path=${CONFIG_PATH} auth_users_path=${AUTH_USERS_PATH} +runtime_env_path=${RUNTIME_ENV_PATH} username=${USERNAME} email=${EMAIL} instance_host=${INSTANCE_HOST} diff --git a/app-instance/entrypoint.sh b/app-instance/entrypoint.sh index b5edddc..f17e105 100755 --- a/app-instance/entrypoint.sh +++ b/app-instance/entrypoint.sh @@ -6,6 +6,7 @@ APP_FRONTEND_PORT="${APP_FRONTEND_PORT:-3000}" APP_BACKEND_PORT="${APP_BACKEND_PORT:-18080}" NANOBOT_HOME="${NANOBOT_HOME:-/root/.nanobot}" NANOBOT_AUTH_FILE="${NANOBOT_AUTH_FILE:-$NANOBOT_HOME/web_auth_users.json}" +NANOBOT_RUNTIME_ENV_FILE="${NANOBOT_RUNTIME_ENV_FILE:-$NANOBOT_HOME/runtime.env}" log() { printf '[app-instance] %s\n' "$*" @@ -41,10 +42,17 @@ trap cleanup EXIT INT TERM mkdir -p "$NANOBOT_HOME" "$NANOBOT_HOME/workspace" -require_file "$NANOBOT_HOME/config.json" "Missing nanobot config" +if [[ -f "$NANOBOT_RUNTIME_ENV_FILE" ]]; then + set -a + . "$NANOBOT_RUNTIME_ENV_FILE" + set +a +fi + +require_file "$NANOBOT_HOME/config.json" "Missing Boardware Genius config" require_file "$NANOBOT_AUTH_FILE" "Missing web auth users file" export NANOBOT_AUTH_FILE +export NANOBOT_RUNTIME_ENV_FILE export PORT="$APP_FRONTEND_PORT" export HOSTNAME="127.0.0.1" @@ -64,4 +72,3 @@ nginx -c /opt/app/nginx.conf -g 'daemon off;' & NGINX_PID=$! wait -n "$BACKEND_PID" "$FRONTEND_PID" "$NGINX_PID" - diff --git a/app-instance/frontend/app/(app)/plugins/page.tsx b/app-instance/frontend/app/(app)/plugins/page.tsx index bc592b1..a984c3b 100644 --- a/app-instance/frontend/app/(app)/plugins/page.tsx +++ b/app-instance/frontend/app/(app)/plugins/page.tsx @@ -60,10 +60,7 @@ export default function PluginsPage() { 插件

- 已安装位置:{' '} - ~/.nanobot/plugins/ - {' '}或{' '} - <workspace>/plugins/ + 已安装位置:全局插件目录或当前 workspace 的 plugins/