Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
eabd863
chore: UI/UX Pro Max refresh for console.css
zsj1024 Mar 21, 2026
84e648b
fix: responsive layout for hero-lanes
zsj1024 Mar 21, 2026
1f458b4
fix: complete tavily settings, mask token, replace alerts, restore he…
zsj1024 Mar 21, 2026
c6c9ba4
fix: use details/summary for token/key tables to save space
zsj1024 Mar 21, 2026
c0c5d58
fix: finish detail summaries
zsj1024 Mar 21, 2026
dcab489
Refine proxy console and document runtime fixes
zsj1024 Mar 22, 2026
2b2e48b
Add proxy Docker publish workflow
zsj1024 Mar 22, 2026
ebfee8a
Polish console scrollbar styling
zsj1024 Mar 22, 2026
4476d53
Add all-in-one MySearch stack deployment
zsj1024 Mar 22, 2026
7c47565
fix stack external proxy bind and docs
zsj1024 Mar 22, 2026
1d1bd74
fix stack proxy import path
zsj1024 Mar 22, 2026
e39f7cb
fix tavily hikari upstream fallback
zsj1024 Mar 22, 2026
fae9424
fix tavily upstream 404 compatibility retry
zsj1024 Mar 22, 2026
567041c
refine mysearch docs routing and official fallback
zsj1024 Mar 22, 2026
c64d7f3
unify proxy sqlite path across docker deployments
zsj1024 Mar 22, 2026
422c937
enhance search evidence and restricted-domain routing
zsj1024 Mar 22, 2026
648cea5
tighten official docs policy and research evidence
zsj1024 Mar 22, 2026
9ad10c6
Merge branch 'skernelx:main' into main
goodnightzsj Mar 23, 2026
67b42e5
refine x provider health probes and ignore llmdoc
zsj1024 Mar 23, 2026
85884cf
merge upstream search fallback and error handling
zsj1024 Mar 23, 2026
48d7f39
merge upstream/main after manual integration
zsj1024 Mar 23, 2026
a991af9
tighten provider routing and research budgets
zsj1024 Mar 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.git
.github
.codex-tasks
llmdoc
venv
.venv
__pycache__
*.pyc
*.pyo
*.pyd
.env
.env.*
proxy/data
proxy/__pycache__
mysearch/__pycache__
openclaw/__pycache__
tests/__pycache__
docs/images
200 changes: 200 additions & 0 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
name: Build and Publish Docker Images

on:
push:
branches:
- main
tags:
- "v*"
pull_request:
branches:
- main
workflow_dispatch:

concurrency:
group: docker-publish-${{ github.ref }}
cancel-in-progress: true

jobs:
verify:
name: Verify proxy runtime and tests
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: pip
cache-dependency-path: |
mysearch/requirements.txt
proxy/requirements.txt

- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
pip install -r mysearch/requirements.txt -r proxy/requirements.txt

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "20"

- name: Run Python test suite
run: python -m unittest discover -s tests

- name: Run Python syntax checks
run: |
python -m py_compile \
proxy/server.py \
mysearch/config.py \
mysearch/clients.py \
mysearch/keyring.py \
mysearch/social_gateway.py \
openclaw/runtime/mysearch/config.py \
openclaw/runtime/mysearch/clients.py \
openclaw/runtime/mysearch/keyring.py

- name: Run frontend syntax check
run: node --check proxy/static/js/console.js

