Skill: adeted/slbrow-skill (version 1.0.0)

--- name: slbrow-skills description: Browser automation via SLBrow MCP and HTTP API. Use when the user needs to navigate pages, analyze content, manage tabs, search history, extract text, or control the browser. Supports both MCP tools and direct curl commands. Requires SLBrow server running and browser extension installed. --- # SLBrow Browser Automation (Skills for Browser) Skills for browser-side AI assistant. The browser plugin (aiassist) connects to slbrow-mcp via MCP protocol; these skills guide when and how to use the tools. **Can be used via MCP tools or direct HTTP API calls with curl.** ## Prerequisites 1. **SLBrow server** running: `npx slbrow` or `npm start` in `ai/slbrowmcp/slbrow-mcp/` 2. **Browser extension** installed and connected (Chrome/Firefox) 3. **MCP plugin** enabled in aiassist: `http://127.0.0.1:5556/mcp` (视联浏览器MCP), `http://127.0.0.1:5556/vaimcp` (视联视频AI MCP) 4. Default ports: HTTP 5556, WebSocket 5555 ## Usage Methods ### Method 1: MCP Tools (Browser Plugin + MCP) When the MCP plugin is enabled, the AI has access to browser tools. Use these skills to guide tool selection and workflow: - **Navigation**: Use `page_navigate` to open URLs - **Analysis**: Use `page_analyze` with intent_hint (article, form submit, post_create) before extraction - **Tabs**: Use `tab_list` first, then pass `tab_id` to target specific tabs ### Method 2: HTTP API with curl (Direct Control) **Always available** - Use curl commands to control browser directly without MCP plugin dependency: **Basic format (PowerShell-friendly — no temp JSON file):** ```bash curl.exe -X POST http://localhost:5556/api/execute -H 'Content-Type: application/json' -d '{\"tool\":\"TOOL_NAME\",\"args\":{\"PARAM\":\"VALUE\"}}' ``` **Important notes for Windows PowerShell:** - Use `curl.exe` instead of `curl` (PowerShell aliases `curl` to `Invoke-WebRequest`) - Put the JSON body in `-d '...'` with **single quotes** around the whole argument; **backslash-escape every double quote** in the JSON (`\"`). That way the shell passes valid JSON to curl without needing `--data-binary @file` **Common curl examples:** ```bash # Create new tab and navigate curl.exe -X POST http://localhost:5556/api/execute -H 'Content-Type: application/json' -d '{\"tool\":\"tab_create\",\"args\":{\"url\":\"https://www.163.com\"}}' # Navigate current tab curl.exe -X POST http://localhost:5556/api/execute -H 'Content-Type: application/json' -d '{\"tool\":\"page_navigate\",\"args\":{\"url\":\"https://www.example.com\"}}' # List all tabs curl.exe -X POST http://localhost:5556/api/execute -H 'Content-Type: application/json' -d '{\"tool\":\"tab_list\",\"args\":{}}' # Analyze page content curl.exe -X POST http://localhost:5556/api/execute -H 'Content-Type: application/json' -d '{\"tool\":\"page_analyze\",\"args\":{\"intent_hint\":\"article\"}}' # Extract article content curl.exe -X POST http://localhost:5556/api/execute -H 'Content-Type: application/json' -d '{\"tool\":\"page_extract_content\",\"args\":{\"content_type\":\"article\"}}' # Close specific tab curl.exe -X POST http://localhost:5556/api/execute -H 'Content-Type: application/json' -d '{\"tool\":\"tab_close\",\"args\":{\"tab_id\":1234567890}}' # Search browser history curl.exe -X POST http://localhost:5556/api/execute -H 'Content-Type: application/json' -d '{\"tool\":\"get_history\",\"args\":{\"keywords\":\"search term\",\"max_results\":10}}' ``` **Response format:** API returns JSON with `success`, `tool`, `result`, and `formatted` fields for easy parsing. ## Available Tools | Tool | Description | |------|-------------| | `page_navigate` | Navigate current tab to URL | | `page_analyze` | Analyze page for elements (intent_hint: article, form submit, post_create, etc.) | | `page_extract_content` | Extract content (content_type: article, search_results, posts) | | `page_wait_for` | Wait for element or text (condition_type, selector, text) | | `tab_create` | Create tab(s) (url, urls[], or count) | | `tab_close` | Close tab(s) (tab_id, tab_ids[]) | | `tab_list` | List open tabs with IDs | | `get_history` | Search browser history (keywords, start_date, end_date, domains, max_results) | | `get_selected_text` | Get selected text (optional tab_id) | | `get_page_links` | Get page links (link_type: all/internal/external, domains, max_results) | ## VAI Tools (Seelink) | Tool | Description | |------|-------------| | `get_page_seelink_player_list` | Get all players on current page | | `use_seelink_players_ai` | Apply AI function to players (ai_function_name: reduce_fog, face_mosaic, dark_reduce, human_outline, vechicle_outline, none) | ## Workflow Patterns **Multi-tab workflow:** 1. `tab_list` to get tab IDs 2. Add `tab_id` to any tool to target a background tab **Content extraction:** 1. `page_navigate` to target URL 2. `page_analyze` with intent_hint to find elements 3. `page_extract_content` for article/search_results/posts **Form filling:** 1. `page_analyze` with intent_hint "form submit" to discover form elements 2. Note the element IDs returned — use them with subsequent `page_analyze` or `page_extract_content` to confirm state **VAI (Seelink player AI):** 1. `get_page_seelink_player_list` to get player IDs and count 2. Choose targets by `player_position_list` (zero-based index) **or** `player_id_list` (not both) 3. `use_seelink_players_ai` with chosen targets and `ai_function_name` 4. To apply different functions to different players, call `use_seelink_players_ai` separately for each 5. To disable AI on a player, set `ai_function_name` to `"none"` ## Fetching Skills in Browser The browser plugin can fetch skills from the server to inject into AI context: ``` GET http://127.0.0.1:5556/api/skills ``` Returns the full SKILL.md content (text/markdown). Use when slbrow MCP plugins are enabled to guide tool usage. ## API Reference For full parameter schemas, see [references/api-reference.md](references/api-reference.md). ## Additional resources - One-page workflow checklist: [SKILL_COMPACT.md](SKILL_COMPACT.md) ## Error Handling - `EXTENSION_DISCONNECTED`: Ensure extension is installed and server is running. Check `http://localhost:5556/health` first. - `Tool call timeout`: Operation took >30s; retry with simpler args or break into smaller steps. - `page_analyze` returns 0 elements: Page may still be loading — wait a moment and retry, or `page_navigate` again. - `tab_id` invalid (tab was closed): Call `tab_list` to refresh available tab IDs before retrying. - Server restarted (session lost): Client will receive 404; re-initialize MCP connection (refresh the page). ## Quick Reference for curl Commands **Tab Management:** - Create tab: `tab_create` with `url` parameter - List tabs: `tab_list` with empty args - Close tab: `tab_close` with `tab_id` parameter - Navigate: `page_navigate` with `url` parameter **Content Analysis:** - Analyze page: `page_analyze` with `intent_hint` (article, form_submit, post_create) - Extract content: `page_extract_content` with `content_type` (article, search_results, posts) - Get links: `get_page_links` with `link_type` (all, internal, external) **History & Text:** - Search history: `get_history` with `keywords`, `max_results` - Get selected text: `get_selected_text` with optional `tab_id` **Seelink AI:** - Get players: `get_page_seelink_player_list` - Apply AI: `use_seelink_players_ai` with `player_position_list` or `player_id_list` and `ai_function_name` ## Troubleshooting **Server not responding:** - Check if SLBrow server is running: `curl.exe http://localhost:5556/health` - Verify server is started with `npx slbrow` or `npm start` **Curl command fails:** - Ensure using `curl.exe` in PowerShell - For `-d`, use single-quoted JSON with `\"` around each JSON string delimiter (see **Method 2** examples above) - Verify server is accessible on port 5556 **Tab operations not working:** - Use `tab_list` to get current tab IDs first - Verify tab_id is valid and tab still exists - Check if browser extension is connected

