Files
claw/docs/superpowers/specs/2026-04-14-service-console-enhancement-design.md
木炎 c60cd308ca feat: service console auto-connect, settings panel, and batch of enhancements
- Auto-connect WebSocket on page load in service console
- Settings modal for editing sgclaw_config.json (API key, base URL, model, skills dir, etc.)
- UpdateConfig/ConfigUpdated protocol messages for remote config save
- save_to_path() for SgClawSettings serialization
- ConfigUpdated handler in sg_claw_client binary
- Protocol serialization tests for new message types
- HTML test assertions for auto-connect and settings UI
- Additional pending changes: deterministic submit, org units, lineloss xlsx export, browser script tool, and docs

🤖 Generated with [Qoder][https://qoder.com]
2026-04-14 14:32:46 +08:00

12 KiB

sgClaw Service Console Enhancement Design

Background

The current sg_claw_service_console.html provides a basic UI for connecting to the sgClaw service WebSocket and submitting tasks. However, it requires manual connection on first load and has no way to configure the sgClaw settings (API key, model, base URL, skills directory) from the UI.

Users need to manually edit sgclaw_config.json before using the console, which is inconvenient for routine operations.

Problem Statement

  1. Page requires manual "Connect" button click on first load
  2. No UI for configuring sgClaw runtime settings (model, API key, base URL, skills dir)
  3. Users must manually edit sgclaw_config.json file to change configuration

Goal

Enhance the service console page with:

  1. Auto-connect on page load - attempt WebSocket connection immediately
  2. Settings panel - edit sgClaw configuration fields through a friendly UI
  3. Config save via WebSocket - send configuration updates to the running sgClaw service, which writes them to sgclaw_config.json

Non-goals

  • Auto-starting sg_claw.exe process (browser security limitation, deferred)
  • Changing existing submit_task protocol or execution flow
  • Modifying browser-helper.html or browser execution logic
  • Adding authentication or multi-user support
  • Configuration validation beyond basic field checks

Architecture

Component Overview

┌─────────────────────────────────────────┐
│  sg_claw_service_console.html           │
│  ┌───────────────────────────────────┐  │
│  │ Auto-connect on load              │  │
│  │ (ws://127.0.0.1:42321 default)    │  │
│  └───────────────────────────────────┘  │
│  ┌───────────────────────────────────┐  │
│  │ Settings Panel (Modal)            │  │
│  │ - API Key                         │  │
│  │ - Base URL                        │  │
│  │ - Model                           │  │
│  │ - Skills Directory                │  │
│  │ - Direct Submit Skill (optional)  │  │
│  │ - Runtime Profile (dropdown)      │  │
│  │ - Browser Backend (dropdown)      │  │
│  │ [Save] [Cancel]                   │  │
│  └───────────────────────────────────┘  │
│  ┌───────────────────────────────────┐  │
│  │ Existing: Connection + Composer   │  │
│  └───────────────────────────────────┘  │
└──────────────┬──────────────────────────┘
               │ WebSocket
               │ submit_task / update_config
               ▼
┌─────────────────────────────────────────┐
│  sg_claw.exe (service)                  │
│  ┌───────────────────────────────────┐  │
│  │ ClientMessage handler             │  │
│  │ - SubmitTask (existing)           │  │
│  │ - UpdateConfig (new)              │  │
│  └───────────────────────────────────┘  │
│  ┌───────────────────────────────────┐  │
│  │ Config writer                     │  │
│  │ Writes to sgclaw_config.json      │  │
│  └───────────────────────────────────┘  │
└─────────────────────────────────────────┘

Data Flow

  1. Auto-connect flow:

    • Page loads → JavaScript calls connect() automatically
    • If WS opens → show "已连接" chip, enable send button
    • If WS fails → show "未连接" chip, keep send disabled
    • Reconnect logic remains unchanged (existing heartbeat/reconnect)
  2. Config save flow:

    • User clicks "设置" button → modal opens with current config values
    • User edits fields → clicks "保存"
    • Page sends update_config message via WS:
      {
        "type": "update_config",
        "config": {
          "apiKey": "...",
          "baseUrl": "...",
          "model": "...",
          "skillsDir": "...",
          "directSubmitSkill": "...",
          "runtimeProfile": "...",
          "browserBackend": "..."
        }
      }
      
    • sgClaw service receives message → validates → writes to sgclaw_config.json
    • Service responds with success/error → page shows notification
    • Service reloads config in-memory (or requires restart - see below)

Protocol Changes

New ClientMessage variant

Add to src/service/protocol.rs:

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ClientMessage {
    Connect,
    Start,
    Stop,
    SubmitTask { ... },
    Ping,
    UpdateConfig {  // NEW
        config: ConfigUpdatePayload,
    },
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ConfigUpdatePayload {
    pub api_key: Option<String>,
    pub base_url: Option<String>,
    pub model: Option<String>,
    pub skills_dir: Option<String>,
    pub direct_submit_skill: Option<String>,
    pub runtime_profile: Option<String>,
    pub browser_backend: Option<String>,
}

New ServiceMessage variant (optional)

Add to src/service/protocol.rs:

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ServiceMessage {
    StatusChanged { state: String },
    LogEntry { level: String, message: String },
    TaskComplete { success: bool, summary: String },
    Busy { message: String },
    Pong,
    ConfigUpdated { success: bool, message: String },  // NEW
}

Config Persistence

The service will:

  1. Load current sgclaw_config.json from the config path (derived from process args)
  2. Merge incoming ConfigUpdatePayload fields (only non-null fields are updated)
  3. Write the merged config back to the same file
  4. Respond with success/error message
  5. Hot reload: The service should reload config in-memory without requiring restart

Important: If the config file path cannot be resolved (no --config-path arg), the service should respond with an error message indicating that config updates are not supported in env-var-only mode.

UI Design

Settings Button

  • Add a "设置" button in the sidebar, below the existing connect button
  • Styled as a ghost button with a gear icon (using unicode ⚙ or CSS-only icon)

Settings Modal

  • Overlay modal with centered card
  • Form fields with labels in Chinese:
    • API 密钥 (apiKey) - password input type with show/hide toggle
    • 模型服务地址 (baseUrl) - text input
    • 模型名称 (model) - text input
    • Skills 目录路径 (skillsDir) - text input with path validation
    • 直接提交技能 (directSubmitSkill) - text input (optional, can be empty)
    • 运行模式 (runtimeProfile) - dropdown: browser-attached / service-standalone
    • 浏览器后端 (browserBackend) - dropdown: super-rpa / pipe / none
  • [保存] primary button, [取消] ghost button
  • Validation:
    • API Key and Model are required (show red error if empty on save)
    • Base URL must be a valid URL format
    • Skills Dir must be a valid path format
    • Other fields are optional

Connection State Auto-detection

  • On page load, call connect() automatically
  • Connection state chip updates as before
  • Reconnect logic (existing) remains unchanged

File Changes

File Change
frontend/service-console/sg_claw_service_console.html Add auto-connect on load, settings modal UI, save logic
src/service/protocol.rs Add UpdateConfig variant and ConfigUpdatePayload struct
src/service/protocol.rs Add ConfigUpdated service message variant
src/service/server.rs Handle UpdateConfig message, merge config, write file
src/agent/task_runner.rs Add pub fn config_path(&self) -> Option<&Path> getter to AgentRuntimeContext
src/config/settings.rs Add save_to_path() method for writing config to file
tests/service_console_html_test.rs Add assertions for settings modal and update_config message

Config Save Implementation

In src/service/server.rs, when handling UpdateConfig:

ClientMessage::UpdateConfig { config } => {
    // 1. Load current config from config_path
    let config_path = runtime_context.config_path(); // needs to be exposed
    let current = SgClawSettings::load(config_path.as_deref())?;

    // 2. Merge: only overwrite fields that are Some in the payload
    let mut merged = current.unwrap_or_default();
    if let Some(v) = config.api_key { merged.provider_api_key = v; }
    if let Some(v) = config.base_url { merged.provider_base_url = v; }
    if let Some(v) = config.model { merged.provider_model = v; }
    if let Some(v) = config.skills_dir { merged.skills_dir = Some(PathBuf::from(v)); }
    // ... etc for other fields

    // 3. Write back to file
    merged.save_to_path(config_path.as_ref().ok_or("no config path")?)?;

    // 4. Respond
    sink.send_service_message(ServiceMessage::ConfigUpdated {
        success: true,
        message: "配置已保存".to_string(),
    })?;
}

Hot Reload Consideration

After saving config, the service should reload its in-memory settings. This requires:

  1. Storing the loaded SgClawSettings in a reloadable container (e.g., Arc<Mutex<SgClawSettings>> or Arc<RwLock<...>>)
  2. Or, the service can respond with "配置已保存,请重启 sg_claw 以应用更改" (simpler, avoids hot reload complexity)

Recommended: Start with "requires restart" approach. Hot reload can be added later if needed.

Error Handling

Scenario Response
WS not connected when saving Show inline error: "请先连接服务"
Config file not found Service responds: "未找到配置文件,请通过 --config-path 指定"
Invalid config values Service validates and responds with specific error
Write permission denied Service responds: "无法写入配置文件,请检查文件权限"
WS disconnected during save Show error: "连接断开,保存失败,请重试"

Test Strategy

  1. Integration test (tests/service_console_html_test.rs):

    • Assert page contains settings modal HTML
    • Assert page contains "设置" button
    • Assert page sends update_config message shape
    • Assert page auto-connects on load (contains window.onload or equivalent)
  2. Protocol test (new or existing test file):

    • Assert ClientMessage::UpdateConfig serializes correctly
    • Assert ServiceMessage::ConfigUpdated deserializes correctly
  3. Config save test (new test in tests/compat_config_test.rs or new file):

    • Create temp config file
    • Send UpdateConfig message
    • Verify file contents match expected merged config

Acceptance Criteria

  1. Page auto-connects to WS on load without manual button click
  2. Settings button visible in sidebar
  3. Settings modal opens with form fields for all configurable options
  4. Clicking "保存" sends update_config message via WS
  5. Service receives message and writes to sgclaw_config.json
  6. Service responds with success/error message
  7. Page displays save result notification
  8. Existing task submission flow unchanged
  9. Existing heartbeat/reconnect logic unchanged
  10. Automated tests pass