docker:
name: Build and publish ${{ matrix.target_label }} image
runs-on: ubuntu-latest
needs: verify
strategy:
fail-fast: false
matrix:
include:
- target: proxy
target_label: proxy
context: ./proxy
dockerfile: ./proxy/Dockerfile
default_image_name: mysearch-proxy
- target: mysearch
target_label: mysearch MCP
context: ./mysearch
dockerfile: ./mysearch/Dockerfile
default_image_name: mysearch-mcp
- target: stack
target_label: all-in-one stack
context: .
dockerfile: ./Dockerfile.stack
default_image_name: mysearch-stack
env:
DOCKERHUB_USERNAME_VAR: ${{ vars.DOCKERHUB_USERNAME }}
DOCKERHUB_USERNAME_SECRET: ${{ secrets.DOCKERHUB_USERNAME }}
IMAGE_NAME_PROXY_VAR: ${{ vars.DOCKERHUB_IMAGE_NAME_PROXY }}
IMAGE_NAME_PROXY_SECRET: ${{ secrets.DOCKERHUB_IMAGE_NAME_PROXY }}
IMAGE_NAME_MYSEARCH_VAR: ${{ vars.DOCKERHUB_IMAGE_NAME_MYSEARCH }}
IMAGE_NAME_MYSEARCH_SECRET: ${{ secrets.DOCKERHUB_IMAGE_NAME_MYSEARCH }}
IMAGE_NAME_STACK_VAR: ${{ vars.DOCKERHUB_IMAGE_NAME_STACK }}
IMAGE_NAME_STACK_SECRET: ${{ secrets.DOCKERHUB_IMAGE_NAME_STACK }}
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Resolve Docker Hub image repository
id: image
env:
GITHUB_EVENT_NAME: ${{ github.event_name }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
TARGET: ${{ matrix.target }}
DEFAULT_IMAGE_NAME: ${{ matrix.default_image_name }}
run: |
trim_single_line() {
printf '%s' "$1" | tr -d '\r\n' | sed 's/^[[:space:]]*//; s/[[:space:]]*$//'
}

DOCKERHUB_USERNAME="$(trim_single_line "${DOCKERHUB_USERNAME_VAR:-${DOCKERHUB_USERNAME_SECRET:-}}")"
case "${TARGET}" in
proxy)
IMAGE_NAME_RAW="${IMAGE_NAME_PROXY_VAR:-${IMAGE_NAME_PROXY_SECRET:-${DEFAULT_IMAGE_NAME}}}"
;;
mysearch)
IMAGE_NAME_RAW="${IMAGE_NAME_MYSEARCH_VAR:-${IMAGE_NAME_MYSEARCH_SECRET:-${DEFAULT_IMAGE_NAME}}}"
;;
stack)
IMAGE_NAME_RAW="${IMAGE_NAME_STACK_VAR:-${IMAGE_NAME_STACK_SECRET:-${DEFAULT_IMAGE_NAME}}}"
;;
*)
echo "Unsupported target: ${TARGET}"
exit 1
;;
esac

IMAGE_NAME_CLEAN="$(trim_single_line "${IMAGE_NAME_RAW}" | tr '[:upper:]' '[:lower:]')"

test -n "${DOCKERHUB_USERNAME}" || {
echo "Missing Docker Hub username. Set Actions Variable DOCKERHUB_USERNAME or Secret DOCKERHUB_USERNAME."
exit 1
}

test -n "${IMAGE_NAME_CLEAN}" || {
echo "Missing Docker image name. Set Actions Variable DOCKERHUB_IMAGE_NAME."
exit 1
}

case "${DOCKERHUB_USERNAME}" in
*/*|*:*|*" "*)
echo "Invalid Docker Hub username."
exit 1
;;
esac

case "${IMAGE_NAME_CLEAN}" in
*/*|*:*|*" "*)
echo "Invalid Docker image name."
exit 1
;;
esac

if [ "${GITHUB_EVENT_NAME}" != "pull_request" ]; then
test -n "${DOCKERHUB_TOKEN}" || {
echo "Missing Docker Hub token. Set Actions Secret DOCKERHUB_TOKEN."
exit 1
}
fi

IMAGE_REPOSITORY="${DOCKERHUB_USERNAME}/${IMAGE_NAME_CLEAN}"
echo "dockerhub_username=${DOCKERHUB_USERNAME}" >> "$GITHUB_OUTPUT"
echo "image_repository=${IMAGE_REPOSITORY}" >> "$GITHUB_OUTPUT"
echo "Resolved Docker image repository: ${IMAGE_REPOSITORY}"

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
username: ${{ steps.image.outputs.dockerhub_username }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ steps.image.outputs.image_repository }}
tags: |
type=raw,value=latest,enable={{is_default_branch}}
type=ref,event=branch
type=ref,event=tag
type=sha,format=short

- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: ${{ matrix.context }}
file: ${{ matrix.dockerfile }}
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,9 @@ openclaw/.venv/
# 本地测试输出
htmlcov/
.coverage