path: references/api-reference.md
content: # SLBrow REST API Reference

Base URL: `http://localhost:5556/api` (port configurable via `--port`)

## Endpoints

### GET /api/tools

Returns available browser and VAI tools.

**Response:**
```json
{
  "browser": [...],
  "vai": [...],
  "all": [...]
}
```

### POST /api/execute

Execute a tool.

**Request body:**
```json
{
  "tool": "page_navigate",
  "args": { "url": "https://example.com" }
}
```

**Response (success):**
```json
{
  "success": true,
  "tool": "page_navigate",
  "result": { "url": "https://example.com", "execution_time": 123 },
  "formatted": "✅ Successfully navigated to: https://example.com"
}
```

**Response (error):**
```json
{
  "success": false,
  "error": "Error message",
  "code": "EXTENSION_DISCONNECTED"
}
```

## Error Codes

| Code | Meaning | Resolution |
|------|---------|------------|
| `EXTENSION_DISCONNECTED` | Browser extension not connected | Install extension, check server is running |
| `TOOL_TIMEOUT` | Tool execution exceeded 30s | Retry with simpler parameters |
| `UNKNOWN_TOOL` | Tool name not found | Check tool name against `/api/tools` |
| `INVALID_ARGS` | Missing or invalid parameters | Verify required fields per tool schema |

