Dev qing #15

Merged
qing merged 57 commits from dev_qing into main 2026-08-01 06:12:14 +08:00
57 Commits
Author SHA1 Message Date
qing 46b66f8bd8 fix(gateway,static): 配置 /ws/aibox WS 反代路由;aibox 桌面端对话左对齐 2026-08-01 06:08:20 +08:00
qing e25c132722 解耦libhttp/HttpClient 2026-08-01 06:04:36 +08:00
qing 373f6af352 ^-^ 2026-08-01 06:04:27 +08:00
qing 65580e7e50 refactor(static): aibox.html 拆分内联 CSS/JS 为独立文件 2026-08-01 05:27:02 +08:00
qing 227fbb17f4 build(libhttp): lower optimization to -O2 to avoid cc1plus OOM on low-memory machines 2026-08-01 05:26:52 +08:00
qing cebef9fe70 docs: 各模块安全审计报告 + 补充 .gitignore 忽略规则 2026-08-01 05:21:23 +08:00
qing 12ece9020c feat(libhttp,gateway): add admin-only WebSocket reverse proxy
libhttp v3.1:
- add ws_proxy(path, handshake) API with WsProxyHandshakeHandler callback
- implement full-duplex async bidirectional bridging (text+binary frames)
- support ws:// and wss:// backends with SNI, write queues for ordering
- proxy routes take priority over regular ws() routes
- 403 response on handshake rejection, graceful close propagation