# 本地 AI 辅助产物
.ace-tool/
llmdoc/
.codex-tasks/
extract.py
23 changes: 23 additions & 0 deletions Dockerfile.stack
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
FROM python:3.11-slim

ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONPATH=/app \
MYSEARCH_PROXY_BASE_URL=http://127.0.0.1:9874 \
MYSEARCH_PROXY_DB_PATH=/data/proxy.db

WORKDIR /app

COPY proxy/requirements.txt /tmp/proxy-requirements.txt
COPY mysearch/requirements.txt /tmp/mysearch-requirements.txt
RUN pip install --no-cache-dir -r /tmp/proxy-requirements.txt -r /tmp/mysearch-requirements.txt

COPY proxy /app/proxy
COPY mysearch /app/mysearch
COPY docker /app/docker

RUN chmod +x /app/docker/combined-entrypoint.sh /app/mysearch/docker-entrypoint.sh

EXPOSE 9874 8000

CMD ["/app/docker/combined-entrypoint.sh"]
36 changes: 25 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,26 +197,40 @@ python3 skill/scripts/check_mysearch.py --health-only
python3 skill/scripts/check_mysearch.py --web-query "OpenAI latest announcements"
```

### 路线 B:先部署 Proxy,再让所有客户端复用
### 路线 B:最简单的单容器部署

```bash
mkdir -p mysearch-proxy-data

docker run -d \
--name mysearch-proxy \
--name mysearch-stack \
--restart unless-stopped \
-p 9874:9874 \
-p 8000:8000 \
-e ADMIN_PASSWORD=change-me \
-v $(pwd)/mysearch-proxy-data:/app/data \
skernelx/mysearch-proxy:latest
-e MYSEARCH_PROXY_BOOTSTRAP_TOKEN=change-me-bootstrap-token \
-v $(pwd)/mysearch-proxy-data:/data \
skernelx/mysearch-stack:latest
```

部署完成后:

- `proxy` 控制台:`http://localhost:9874`
- `mysearch` MCP:`http://localhost:8000/mcp`

单容器镜像里,`proxy` 默认对外监听 `9874`,`mysearch` 默认对外监听 `8000/mcp`;`mysearch` 自己仍然通过容器内 `127.0.0.1:9874` 访问 Proxy。

这条链路里不再需要手动先创建 `mysp-` token。容器启动时会通过受限 bootstrap 接口自动创建或复用一个 `mysearch` 代理 token,再交给同容器里的 `mysearch` 运行时使用。

### 路线 C:一套 compose 部署 `proxy + mysearch`

```bash
cd /path/to/MySearch-Proxy
docker compose up -d
```

部署后
部署完成后

1. 登录控制台
2. 添加 Tavily / Firecrawl / Exa / Social 上游配置
3. 创建 MySearch 通用 token
4. 把这个 token 填给 `mysearch/.env` 或 OpenClaw skill env
- `proxy` 控制台:`http://localhost:9874`
- `mysearch` MCP:`http://localhost:8000/mcp`

## 目录说明