## Browser Tools

### page_navigate
Navigate current tab to URL.
- `url` (string, required)

### page_analyze
Analyze page for elements.
- `intent_hint` (string, required): article, form submit, post_create, etc.
- `tab_id` (number, optional): target background tab

### page_extract_content
Extract content from page.
- `content_type` (string, required): article | search_results | posts
- `tab_id` (number, optional)

### page_wait_for
Wait for element or text.
- `condition_type` (string, required): element_visible | text_present
- `selector` (string, optional)
- `text` (string, optional)

### tab_create
Create tab(s).
- `url` (string)
- `urls` (array of strings)
- `count` (number, 1-50)

### tab_close
Close tab(s).
- `tab_id` (number)
- `tab_ids` (array of numbers)

### tab_list
List open tabs with IDs.

### get_history
Search browser history.
- `keywords` (string)
- `start_date`, `end_date` (ISO date-time)
- `domains` (array of strings)
- `max_results` (number, max 500)

### get_selected_text
Get selected text.
- `tab_id` (number, optional)

### get_page_links
Get page links.
- `link_type`: all | internal | external
- `domains` (array)
- `max_results` (max 200)

## VAI Tools (Seelink)

### get_page_seelink_player_list
Get all players on current page. No args.

### use_seelink_players_ai
Apply AI function to players.
- `ai_function_name` (required): reduce_fog | face_mosaic | dark_reduce | human_outline | vechicle_outline | none
- `player_position_list` (array of numbers): zero-based positions (use OR `player_id_list`, not both)
- `player_id_list` (array of strings): player IDs (use OR `player_position_list`, not both)

**Example response:**
```json
{
  "success": true,
  "tool": "use_seelink_players_ai",
  "result": { "result": "success", "message": "AI function control action for all players are completed successfully" },
  "formatted": "{\"result\":\"success\",...}"
}
```

## Health Check

### GET /health

Check server status.

**Response:**
```json
{ "status": "ok", "ws_clients": 1 }
```

## cURL Examples

```bash
# Navigate to a URL
curl -X POST http://localhost:5556/api/execute \
  -H 'Content-Type: application/json' \
  -d '{"tool":"page_navigate","args":{"url":"https://example.com"}}'

# List open tabs
curl -X POST http://localhost:5556/api/execute \
  -H 'Content-Type: application/json' \
  -d '{"tool":"tab_list","args":{}}'

# Get Seelink player list
curl -X POST http://localhost:5556/api/execute \
  -H 'Content-Type: application/json' \
  -d '{"tool":"get_page_seelink_player_list","args":{}}'

# Apply AI function to player at position 0
curl -X POST http://localhost:5556/api/execute \
  -H 'Content-Type: application/json' \
  -d '{"tool":"use_seelink_players_ai","args":{"player_position_list":[0],"ai_function_name":"face_mosaic"}}'
```


path: scripts/slbrow.js
content: #!/usr/bin/env node
/**
 * SLBrow CLI - Execute browser automation tools via REST API
 * Usage: node slbrow.js <tool> [--arg value] [--port 5556]
 * Example: node slbrow.js page_navigate --url "https://example.com"
 */

const path = require('path');
const fs = require('fs');
const DEFAULT_PORT = 5556;

// Dynamically load tool list from tools.json (single source of truth)
function loadTools() {
  try {
    const toolsPath = path.join(__dirname, '..', 'tools.json');
    const raw = fs.readFileSync(toolsPath, 'utf8');
    return JSON.parse(raw);
  } catch {
    return { browser: [], vai: [] };
  }
}

function getToolNames() {
  const config = loadTools();
  const browser = (config.browser || []).map(t => t.name);
  const vai = (config.vai || []).map(t => t.name);
  return { browser, vai, all: [...browser, ...vai] };
}