gateway v0.6.0:
- add [WsProxy] config section (routes = /path=ws://backend;...)
- handshake callback verifies Token (Authorization/Cookie) is_admin=true
- non-admin / expired / missing token → 403, admin → transparent bridge
- fix LocalTime.hpp C++20 tzdb incompatibility on Apple clang (use strftime/get_time/mktime)
- update example.ini and README docs for both modules
2026-08-01 05:20:14 +08:00
qing 6ed88feaa4 feat(static): 新增 aibox 智能体 Web 客户端(移动端适配)
- aibox.html:自包含移动端聊天页,经 gateway [WsProxy] 反向代理与 aibox 通信
- 协议兼容 BotOps client.py:chunk 流式渲染 / reasoning 思考区 / task 任务面板 / 指数退避重连
- 移动端适配:100dvh 动态视口 + safe-area 刘海屏 + 44px 触控目标 + 任务面板抽屉式
- 安全:握手由 gateway 强制 is_admin 验签,Token 全程 HttpOnly Cookie,CSP 合规(本地 markdown-it)
- README 同步页面清单与功能说明
2026-08-01 05:18:30 +08:00
qing bf2b5d31f7 fix(botops): v1.2 - 彻底修复思考区域与正式回答顺序问题
问题根因:之前预创建空 AI widget,思考 widget 挂载在其后,即使动态重排也可能因 Textual 组件树更新时序导致顺序不稳定。

修复方案:
- 移除 prefab 空 AI widget 预创建逻辑
- start_ai() 仅标记状态,不创建任何 widget
- _ensure_ai_widget() 延迟到第一次 chunk 到来时才创建 AI widget
- 按消息到达顺序自然挂载:reasoning_start → reasoning → chunk → end_reasoning/finish_ai
- 思考 widget 必然先于 AI widget 挂载,顺序天然正确

同时:
- 简化 app.py 发送失败处理,不再需要移除空 widget
- 更新 README v1.2 说明
2026-08-01 05:00:56 +08:00
qing 9815ff44af feat(libhttp): integrate WebSocket client into HttpClient
Merge WebSocket client functionality into HttpClient using Boost.Beast,
enabling a single client instance to handle both HTTP (libcurl) and
WebSocket (ws:// and wss://) protocols. Backward-compatible type aliases
(WebSocketClient, WebSocketClientBase) preserve existing code.

Changes:
- Add synchronous WebSocket client: ws_connect/ws_send/ws_recv/ws_close
- Support ws:// (plain TCP) and wss:// (TLS with SNI) connections
- Layered stream architecture with beast::tcp_stream for timeout support
- Binary message support via ws_send_binary()
- Update Makefile to link libcurl (-lcurl)
- Add README.md documenting unified HttpServer + HttpClient API
2026-08-01 04:59:47 +08:00
qing 5edb67b46d docs(botops): update README with reasoning mode ordering fix and v1.1 changelog
- Update reasoning display limit from 2000 to 3000 chars in protocol docs
- Add widget ordering guarantee note (reasoning always before answer)
- Document info message filtering for conversation start notifications
- Add v1.1 changelog section listing reasoning mode features and fixes
2026-08-01 04:56:25 +08:00
qing 039d6acf37 fix(botops): fix reasoning panel appearing after AI response and filter conversation start notification
- Add _ai_prefab flag to track pre-created empty AI widget
- start_reasoning() now reorders widgets: remove empty AI widget, mount reasoning first, then remount AI widget
- This ensures reasoning always appears BEFORE the actual answer
- Filter out 'Conversation started'/'对话开始' info notifications (implicit event, no need to show)
- Increase reasoning display limit from 2000 to 3000 chars
2026-08-01 04:55:12 +08:00
qing ba9a77fe66 feat(libhttp): integrate WebSocket support into HttpServer
Merge WebSocketServer functionality into HttpServer so a single port can
serve both HTTP(S) and WebSocket(S) endpoints while preserving full backward
compatibility for existing HTTP interfaces.

Key changes:
- Add WebSocket route registration: ws(), onWsConnect(), onWsDisconnect()
- Add client management: sendToWsClient(), broadcastWs(), disconnectWsClient()
- Rewrite Session class using beast::tcp_stream for unified HTTP/WS handling
- Support automatic ws:// and wss:// upgrades on HTTP/HTTPS ports
- Thread-safe message queue with asio::post for cross-thread sends
- Update Makefile to link OpenSSL and pthread for TLS/WebSocket support
- Add test_server.cpp demonstrating HTTP + WebSocket mixed usage
2026-08-01 04:43:33 +08:00
qing e007990594 feat(dnfs,static): 新增管理员特殊节点(链接/网页代码/图片/文件)
- 信封格式:content 为 {"_sp":...} JSON,普通 Markdown 节点不受影响
- dnfs:add_note/update_note 按 is_admin 强制特殊节点发布权(非管理员 403),
  字段与大小校验(url≤2048、code≤512KB、base64≤3.5M 字符),AI 审核剔除 base64 负载
- dnfs:修复存量 bug —— 模块级 _sys.path.insert 未定义导致 import 即 NameError
- static:新增 GET /api/favicon 服务端代理(SSRF 防护:内网地址拦截 + 重定向校验 + 512KB 上限)
- 前端:管理员可见节点类型选择器;链接 favicon 卡片 / 网页可审计可运行 / 图片直显 / 文件下载
- theme.css 补充 select 与特殊节点样式;两个 README 同步更新
2026-08-01 04:34:23 +08:00
qing b79044d631 feat(aibox): add DeepSeek reasoning mode support with tool calling on deepseek-v4-flash
- Add api.reasoning_enabled config (default false) to enable thinking mode
- Uses deepseek-v4-flash native thinking capability (thinking param), supports tool calls while reasoning
- Real-time reasoning content streaming via reasoning_start/reasoning/reasoning_end WS messages
- Frontend shows reasoning in gray italic area, auto-folds long reasoning (>2000 chars)
- Dynamic reasoning detection: shows reasoning even if model returns it without explicit config
- Increase default API timeout to 120s for longer reasoning responses
- Remove duplicate _add_assistant_message_with_reasoning method
- Update docs: config example, message types table, v2.5 changelog
2026-08-01 04:33:55 +08:00
qing 81ae84851d feat(waf): add WebSocket proxy support and fix HTTP/2 proto version
- Implement http.Hijacker on responseRecorder to enable WebSocket upgrade
  (delegates Hijack() to underlying ResponseWriter, marks hijacked state)
- After WebSocket upgrade (101 Switching Protocols), skip HTTP response-phase
  WAF checks since connection becomes a binary frame stream
- Log WebSocket upgrades at Info level for observability
- Fix P2-3: use r.Proto instead of hardcoded "HTTP/1.1" in ProcessResponseHeaders
  for correct HTTP/2 protocol version reporting
- Add Write() method to responseRecorder to ensure WriteHeader is called
  before first Write (complies with http.ResponseWriter contract)
- Add bufio/net imports required by Hijacker interface
- Update README with WebSocket support section and v1.2 changelog
- Update SECURITY_AUDIT to mark P2-3 and P2-4 as fixed
2026-08-01 04:18:30 +08:00
qing 4e190953c0 feat(confs): integrate dnfs service with centralized config management
- Add dnfs.ini to confs/configs/ with server, database, ai_review, confs sections
- Refactor dnfs.py to use three-layer fail-safe config loading (defaults → confs → env vars)
- Unify timeouts for DB RPC and AI review calls via configuration
- Add _merge_ini() whitelist merging to prevent unexpected config injection
- Environment variables have highest priority for Docker networking (db/ai_review hostnames)
- Document config loading mechanism in both confs/README.md and dnfs/README.md

Follows the same pattern as static service integration; confs failure falls back
gracefully to built-in defaults + environment variables.
2026-08-01 04:13:39 +08:00
qing b4436b192a fix(waf): harden against OOM and Slowloris DoS attacks
- P0: add 10MB hard request body size limit with io.LimitReader,
  return 413 Request Entity Too Large when exceeded, prevents OOM
- P0: configure http.Server timeouts: ReadHeaderTimeout=10s (Slowloris),
  ReadTimeout=15s, WriteTimeout=30s, IdleTimeout=120s, MaxHeaderBytes=1MB
- P1: properly handle body read errors, log and return on client disconnect
- P2: drain and close r.Body in defer to ensure TCP connection reuse
- Log errors from ReadRequestBodyFrom and ProcessRequestBody
- Inject security headers on 413 responses
- Add security audit report (docs/SECURITY_AUDIT_20260801.md)
- Update README with timeout/body-limit docs and changelog
2026-08-01 04:13:39 +08:00
qing bd544a4181 docs(aibox): update file line count to ~8450 2026-08-01 03:51:17 +08:00
qing 164bc65b95 feat(aibox): add dedicated workspace directory to prevent file pollution
- Add WORKSPACE_DIR constant pointing to ~/.aibox/workspace by default
- Update file operations prompt to require writing temp files to workspace
- Update command execution prompt to use workspace as default work_dir
- Add command output size limit note in command execution prompt
- Update README with workspace directory structure and specification
- Add system.workspace_dir config item documentation
- Add v2.4 changelog entry
2026-08-01 03:49:25 +08:00
qing 53ce69c8dd docs(confs): update README for P1 fixes and clarify RpcClient usage
- Update title/description: pymsg RpcServer/TCP instead of plain pymsg/TCP
- Add new feature entries: cache copy return, list symlink guard, strict
  param validation, ConfigParser interpolation=None, Slowloris protection
- Fix Python call examples: RpcClient.call() returns data directly, not
  wrapped in {'success':..., 'data':...}; add RpcError try/except example
- Clarify reload semantics: empty string/non-string name returns error;
  only omitted name or explicit null triggers full cache clear
- Add parameter validation rules for get (name/key type checks) and reload
- Document INI % literal behavior (interpolation disabled)
- Add '返回值隔离' to cache mechanism section
- Expand security mechanism table: cache anti-tamper, list symlink guard,
  strict param validation, INI safe parsing
- Add docs/ directory and audit report reference to project structure
- Add notes about correct reload usage, INI % handling, and return copy
- Add audit report cross-reference in 注意事项
2026-08-01 03:46:39 +08:00
qing 2f3e63e499 fix(aibox): prevent OOM from large tool outputs with multi-layer safeguards
- ReadFileTool: replace readlines() with on-demand range reading; add reverse block reading for large file tail; limit single line to 10KB; hard-cap max_lines to 500
- SubprocessCommandExecutor: replace capture_output with line-by-line Popen reading; enforce 1MB output limit with immediate process termination; merge stdout/stderr counting for both sync and streaming modes
- Raise TOOL_MAX_RESULT_LENGTH default from 10KB to 1MB as final safety net
- Add SECURITY_AUDIT.md documenting vulnerabilities and remediation roadmap
- Update README with memory safety features and v2.3 changelog
2026-08-01 03:46:02 +08:00
qing 90761a723d docs(confs): fill in commit hashes for fixed issues in audit report 2026-08-01 03:43:01 +08:00
qing b53d4999ce fix(confs): harden parameter validation and prevent cache/info leaks
P1 fixes:
- P1-1 _handle_reload: empty string/0/false/falsy name no longer clears all
  caches; only explicit null/omitted name triggers full cache clear; non-string
  name raises ConfigError instead of being silently str()-converted
- P1-2 ConfigStore.get(): return dict(entry) shallow copy instead of direct
  cache reference to prevent callers from mutating the internal cache
- P1-3 list(): resolve symlinks via os.path.realpath() and skip entries whose
  real path escapes configs/ root; prevents leaking external file metadata
  (size, mtime) through directory-traversing symlinks
- P1-4 _handle_get key: validate key is a string (isinstance check); reject
  non-string/empty key with clear ConfigError instead of misleading str()
  conversion leading to "key not found" errors

P2 fixes:
- P2-1 ConfigParser: use interpolation=None to disable %(key)s interpolation,
  preventing InterpolationError on config values containing bare % characters
- P2-2 argparse: update description from "pymsg/TCP" to "pymsg RpcServer/TCP"
  to match module docstring

Also update SECURITY_AUDIT_20260801.md to mark issues 9-14 as fixed and
refresh the security mechanisms summary table; all P0/P1 issues are now resolved.
2026-08-01 03:42:46 +08:00
qing e4e9968d14 docs(confs): add security audit report 20260801
- Audit covers confs.py, log_client.py, startup scripts, and pymsg/rpc.py dependency
- Documents 8 issues fixed during this audit cycle (P0-P2) including Slowloris DoS,
  OOM, thread death, TOCTOU race, and log_client hardening
- Lists 4 remaining P1 issues (reload empty string clears all cache, mutable cache
  reference leak, list() symlink metadata leak, key type validation), 4 P2 issues,
  and 6 P3 issues with root cause, risk analysis, and fix suggestions
- Security mechanisms summary table for quick reference
- Follows project convention: gateway/docs/SECURITY_AUDIT_YYYYMMDD.md
2026-08-01 03:37:22 +08:00
qing 9efb96ea66 fix(confs): eliminate TOCTOU race in ConfigStore path resolution
- Split _resolve() into _validate_name() (pure string checks, lock-free
  fast-fail) and _resolve_locked() (filesystem checks, must be called
  under lock)
- Perform all filesystem operations (realpath → isfile → stat → open)
  inside self._lock in get(), eliminating the window between path
  validation and file open where a symlink swap could escape the
  configs directory
- Apply same fix to reload(): validate name outside lock, resolve path
  inside lock to prevent TOCTOU during targeted reload
- Hold lock during list() directory traversal so the `cached` flag
  (which reads self._cache) is consistent with directory state; wrap
  os.stat() in try/except to tolerate files disappearing mid-traversal
- Update docstring and README:
  - Protocol description corrected to pymsg RpcServer/TCP
  - Python client examples updated from legacy MsgClient to RpcClient
  - Security section now documents TOCTOU mitigation and Slowloris
    protection (added by earlier pymsg fix)
2026-08-01 03:32:19 +08:00
qing 6c587a22d8 docs(db): update README for log_client SDK template and hardening features
- Document that example_log_client.py now uses RpcClient (high-level RPC)
  instead of legacy MsgClient (low-level TCP frames)
- Add comprehensive client feature list reflecting all hardening fixes:
  - Thread exception protection (outer try/except prevents thread death)
  - Accurate enqueue-time timestamps
  - Queue maxsize with drop-oldest policy
  - Safe JSON serialization fallback
  - Event-based flush synchronization
  - Non-blocking push() guarantee
- Clarify length truncation: server-side _log_internal() truncates
  internal logs to 8KB/4KB, external clients should control length
- Add db_tools.py to file table (was previously undocumented)
- Update service description to mention RpcServer
2026-08-01 03:28:40 +08:00
qing bdbea6ac40 fix(log_client): update example_log_client template to use RpcClient
- Replace legacy MsgClient (low-level TCP frames) with RpcClient (high-level
  RPC) to match dbv2's RpcServer interface; the old MsgClient-based version
  was incompatible with the current RPC routing protocol
- Apply all hardening fixes from the service copies:
  - Outer try/except in _flush_loop to prevent any exception from killing
    the background thread
  - _safe_json() helper for unserializable extra fields (fallback to str/repr)
  - Timestamp generated at push() (enqueue time) instead of _send() time
  - Event-based flush() synchronization instead of unreliable sleep(0.6)
  - Queue maxsize=10000 with drop-oldest policy to prevent unbounded memory
  - put_nowait with Full handling ensures push() never blocks business threads
  - RpcClient construction wrapped in try/except
- Add explicit service_name parameter example for all 4 services in comments
- This is the template file referenced by db/README.md and copied by new
  services; keeping it in sync prevents propagating old bugs
2026-08-01 03:26:19 +08:00
qing 3be9e78335 docs(aibox): update README for v2.2 prompt system fixes
- Expand prompts directory listing to show all 10 skill files explicitly
- Update prompt system table: '1_*.md' → 'N_*.md (N=1~10)' with numeric ordering
- Document load order guarantee (identity → skills 1-10 → soul → status)
- Document is_continuation behavior (no repeated pre_prompt on recursive calls)
- Add v2.2 changelog: prompt ordering fix, token waste reduction,
  recursion pre_prompt dedup, large-file threshold, memory search tuning
2026-08-01 03:24:39 +08:00
qing 1b9c727e80 fix(log_client): harden background flush thread against exceptions
- Wrap _flush_loop main body in outer try/except to prevent any
  unexpected exception (network errors, serialization errors, etc.)
  from killing the background thread (which would cause unbounded
  queue growth and eventual OOM)
- Wrap _send() call in inner try/except as final safety net
- Wrap RpcClient() construction in try/except (connection failures
  during client creation were previously unhandled)
- Add _safe_json() helper: unserializable extra fields fall back to
  repr()/str() instead of raising TypeError that could kill thread
- Generate timestamp at push() (enqueue time) instead of _send() time,
  so timestamps reflect actual event time even when queue is backed up
- Replace unreliable time.sleep(0.6) in flush() with _flush_done Event
  for proper synchronization between caller and background thread
- Add _QUEUE_MAXSIZE=10000 with drop-oldest policy to prevent unbounded
  memory growth even in pathological cases
- Use put_nowait with Full handling to ensure push() never blocks
  business threads
- Sync fix to all 4 copies: confs/static/smtp/dnfs
2026-08-01 03:23:19 +08:00
qing e8fa26d79f fix(aibox): fix prompt ordering, reduce per-turn token waste, and improve defaults
Prompt system bugs and improvements:

1. **SKILL_PREFIX bug fixed**: SKILL_PREFIX = "1_" only matched files starting
   with "1_", causing 2_ through 10_ skill prompt files to be misclassified
   as "other" files and sorted in wrong order (e.g., 10_task_handling.md
   sorted before 2_memory_save.md due to lexicographic ordering). Replaced
   with regex _SKILL_FILE_RE = re.compile(r'^(\d+)_') and _skill_sort_key()
   that extracts the numeric prefix for proper numeric sorting.

2. **_init_system_prompts used unsorted list**: get_prompts_list() returned
   prompts in os.listdir() lexicographic order, which put 100_STATUS.md
   before 1_memory_search.md and 10_task_handling.md before 1_memory_search.md.
   Now uses get_sorted_prompts_list() for correct ordering:
   identity → skill prompts (1-10 by number) → soul → status.

3. **Verbose pre_prompt injected every turn (~30 lines)**: The pre_prompt
   contained detailed workflow instructions (search_skills first, memory
   search, ask_user, etc.) that duplicated what system skill prompts
   already say, wasting ~300+ tokens per user message. Worse, the same
   verbose prompt was injected on recursive think_and_respond calls after
   tool execution, confusing the model with repeated "第一步搜索技能"
   instructions when it was already mid-conversation with tool results.
   - Reduced pre_prompt to 5 concise bullet points as a reminder
   - Added is_continuation parameter: recursive calls after tool execution
     skip pre_prompt entirely and just pass "请基于以上工具执行结果继续回答。"
   - Removed post_prompt (save_memory/update_soul instructions already in
     2_memory_save.md and 8_identity_management.md)

4. **Unreasonable large-file thresholds**: 3_file_operations.md default
   said >50KB or >200 lines is "too large" — too conservative for code
   files. Raised to 500KB/2000 lines; added tail=true and binary file
   guidance.

5. **Overly rigid memory search instruction**: 1_memory_search.md said
   "每次都要做!" (must do every time), causing unnecessary memory searches
   for simple greetings/acknowledgments. Softened to "根据话题相关性判断",
   with explicit examples of when to skip (greetings, confirmations).

New/changed methods:
- PromptManager._skill_sort_key(cls, filename) - extract numeric prefix
- PromptManager._prompt_sort_key(self, p) - tuple-based sort key
- PromptManager.get_sorted_prompts_list() - returns correctly ordered list
- PromptManager.get_skill_prompts() - uses regex instead of startswith("1_")
- DeepSeekChat.think_and_respond() - added is_continuation parameter
2026-08-01 03:19:58 +08:00
qing da7f11d352 fix(confs): add MAX_CONFIG_SIZE (1 MiB) to prevent OOM on oversized configs
- Add MAX_CONFIG_SIZE constant (1 MiB) to limit single config file size
- Check file size via os.stat() before reading into memory
- Oversized files raise ConfigError with descriptive message instead of
  being fully read (which could cause OOM with multi-GB files)
- Use single os.stat() call to get both mtime and size atomically
- Update README: document size limit in security mechanisms and notes
- Existing small configs (ini/json/yaml, typically <10KB) are unaffected
2026-08-01 03:15:06 +08:00
qing 9708fbc87e fix(pymsg): add recv_timeout to MsgServer to prevent Slowloris DoS
- Add configurable recv_timeout (default 10s) to MsgServer
- _handle_client now uses recv(s, timeout=...) instead of infinite blocking recv(s)
- Catch TimeoutError and close the slow client connection
- This prevents a single stalled connection from blocking the entire main select loop
- Update README with recv_timeout parameter and Slowloris mitigation docs
- Backward compatible: keyword-only arg with sensible default
2026-08-01 03:12:49 +08:00
qing ae42ab8a91 docs(aibox): update README for v2.1 robustness fixes and correct entry filename
- Fix all references from main.py to aibox.py (actual entry file)
- Update installation to use pip install -r requirements.txt
- Expand WebSocket message types table: add line/task_error/task_added/
  notification/connection_ack/interrupt_ack, correct chunk/complete/error semantics
- Fix message flow example: tool calls are reported via 'line' messages,
  not a non-existent 'tool_call' type
- Document _sanitize_tool_result() and error recovery (_rollback_messages)
  in development guide
- Add FAQ entry for tool-call-induced API failures
- Add v2.1 changelog covering all robustness fixes (reasoning_content
  filtering, tool_call id fallback, control character sanitization,
  message rollback on API failure)
2026-08-01 03:09:34 +08:00
qing a80bd582da fix(aibox): prevent tool results from corrupting conversation and breaking API requests
Root causes of API request failures after tool calls:
1. reasoning_content (an API response field) was echoed back in assistant
   messages, which could cause API format errors
2. tool_call id could be empty string when model didn't return one in a
   streaming chunk, producing invalid tool_calls that caused 400 errors
3. Tool execute() could return None or non-string types; direct string
   operations (len(), + "\n", [:200]) raised TypeError and produced
   non-string content in tool messages, breaking JSON serialization
4. Tool output (e.g. from run_command) could contain control characters
   (NULL/BEL/BS) that corrupted JSON payloads
5. When API request failed (network/4xx/5xx/parse error), messages added
   in that turn (user msg, assistant msg with tool_calls, tool responses)
   remained in self.messages, permanently poisoning conversation history
   so all subsequent requests would also fail

Fixes:
- Stop sending reasoning_content back to API in message construction;
  normalize assistant message content: null when tool_calls present, ""
  when neither content nor tool_calls exist, str otherwise
- Sanitize tool messages: ensure tool_call_id is non-empty (fallback id)
  and content is always a string
- Generate fallback tool_call id when model omits it; handle streaming id
  delivery where id arrives in a later chunk
- Add _sanitize_tool_result(): coerce None/non-string to str, strip
  control characters (preserve \n \t \r) to prevent JSON/API corruption
- Wrap execute_tool call in try/except in _handle_tool_calls for double
  safety; ensure history logging never crashes
- Truncate oversized results with an explicit note instead of replacing
  with a generic error that lost all useful output
- Add _rollback_messages(to_length) and record msgs_before_turn at start
  of think_and_respond; on any exception (RequestException, JSONDecodeError,
  or unexpected Exception), rollback self.messages to the pre-turn state
  so the conversation is not left in a broken state
- In _handle_tool_calls, wrap execute_tool in try/except and always
  produce a safe string result
2026-08-01 03:06:34 +08:00
qing 1b9e6f6b3a fix(botops): resolve duplicate AI response rendering in ChatView
Root cause: aibox.py think_and_respond recursively calls itself after tool
execution, and each recursive call's complete callback carries only the
final-round full_response. The old finish_ai logic treated complete as a
full-text alignment signal — when full_text != self._ai_text (which always
happened after recursive tool calls because _ai_text accumulated all chunks
while complete only held the last round), it triggered remove+remount,
causing visible duplicate/flash of AI responses. A secondary issue was
dialogue_ended arriving before complete, which made a second finish_ai call
remount the message a second time.

Changes:
- Remove the full-text replacement branch in finish_ai; chunk streaming is
  now the authoritative content source, complete is a finalization signal only
- Add _ai_finalized flag: start_ai resets it, finish_ai sets it; repeated
  finish_ai calls (dialogue_ended + complete) return early to prevent remount
- append_ai drops late chunks after finalization to avoid dirty appends
- Empty pre-built widget without chunks still gets filled via w.update(full_text)
- Full lost-frame fallback (no widget + full_text) is preserved
- Update BotOps/README.md protocol section to reflect new complete semantics
2026-08-01 02:56:29 +08:00
qing f003b4dfaa style(static): 注册页图形验证码统一为 code-box 布局
- register.html 图形验证码改用 .code-box 行内布局,与邮箱验证码对齐
- theme.css 新增可复用 .captcha-img 类,替代内联样式
- README 功能列表补充表单组件说明
2026-08-01 02:51:55 +08:00
qing eb506e5cbf docs(confs): 更新 README 反映 static 已接入现状
新增"已接入的服务"表格,记录 static 的三层 fail-safe 接入方式
及实现细节;注意事项改为指向集成章节,避免重复描述。
2026-08-01 02:46:58 +08:00
qing ec1eb1e805 docs(gateway): 补充前端接入图形验证码说明
- README.md v0.5.4 变更记录补充前端改动(register.html/register.js 接入图形验证码)
- SECURITY_AUDIT_20260801.md P0-1 修复说明补充第 5 项前端同步,改动文件清单补全
2026-08-01 02:43:06 +08:00
qing 6ee1eca789 feat(static): 注册页获取验证码接入图形验证码
配合 gateway v0.5.4 邮件轰炸防护(/register/send_code 强制 CAPTCHA):

- register.html: 邮箱与邮箱验证码之间新增图形验证码区块(图片+输入框,复用 login 页结构)
- register.js:
  - 新增 refreshCaptcha() 复用 /captcha/image 代理路由(与 login.js 一致)
  - 新增 checkCaptcha() 校验图形验证码非空且 Token 已加载
  - 获取验证码按钮点击时先校验图形验证码,再携带 captcha_token + captcha_answer 发送
  - 无论发送成败都刷新图形验证码(Token 一次性消费)
  - 重置表单时清空图形验证码并刷新

方案 A:仅在「获取验证码」时要求 CAPTCHA,注册提交不加(邮箱验证码已是二次验证)
2026-08-01 02:42:15 +08:00
qing 0ef96c28f6 docs(confs): 新增服务 README
含功能特性、协议路由、缓存/安全机制、集成关系与项目结构
2026-08-01 02:38:20 +08:00
qing 49f0698b9d fix(gateway,smtp): 修复邮件轰炸漏洞(审计 P0-1)
gateway v0.5.3 → v0.5.4:

1. gateway/src/main.cpp
   - /register/send_code 强制 CAPTCHA 校验(经 captcha:8009,Token 一次性消费,fail-closed)
   - 新增邮箱级双窗口限流(3 次/小时 + 10 次/天,CAPTCHA 通过后才计入配额)
   - 新增 _EMAIL_ADDR_RATE_HOUR / _EMAIL_ADDR_RATE_DAY 限流器实例

2. smtp/smtp_v2.py
   - 新增邮箱级长期限流兜底(EMAIL_HOURLY_LIMIT / EMAIL_DAILY_LIMIT,默认 3/10)
   - 新增全局熔断(SMTP_HOURLY_LIMIT,默认 100 封/小时,防 smtp 账号被封)
   - 预占/回滚与 60s 冷却同模式,发送失败不计入配额

3. gateway/docs/SECURITY_AUDIT_20260801.md
   - 新增带日期的安全审计报告
   - P0-1 标记为已修复 v0.5.4

4. gateway/README.md
   - 更新 /register/send_code 接口文档(CAPTCHA 必填 + 5 层防护)
   - 更新版本变更日志

防护层次:IP 限流 → CAPTCHA → 邮箱级限流(gateway)→ 60s 冷却 → 邮箱级限流(smtp)→ 全局熔断
2026-08-01 02:37:15 +08:00
qing 2774c3471f docs(readme): 补充 confs 统一配置服务说明
- 架构总览/服务表格/端口表新增 confs(8010),日志链路加入 confs,新增配置链路
- 新增 confs 小节:路由说明、mtime 自动重载、消费方接入约定
- static 配置说明更新为 confs 分发 + 本地回退
- 修正安装序列(补 captcha/confs)、init.txt 启动顺序与实际一致
2026-08-01 02:34:51 +08:00
qing 287cab189d feat(static): 配置接入 confs 统一配置服务
- fsys.py 启动时优先从 confs(127.0.0.1:8010)拉取 static.ini,
  支持 CONFS_HOST/CONFS_PORT 环境变量覆盖
- confs 不可用时 fail-safe 回退本地 config.ini
- static.ini 纳入 confs/configs/ 统一分发
2026-08-01 02:30:14 +08:00
qing 7d7e3dc10a fix(confs): 删除错放的 .env 文件
confs.py 不读取 .env(无 dotenv 依赖),该文件实为 SMTP 凭据,
被误放入 confs 目录且会导致 python-dotenv 解析报错。
2026-08-01 02:20:46 +08:00
qing 55fbe2f1f1 feat(confs): 统一配置服务(pymsg/TCP :8010)
- confs.py 重写为统一配置服务:get/list/reload/health 路由,
  内存缓存 + mtime 自动重载,路径遍历防护,ini/json 按键提取
- 复制 log_client.py,新增 configs/example.ini 示例配置
- 注册到 core/init.txt(db 之后启动)与 _inst.sh 安装序列
2026-08-01 02:15:04 +08:00
qing ae92a6edde perf(pymsg): send/recv 零拷贝优化
send: sendmsg 散射-聚集 I/O 替代 header+payload 拼接,内核直接
从两块内存读取,省掉大消息的用户态拷贝。

recv: recv_into 写入预分配 bytearray 替代 bytearray 逐步 extend,
省掉 realloc 和 bytes() 转换。
2026-08-01 02:08:03 +08:00
qing 267d572906 fix(pymsg): 修复消息长度无上限导致 OOM DoS 漏洞
Python/Go 端 recv 缺失消息大小上限检查,恶意客户端发送 4 字节
超大长度头即可触发 OOM。新增 64 MiB 上限(与 C++ 端 Msg.hpp
对齐),在分配内存前拒绝并断开连接。新增 5 个单元测试覆盖。
2026-08-01 01:55:44 +08:00
qing e248290860 统一配置模块 2026-08-01 01:54:25 +08:00
qing 48b45cb44a 修复网关bug 2026-07-31 22:19:56 +08:00
qing e7f435dc4e refix 2026-07-31 21:07:41 +08:00
qing ada2af47af 增加handle_exp包装器 2026-07-31 19:47:27 +08:00
qing ea670adde2 feat(static): dnfs 内容输入框随内容自动调整高度
- textarea 输入时自动增长,最小 120px,最大 400px
- 超过最大高度后出现滚动条,避免页面无限撑开
- 编辑已有节点时自动适配内容高度
- 提交/重置/退出编辑后恢复初始高度
- 禁用手动拖拽 resize,由自动增长统一控制
2026-07-30 22:21:24 +08:00
qing 5839172dae 迁移/whoami视图 2026-07-30 22:05:12 +08:00
qing 3f97dd92ef update 2026-07-30 21:57:04 +08:00
qing f935448389 refix 2026-07-30 21:33:03 +08:00
qing 32d3a5a19f 重构db 2026-07-30 20:15:02 +08:00