Expand Down
2 changes: 1 addition & 1 deletion README_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ docker run -d \
--restart unless-stopped \
-p 9874:9874 \
-e ADMIN_PASSWORD=your-admin-password \
-v $(pwd)/mysearch-proxy-data:/app/data \
-v $(pwd)/mysearch-proxy-data:/data \
skernelx/mysearch-proxy:latest
```

Expand Down
48 changes: 48 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
services:
proxy:
build:
context: ./proxy
dockerfile: Dockerfile
ports:
- "${MYSEARCH_PROXY_PORT:-9874}:9874"
environment:
ADMIN_PASSWORD: ${ADMIN_PASSWORD:-change-me}
MYSEARCH_PROXY_DB_PATH: ${MYSEARCH_PROXY_DB_PATH:-/data/proxy.db}
MYSEARCH_PROXY_BOOTSTRAP_TOKEN: ${MYSEARCH_PROXY_BOOTSTRAP_TOKEN:-change-me-bootstrap-token}
STATS_CACHE_TTL_SECONDS: ${STATS_CACHE_TTL_SECONDS:-8}
DASHBOARD_AUTO_SYNC_ON_STATS: ${DASHBOARD_AUTO_SYNC_ON_STATS:-0}
DASHBOARD_BACKGROUND_SYNC_ON_STATS: ${DASHBOARD_BACKGROUND_SYNC_ON_STATS:-1}
DASHBOARD_BACKGROUND_SYNC_MIN_INTERVAL_SECONDS: ${DASHBOARD_BACKGROUND_SYNC_MIN_INTERVAL_SECONDS:-45}
volumes:
- mysearch-proxy-data:/data
restart: unless-stopped

mysearch:
build:
context: ./mysearch
dockerfile: Dockerfile
depends_on:
- proxy
ports:
- "${MYSEARCH_MCP_PORT:-8000}:8000"
environment:
MYSEARCH_NAME: ${MYSEARCH_NAME:-MySearch}
MYSEARCH_TIMEOUT_SECONDS: ${MYSEARCH_TIMEOUT_SECONDS:-45}
MYSEARCH_PROXY_BASE_URL: ${MYSEARCH_PROXY_BASE_URL:-http://proxy:9874}
MYSEARCH_PROXY_API_KEY: ${MYSEARCH_PROXY_API_KEY:-}
MYSEARCH_PROXY_BOOTSTRAP_TOKEN: ${MYSEARCH_PROXY_BOOTSTRAP_TOKEN:-change-me-bootstrap-token}
MYSEARCH_PROXY_BOOTSTRAP_NAME: ${MYSEARCH_PROXY_BOOTSTRAP_NAME:-docker-mysearch}
MYSEARCH_MCP_HOST: 0.0.0.0
MYSEARCH_MCP_PORT: 8000
MYSEARCH_MCP_STREAMABLE_HTTP_PATH: ${MYSEARCH_MCP_STREAMABLE_HTTP_PATH:-/mcp}
MYSEARCH_MCP_SSE_PATH: ${MYSEARCH_MCP_SSE_PATH:-/sse}
MYSEARCH_MCP_STATELESS_HTTP: ${MYSEARCH_MCP_STATELESS_HTTP:-false}
MYSEARCH_MAX_PARALLEL_WORKERS: ${MYSEARCH_MAX_PARALLEL_WORKERS:-4}
MYSEARCH_SEARCH_CACHE_TTL_SECONDS: ${MYSEARCH_SEARCH_CACHE_TTL_SECONDS:-30}
MYSEARCH_EXTRACT_CACHE_TTL_SECONDS: ${MYSEARCH_EXTRACT_CACHE_TTL_SECONDS:-300}
entrypoint: ["/app/mysearch/docker-entrypoint.sh"]
command: ["python", "-m", "mysearch", "--transport", "streamable-http", "--host", "0.0.0.0", "--port", "8000"]
restart: unless-stopped

volumes:
mysearch-proxy-data:
34 changes: 34 additions & 0 deletions docker/combined-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/env bash
set -euo pipefail

export PYTHONPATH="${PYTHONPATH:-/app}:/app/proxy"
export MYSEARCH_PROXY_BASE_URL="${MYSEARCH_PROXY_BASE_URL:-http://127.0.0.1:9874}"
export MYSEARCH_PROXY_HOST="${MYSEARCH_PROXY_HOST:-0.0.0.0}"

cleanup() {
local exit_code=$?
if [[ -n "${MCP_PID:-}" ]]; then
kill "${MCP_PID}" 2>/dev/null || true
fi
if [[ -n "${PROXY_PID:-}" ]]; then
kill "${PROXY_PID}" 2>/dev/null || true
fi
wait 2>/dev/null || true
exit "${exit_code}"
}

trap cleanup EXIT INT TERM

python -m uvicorn proxy.server:app --host "${MYSEARCH_PROXY_HOST}" --port 9874 &
PROXY_PID=$!

if [[ -z "${MYSEARCH_PROXY_API_KEY:-}" && -n "${MYSEARCH_PROXY_BOOTSTRAP_TOKEN:-}" ]]; then
export MYSEARCH_PROXY_API_KEY="$(
python /app/mysearch/scripts/bootstrap_proxy_token.py
)"
fi

python -m mysearch --transport streamable-http --host 0.0.0.0 --port "${MYSEARCH_MCP_PORT:-8000}" &
MCP_PID=$!

wait -n "${PROXY_PID}" "${MCP_PID}"
Loading