function parseArgs(argv) {
  const args = argv.slice(2);
  const result = { tool: null, port: DEFAULT_PORT, args: {} };

  for (let i = 0; i < args.length; i++) {
    const arg = args[i];
    if (arg === '--port' && args[i + 1]) {
      result.port = parseInt(args[++i], 10) || DEFAULT_PORT;
    } else if (arg === '--help' || arg === '-h') {
      return { help: true };
    } else if (arg === '--list') {
      return { list: true };
    } else if (arg.startsWith('--') && !arg.includes('=')) {
      const key = arg.slice(2).replace(/-/g, '_');
      const next = args[i + 1];
      if (next !== undefined && !next.startsWith('--')) {
        result.args[key] = next;
        i++;
      } else {
        result.args[key] = true;
      }
    } else if (arg.startsWith('--') && arg.includes('=')) {
      const [k, v] = arg.slice(2).split('=');
      result.args[k.replace(/-/g, '_')] = v;
    } else if (!result.tool && !arg.startsWith('--')) {
      result.tool = arg;
    }
  }

  return result;
}

function coerceArgs(obj) {
  const out = {};
  for (const [k, v] of Object.entries(obj)) {
    if (v === 'true') out[k] = true;
    else if (v === 'false') out[k] = false;
    else if (v === 'null') out[k] = null;
    else if (/^\d+$/.test(v)) out[k] = parseInt(v, 10);
    else if (/^\d+\.\d+$/.test(v)) out[k] = parseFloat(v);
    else if (v && typeof v === 'string' && (v.startsWith('[') || v.startsWith('{'))) {
      try {
        out[k] = JSON.parse(v);
      } catch {
        out[k] = v;
      }
    } else {
      out[k] = v;
    }
  }
  return out;
}

async function main() {
  const parsed = parseArgs(process.argv);

  if (parsed.help) {
    const { browser, vai } = getToolNames();
    console.log(`
SLBrow CLI - Execute browser automation tools

Usage: node slbrow.js <tool> [--arg value] [--port PORT]

Options:
  --port PORT    HTTP port (default: ${DEFAULT_PORT})
  --list         List all available tools by category
  --help, -h     Show this help

Examples:
  node slbrow.js page_navigate --url "https://example.com"
  node slbrow.js tab_list
  node slbrow.js page_analyze --intent_hint "article"
  node slbrow.js get_history --keywords "react" --max_results 10
  node slbrow.js get_page_seelink_player_list
  node slbrow.js use_seelink_players_ai --player_position_list "[0]" --ai_function_name "reduce_fog"

Browser Tools:
  ${browser.join(', ') || '(none)'}

VAI Tools (Seelink):
  ${vai.join(', ') || '(none)'}
`);
    process.exit(0);
  }

  if (parsed.list) {
    const { browser, vai } = getToolNames();
    console.log('Browser Tools:');
    browser.forEach(name => console.log(`  ${name}`));
    console.log('\nVAI Tools (Seelink):');
    vai.forEach(name => console.log(`  ${name}`));
    process.exit(0);
  }

  if (!parsed.tool) {
    console.error('Error: Missing tool name. Use --help for usage.');
    process.exit(1);
  }

  const port = parsed.port;
  const baseUrl = `http://127.0.0.1:${port}`;
  const args = coerceArgs(parsed.args);

  try {
    const res = await fetch(`${baseUrl}/api/execute`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ tool: parsed.tool, args }),
    });

    const data = await res.json().catch(() => ({}));

    if (!res.ok) {
      console.error('Error:', data.error || res.statusText);
      if (data.code === 'EXTENSION_DISCONNECTED') {
        console.error('Ensure the browser extension is installed and the server is running.');
      }
      process.exit(1);
    }

    if (data.formatted) {
      console.log(data.formatted);
    } else {
      console.log(JSON.stringify(data, null, 2));
    }
  } catch (err) {
    console.error('Error:', err.message);
    if (err.cause?.code === 'ECONNREFUSED') {
      console.error(`Cannot connect to ${baseUrl}. Is the SLBrow server running?`);
    }
    process.exit(1);
  }
}

main();


path: SKILL_COMPACT.md
content: # SLBrow Workflow (compact)

- **Tabs**: `tab_list` first, then pass `tab_id` to target background tabs.
- **Content**: `page_navigate` → `page_analyze`(intent_hint) → `page_extract_content`.
- **Forms**: `page_analyze`(intent_hint:"form submit") to get element IDs, then interact via element IDs.
- **VAI**: `get_page_seelink_player_list` first to get player IDs/count → `use_seelink_players_ai`(ai_function_name, player_position_list OR player_id_list, not both). Functions: reduce_fog, face_mosaic, dark_reduce, human_outline, vechicle_outline, none. Call separately per function.
- **Fallback (no MCP tools)**: `curl -X POST http://localhost:5556/api/execute -H 'Content-Type: application/json' -d '{"tool":"use_seelink_players_ai","args":{"player_position_list":[0],"ai_function_name":"face_mosaic"}}'`


