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,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

View File

@ -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

View File

@ -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": {

View File

@ -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);

View File

@ -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

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):

View File

@ -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() -> 程序退出
```

View File

@ -48,7 +48,7 @@ Required:
--instance-id <id> Unique instance id.
--auth-username <name> Initial web login username.
--auth-password <password> Initial web login password.
--api-key <key> Provider API key for nanobot.
--api-key <key> Provider API key for Boardware Genius.
Optional:
--image <name> 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}

View File

@ -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"

View File

@ -60,10 +60,7 @@ export default function PluginsPage() {
</h1>
<p className="text-sm text-muted-foreground mt-1">
{' '}
<code className="text-xs bg-muted px-1 py-0.5 rounded">~/.nanobot/plugins/</code>
{' '}{' '}
<code className="text-xs bg-muted px-1 py-0.5 rounded">&lt;workspace&gt;/plugins/</code>
workspace <code className="text-xs bg-muted px-1 py-0.5 rounded">plugins/</code>
</p>
</div>
<Button onClick={load} variant="outline" size="sm">
@ -91,7 +88,7 @@ export default function PluginsPage() {
<Blocks className="w-12 h-12 mx-auto mb-4 opacity-30" />
<p className="font-medium"></p>
<p className="text-sm mt-2 max-w-sm mx-auto">
<code className="text-xs bg-muted px-1 py-0.5 rounded">~/.nanobot/plugins/</code>
workspace <code className="text-xs bg-muted px-1 py-0.5 rounded">plugins/</code>
Boardware Agent Sandbox
</p>
</CardContent>

View File

@ -60,7 +60,7 @@ export default function StatusPage() {
<p className="font-medium"> Boardware Agent Sandbox </p>
<p className="text-sm text-muted-foreground mt-1">{error}</p>
<p className="text-sm text-muted-foreground mt-1">
<code className="bg-muted px-1 rounded">nanobot web</code>
访
</p>
</div>
</div>

View File

@ -1,4 +1,4 @@
# 单用户后端地址(nanobot web
# 单用户后端地址(Boardware Genius Web 后端
NEXT_PUBLIC_API_URL=http://127.0.0.1:10000
NEXT_PUBLIC_WS_URL=wss://127.0.0.1:10000
NEXT_PUBLIC_AUTH_PORTAL_URL=http://127.0.0.1:3081

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 120 KiB