path: tools.json
content: {
  "browser": [
    {
      "name": "page_analyze",
      "description": "Analyze page. Optional tab_id for background tab.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "intent_hint": { "type": "string" },
          "tab_id": { "type": "number" }
        },
        "required": ["intent_hint"]
      }
    },
    {
      "name": "page_extract_content",
      "description": "Extract content. content_type: article|search_results|posts. Optional tab_id.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "content_type": { "type": "string", "enum": ["article", "search_results", "posts"] },
          "tab_id": { "type": "number" }
        },
        "required": ["content_type"]
      }
    },
    {
      "name": "page_navigate",
      "description": "Navigate current tab to URL.",
      "inputSchema": {
        "type": "object",
        "properties": { "url": { "type": "string" } },
        "required": ["url"]
      }
    },
    {
      "name": "page_wait_for",
      "description": "Wait for element or condition. condition_type: element_visible|text_present; selector or text.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "condition_type": { "type": "string", "enum": ["element_visible", "text_present"] },
          "selector": { "type": "string" },
          "text": { "type": "string" }
        },
        "required": ["condition_type"]
      }
    },
    {
      "name": "tab_create",
      "description": "Create tab(s): url, or urls[], or url+count (1-50).",
      "inputSchema": {
        "type": "object",
        "properties": {
          "url": { "type": "string" },
          "urls": { "type": "array", "items": { "type": "string" }, "maxItems": 100 },
          "count": { "type": "number", "minimum": 1, "maximum": 50 }
        }
      }
    },
    {
      "name": "tab_close",
      "description": "Close tab(s): tab_id, tab_ids[], or omit for current.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "tab_id": { "type": "number" },
          "tab_ids": { "type": "array", "items": { "type": "number" } }
        }
      }
    },
    {
      "name": "tab_list",
      "description": "List open tabs with IDs.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      }
    },
    {
      "name": "get_history",
      "description": "Search browser history. keywords, start_date, end_date (ISO), domains[], max_results (max 500).",
      "inputSchema": {
        "type": "object",
        "properties": {
          "keywords": { "type": "string" },
          "start_date": { "type": "string", "format": "date-time" },
          "end_date": { "type": "string", "format": "date-time" },
          "domains": { "type": "array", "items": { "type": "string" } },
          "max_results": { "type": "number", "maximum": 500 }
        }
      }
    },
    {
      "name": "get_selected_text",
      "description": "Get selected text. Optional tab_id.",
      "inputSchema": {
        "type": "object",
        "properties": { "tab_id": { "type": "number" } }
      }
    },
    {
      "name": "get_page_links",
      "description": "Get page links. link_type: all|internal|external; domains[]; max_results (max 200).",
      "inputSchema": {
        "type": "object",
        "properties": {
          "link_type": { "type": "string", "enum": ["all", "internal", "external"] },
          "domains": { "type": "array", "items": { "type": "string" } },
          "max_results": { "type": "number", "maximum": 200 }
        }
      }
    }
  ],
  "vai": [
    {
      "name": "get_page_seelink_player_list",
      "description": "Get all players on current page.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      }
    },
    {
      "name": "use_seelink_players_ai",
      "description": "Select one AI function or none for specified player(s) or 宫格(grid) on the page. Use 'player_position_list' or 'player_id_list' to specify targets (not both). Call separately for each different AI function needed. To disable any AI, set 'ai_function_name' to \"none\". Run this tool each time as results may change.",
      "inputSchema": {
        "type": "object",
        "examples": [
          { "player_position_list": [0, 3], "ai_function_name": "reduce_fog" },
          { "player_id_list": ["id-of-player-x"], "ai_function_name": "face_mosaic" },
          { "player_id_list": ["id-of-player-x", "id-of-player-y"], "ai_function_name": "none" }
        ],
        "properties": {
          "player_position_list": {
            "type": "array",
            "description": "Array of player positions (zero-based)",
            "items": { "type": "number" },
            "maxItems": 100
          },
          "player_id_list": {
            "type": "array",
            "description": "Array of player IDs",
            "items": { "type": "string" },
            "maxItems": 100
          },
          "ai_function_name": {
            "type": "string",
            "description": "reduce_fog: 去雾. face_mosaic: 人脸打码. dark_reduce: 暗场景增强. human_outline: 行人画框. vechicle_outline: 车辆画框. none: Disable.",
            "enum": ["reduce_fog", "face_mosaic", "dark_reduce", "human_outline", "vechicle_outline", "none"]
          }
        },
        "required": ["ai_function_name"]
      }
    }
  ]
}


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)