- 数据层:messages 表增加 part_type 字段,新建 file_versions 表支持版本追踪 - 后端:saveWorkspace 版本追踪、saveAgentOutput 源头分离、generateBriefMessage 成员简报 - 后端:applyDocumentEdit 增量编辑、buildWorkflowStep phase-aware 工作流引擎 - API:文件版本查询/回退接口 - 前端:part_type 驱动渲染,产物面板版本历史 - 新增写手团队(主编/搜索员/策划编辑/合规审查员)配置 - store 模块、scheduler 模块、web-search skill Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
572 lines
15 KiB
Bash
572 lines
15 KiB
Bash
#!/bin/bash
|
|
# Web Search CLI - Simplified search interface for Claude
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
|
DEFAULT_SERVER_URL="http://127.0.0.1:8923"
|
|
SERVER_URL="${WEB_SEARCH_SERVER:-$DEFAULT_SERVER_URL}"
|
|
ACTIVE_SERVER_URL="$SERVER_URL"
|
|
CONNECTION_CACHE="$PROJECT_DIR/.connection"
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
HTTP_NODE_CMD=""
|
|
HTTP_NODE_ARGS=()
|
|
HTTP_NODE_ENV_PREFIX=()
|
|
|
|
is_windows_bash() {
|
|
case "$(uname -s 2>/dev/null)" in
|
|
MINGW*|MSYS*|CYGWIN*) return 0 ;;
|
|
*) return 1 ;;
|
|
esac
|
|
}
|
|
|
|
# Usage information
|
|
usage() {
|
|
cat << EOF
|
|
Usage: $(basename "$0") <query|@query_file> [max_results]
|
|
|
|
Arguments:
|
|
query Search query (required), or @UTF-8-file-path for non-ASCII
|
|
max_results Maximum number of results (default: 10)
|
|
|
|
Examples:
|
|
$(basename "$0") "TypeScript tutorial" 5
|
|
$(basename "$0") "React hooks" 10
|
|
|
|
Environment:
|
|
WEB_SEARCH_SERVER Bridge Server URL (default: http://127.0.0.1:8923)
|
|
WEB_SEARCH_ENGINE Preferred engine: auto|google|bing (default: auto)
|
|
WEB_SEARCH_CLEANUP Set to 1 to close browser after each search (default: keep alive)
|
|
|
|
EOF
|
|
exit 1
|
|
}
|
|
|
|
resolve_http_node_runtime() {
|
|
if [ -n "$HTTP_NODE_CMD" ]; then
|
|
return 0
|
|
fi
|
|
|
|
if command -v node > /dev/null 2>&1; then
|
|
HTTP_NODE_CMD="node"
|
|
HTTP_NODE_ARGS=()
|
|
HTTP_NODE_ENV_PREFIX=()
|
|
return 0
|
|
fi
|
|
|
|
if [ -n "${LOBSTERAI_ELECTRON_PATH:-}" ] && [ -x "${LOBSTERAI_ELECTRON_PATH}" ]; then
|
|
HTTP_NODE_CMD="$LOBSTERAI_ELECTRON_PATH"
|
|
HTTP_NODE_ARGS=()
|
|
HTTP_NODE_ENV_PREFIX=("ELECTRON_RUN_AS_NODE=1")
|
|
return 0
|
|
fi
|
|
|
|
return 1
|
|
}
|
|
|
|
http_request() {
|
|
local METHOD="$1"
|
|
local URL="$2"
|
|
local BODY="${3:-}"
|
|
|
|
# On Windows Git Bash/MSYS/Cygwin, prefer Node fetch to avoid codepage-related
|
|
# corruption for non-ASCII payloads in curl/wget command-line arguments.
|
|
if ! is_windows_bash; then
|
|
if command -v curl > /dev/null 2>&1; then
|
|
if [ "$METHOD" = "GET" ]; then
|
|
if curl -s -f "$URL" 2>/dev/null; then
|
|
return 0
|
|
fi
|
|
else
|
|
if curl -s -f -X "$METHOD" "$URL" \
|
|
-H "Content-Type: application/json" \
|
|
-d "$BODY" 2>/dev/null; then
|
|
return 0
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if command -v wget > /dev/null 2>&1; then
|
|
if [ "$METHOD" = "GET" ]; then
|
|
if wget -q -O- "$URL" 2>/dev/null; then
|
|
return 0
|
|
fi
|
|
else
|
|
if wget -q -O- --method="$METHOD" \
|
|
--header="Content-Type: application/json" \
|
|
--body-data="$BODY" \
|
|
"$URL" 2>/dev/null; then
|
|
return 0
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if ! resolve_http_node_runtime; then
|
|
return 127
|
|
fi
|
|
|
|
env "${HTTP_NODE_ENV_PREFIX[@]}" "$HTTP_NODE_CMD" "${HTTP_NODE_ARGS[@]}" - "$METHOD" "$URL" "$BODY" <<'NODE'
|
|
const [method, url, body] = process.argv.slice(2);
|
|
|
|
(async () => {
|
|
try {
|
|
const init = { method };
|
|
if (method !== 'GET') {
|
|
init.headers = { 'Content-Type': 'application/json' };
|
|
init.body = body ?? '';
|
|
}
|
|
const response = await fetch(url, init);
|
|
if (!response.ok) {
|
|
process.exit(22);
|
|
}
|
|
process.stdout.write(await response.text());
|
|
} catch {
|
|
process.exit(1);
|
|
}
|
|
})();
|
|
NODE
|
|
}
|
|
|
|
http_get() {
|
|
http_request "GET" "$1"
|
|
}
|
|
|
|
http_post_json() {
|
|
local BODY="${2:-}"
|
|
if [ -z "$BODY" ]; then
|
|
BODY='{}'
|
|
fi
|
|
|
|
http_request "POST" "$1" "$BODY"
|
|
}
|
|
|
|
build_search_payload() {
|
|
local CONNECTION_ID="$1"
|
|
local QUERY="$2"
|
|
local MAX_RESULTS="$3"
|
|
local ENGINE="$4"
|
|
|
|
if resolve_http_node_runtime; then
|
|
env "${HTTP_NODE_ENV_PREFIX[@]}" "$HTTP_NODE_CMD" "${HTTP_NODE_ARGS[@]}" - "$CONNECTION_ID" "$QUERY" "$MAX_RESULTS" "$ENGINE" <<'NODE'
|
|
const [connectionId, query, maxResultsRaw, engineRaw] = process.argv.slice(2);
|
|
const maxResults = Number.parseInt(maxResultsRaw, 10);
|
|
const engine = engineRaw || 'auto';
|
|
|
|
process.stdout.write(JSON.stringify({
|
|
connectionId,
|
|
query,
|
|
maxResults: Number.isFinite(maxResults) ? maxResults : 10,
|
|
engine,
|
|
}));
|
|
NODE
|
|
return $?
|
|
fi
|
|
|
|
# Fallback when Node runtime is unavailable.
|
|
local ESCAPED_QUERY
|
|
ESCAPED_QUERY=$(printf '%s' "$QUERY" | sed 's/\\/\\\\/g; s/"/\\"/g')
|
|
printf '{"connectionId":"%s","query":"%s","maxResults":%s,"engine":"%s"}' \
|
|
"$CONNECTION_ID" "$ESCAPED_QUERY" "${MAX_RESULTS:-10}" "${ENGINE:-auto}"
|
|
}
|
|
|
|
ensure_http_client_available() {
|
|
if is_windows_bash; then
|
|
if resolve_http_node_runtime; then
|
|
return 0
|
|
fi
|
|
echo -e "${RED}✗ No supported HTTP client available for web-search on Windows${NC}" >&2
|
|
echo -e "${YELLOW} Node/Electron runtime is required in Windows shell mode.${NC}" >&2
|
|
return 1
|
|
fi
|
|
|
|
if command -v curl > /dev/null 2>&1; then
|
|
return 0
|
|
fi
|
|
|
|
if command -v wget > /dev/null 2>&1; then
|
|
return 0
|
|
fi
|
|
|
|
if resolve_http_node_runtime; then
|
|
return 0
|
|
fi
|
|
|
|
echo -e "${RED}✗ No HTTP client available for web-search${NC}" >&2
|
|
echo -e "${YELLOW} Install curl or wget, or ensure Node/Electron runtime is available.${NC}" >&2
|
|
return 1
|
|
}
|
|
|
|
is_server_healthy() {
|
|
local BASE_URL="${1%/}"
|
|
local HEALTH_RESPONSE
|
|
HEALTH_RESPONSE=$(http_get "$BASE_URL/api/health" || true)
|
|
echo "$HEALTH_RESPONSE" | grep -q '"success":true'
|
|
}
|
|
|
|
try_switch_to_local_server() {
|
|
if [ "$ACTIVE_SERVER_URL" = "$DEFAULT_SERVER_URL" ]; then
|
|
return 1
|
|
fi
|
|
|
|
if is_server_healthy "$DEFAULT_SERVER_URL"; then
|
|
echo -e "${YELLOW}Bridge Server at $ACTIVE_SERVER_URL is unavailable, falling back to ${DEFAULT_SERVER_URL}${NC}" >&2
|
|
ACTIVE_SERVER_URL="$DEFAULT_SERVER_URL"
|
|
return 0
|
|
fi
|
|
|
|
return 1
|
|
}
|
|
|
|
# Check if server is running
|
|
check_server() {
|
|
if is_server_healthy "$ACTIVE_SERVER_URL"; then
|
|
return 0
|
|
fi
|
|
|
|
if try_switch_to_local_server; then
|
|
return 0
|
|
fi
|
|
|
|
echo -e "${YELLOW}Bridge Server is not running, trying to start it...${NC}" >&2
|
|
if ! WEB_SEARCH_SERVER="$ACTIVE_SERVER_URL" bash "$SCRIPT_DIR/start-server.sh" > /dev/null 2>&1; then
|
|
if is_server_healthy "$ACTIVE_SERVER_URL" || try_switch_to_local_server; then
|
|
echo -e "${YELLOW}Bridge Server startup returned an error, but a healthy server is already available. Continuing...${NC}" >&2
|
|
return 0
|
|
fi
|
|
echo -e "${RED}✗ Failed to auto-start Bridge Server${NC}" >&2
|
|
echo -e "${YELLOW} Try manually: bash $SCRIPT_DIR/start-server.sh${NC}" >&2
|
|
if [ -f "$PROJECT_DIR/.server.log" ]; then
|
|
echo -e "${YELLOW} Recent logs:${NC}" >&2
|
|
tail -20 "$PROJECT_DIR/.server.log" >&2
|
|
fi
|
|
exit 1
|
|
fi
|
|
|
|
sleep 2
|
|
if is_server_healthy "$ACTIVE_SERVER_URL"; then
|
|
return 0
|
|
fi
|
|
|
|
if try_switch_to_local_server; then
|
|
return 0
|
|
fi
|
|
|
|
if ! is_server_healthy "$ACTIVE_SERVER_URL"; then
|
|
echo -e "${RED}✗ Bridge Server still unavailable after start${NC}" >&2
|
|
echo -e "${YELLOW} Endpoint checked: $ACTIVE_SERVER_URL/api/health${NC}" >&2
|
|
if [ -f "$PROJECT_DIR/.server.log" ]; then
|
|
echo -e "${YELLOW} Recent logs:${NC}" >&2
|
|
tail -20 "$PROJECT_DIR/.server.log" >&2
|
|
fi
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
is_iconv_runtime_error() {
|
|
local RESPONSE="$1"
|
|
if echo "$RESPONSE" | grep -q "Cannot find module" && echo "$RESPONSE" | grep -q "encodings"; then
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
is_connection_runtime_error() {
|
|
local RESPONSE="$1"
|
|
if echo "$RESPONSE" | grep -q "Connection not found"; then
|
|
return 0
|
|
fi
|
|
if echo "$RESPONSE" | grep -q "Connection not active"; then
|
|
return 0
|
|
fi
|
|
if echo "$RESPONSE" | grep -q "Connection became invalid"; then
|
|
return 0
|
|
fi
|
|
if echo "$RESPONSE" | grep -q "browserContext.newPage"; then
|
|
return 0
|
|
fi
|
|
if echo "$RESPONSE" | grep -q "Target page, context or browser has been closed"; then
|
|
return 0
|
|
fi
|
|
if echo "$RESPONSE" | grep -q "Failed to connect to CDP"; then
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
repair_server_runtime() {
|
|
echo -e "${YELLOW}Detected broken web-search runtime, trying automatic repair...${NC}" >&2
|
|
rm -f "$CONNECTION_CACHE"
|
|
bash "$SCRIPT_DIR/stop-server.sh" > /dev/null 2>&1 || true
|
|
|
|
if ! WEB_SEARCH_FORCE_REPAIR=1 bash "$SCRIPT_DIR/start-server.sh" > /dev/null 2>&1; then
|
|
echo -e "${RED}✗ Failed to repair web-search runtime${NC}" >&2
|
|
if [ -f "$PROJECT_DIR/.server.log" ]; then
|
|
echo -e "${YELLOW} Recent logs:${NC}" >&2
|
|
tail -20 "$PROJECT_DIR/.server.log" >&2
|
|
fi
|
|
return 1
|
|
fi
|
|
|
|
sleep 2
|
|
return 0
|
|
}
|
|
|
|
is_cached_connection_valid() {
|
|
local CONNECTION_ID="$1"
|
|
local VALIDATE_RESPONSE
|
|
VALIDATE_RESPONSE=$(http_post_json "$ACTIVE_SERVER_URL/api/page/text" "{\"connectionId\":\"$CONNECTION_ID\"}" || true)
|
|
|
|
if echo "$VALIDATE_RESPONSE" | grep -q '"success":true'; then
|
|
return 0
|
|
fi
|
|
|
|
if is_connection_runtime_error "$VALIDATE_RESPONSE"; then
|
|
return 1
|
|
fi
|
|
|
|
# Unknown validation failure should not block new connection creation.
|
|
return 1
|
|
}
|
|
|
|
# Get or create browser connection
|
|
get_connection() {
|
|
local ATTEMPT="${1:-1}"
|
|
local CONNECTION_ID=""
|
|
|
|
# Try to use cached connection
|
|
if [ -f "$CONNECTION_CACHE" ]; then
|
|
CONNECTION_ID=$(cat "$CONNECTION_CACHE")
|
|
|
|
# Verify cached connection is actually usable.
|
|
if [ -n "$CONNECTION_ID" ] && is_cached_connection_valid "$CONNECTION_ID"; then
|
|
echo "$CONNECTION_ID"
|
|
return 0
|
|
fi
|
|
|
|
# Stale connection, remove cache
|
|
rm -f "$CONNECTION_CACHE"
|
|
fi
|
|
|
|
# Launch browser if not running
|
|
local LAUNCH_RESPONSE
|
|
LAUNCH_RESPONSE=$(http_post_json "$ACTIVE_SERVER_URL/api/browser/launch" "{}" || true)
|
|
|
|
if ! echo "$LAUNCH_RESPONSE" | grep -q '"success":true'; then
|
|
if [ "$ATTEMPT" -eq 1 ] && is_iconv_runtime_error "$LAUNCH_RESPONSE"; then
|
|
if repair_server_runtime; then
|
|
get_connection 2
|
|
return $?
|
|
fi
|
|
fi
|
|
if [ "$ATTEMPT" -eq 1 ] && is_connection_runtime_error "$LAUNCH_RESPONSE"; then
|
|
rm -f "$CONNECTION_CACHE"
|
|
bash "$SCRIPT_DIR/stop-server.sh" > /dev/null 2>&1 || true
|
|
if WEB_SEARCH_FORCE_REPAIR=1 bash "$SCRIPT_DIR/start-server.sh" > /dev/null 2>&1; then
|
|
get_connection 2
|
|
return $?
|
|
fi
|
|
fi
|
|
echo -e "${RED}✗ Failed to launch browser${NC}" >&2
|
|
echo "$LAUNCH_RESPONSE" >&2
|
|
return 1
|
|
fi
|
|
|
|
# Connect to browser
|
|
local CONNECT_RESPONSE
|
|
CONNECT_RESPONSE=$(http_post_json "$ACTIVE_SERVER_URL/api/browser/connect" "{}" || true)
|
|
|
|
if ! echo "$CONNECT_RESPONSE" | grep -q '"success":true'; then
|
|
if [ "$ATTEMPT" -eq 1 ] && is_iconv_runtime_error "$CONNECT_RESPONSE"; then
|
|
if repair_server_runtime; then
|
|
get_connection 2
|
|
return $?
|
|
fi
|
|
fi
|
|
if [ "$ATTEMPT" -eq 1 ] && is_connection_runtime_error "$CONNECT_RESPONSE"; then
|
|
rm -f "$CONNECTION_CACHE"
|
|
bash "$SCRIPT_DIR/stop-server.sh" > /dev/null 2>&1 || true
|
|
if WEB_SEARCH_FORCE_REPAIR=1 bash "$SCRIPT_DIR/start-server.sh" > /dev/null 2>&1; then
|
|
get_connection 2
|
|
return $?
|
|
fi
|
|
fi
|
|
echo -e "${RED}✗ Failed to connect to browser${NC}" >&2
|
|
echo "$CONNECT_RESPONSE" >&2
|
|
return 1
|
|
fi
|
|
|
|
# Extract connection ID
|
|
CONNECTION_ID=$(echo "$CONNECT_RESPONSE" | grep -o '"connectionId":"[^"]*"' | cut -d'"' -f4)
|
|
|
|
if [ -z "$CONNECTION_ID" ]; then
|
|
echo -e "${RED}✗ Failed to get connection ID${NC}" >&2
|
|
return 1
|
|
fi
|
|
|
|
# Cache connection ID
|
|
echo "$CONNECTION_ID" > "$CONNECTION_CACHE"
|
|
echo "$CONNECTION_ID"
|
|
}
|
|
|
|
# Perform search
|
|
search() {
|
|
local QUERY="$1"
|
|
local MAX_RESULTS="${2:-10}"
|
|
local CONNECTION_ID="$3"
|
|
local ATTEMPT="${4:-1}"
|
|
local ENGINE="${WEB_SEARCH_ENGINE:-auto}"
|
|
|
|
echo -e "${BLUE}🔍 Searching for: \"$QUERY\"${NC}" >&2
|
|
echo "" >&2
|
|
|
|
# Perform search via API
|
|
local SEARCH_RESPONSE
|
|
local SEARCH_PAYLOAD
|
|
if ! SEARCH_PAYLOAD="$(build_search_payload "$CONNECTION_ID" "$QUERY" "$MAX_RESULTS" "$ENGINE")"; then
|
|
echo -e "${RED}✗ Failed to build search payload${NC}" >&2
|
|
return 1
|
|
fi
|
|
|
|
SEARCH_RESPONSE=$(http_post_json "$ACTIVE_SERVER_URL/api/search" "$SEARCH_PAYLOAD" || true)
|
|
|
|
if ! echo "$SEARCH_RESPONSE" | grep -q '"success":true'; then
|
|
if [ "$ATTEMPT" -eq 1 ] && is_iconv_runtime_error "$SEARCH_RESPONSE"; then
|
|
if repair_server_runtime; then
|
|
if CONNECTION_ID="$(get_connection 2)"; then
|
|
search "$QUERY" "$MAX_RESULTS" "$CONNECTION_ID" 2
|
|
return $?
|
|
fi
|
|
return 1
|
|
fi
|
|
fi
|
|
if [ "$ATTEMPT" -eq 1 ] && is_connection_runtime_error "$SEARCH_RESPONSE"; then
|
|
rm -f "$CONNECTION_CACHE"
|
|
if CONNECTION_ID="$(get_connection 2)"; then
|
|
search "$QUERY" "$MAX_RESULTS" "$CONNECTION_ID" 2
|
|
return $?
|
|
fi
|
|
fi
|
|
echo -e "${RED}✗ Search failed${NC}" >&2
|
|
echo "$SEARCH_RESPONSE" >&2
|
|
return 1
|
|
fi
|
|
|
|
# Parse and display results
|
|
local DURATION=$(echo "$SEARCH_RESPONSE" | grep -o '"duration":[0-9]*' | cut -d':' -f2)
|
|
local TOTAL=$(echo "$SEARCH_RESPONSE" | grep -o '"totalResults":[0-9]*' | cut -d':' -f2)
|
|
local ENGINE_USED=$(echo "$SEARCH_RESPONSE" | grep -o '"engine":"[^"]*"' | cut -d'"' -f4)
|
|
|
|
if [ -z "$ENGINE_USED" ]; then
|
|
ENGINE_USED="unknown"
|
|
fi
|
|
|
|
echo -e "${GREEN}✓ Found $TOTAL results in ${DURATION}ms (engine: ${ENGINE_USED})${NC}" >&2
|
|
echo "" >&2
|
|
|
|
# Format results as Markdown
|
|
echo "# Search Results: $QUERY"
|
|
echo ""
|
|
echo "**Query:** $QUERY "
|
|
echo "**Engine:** $ENGINE_USED "
|
|
echo "**Results:** $TOTAL "
|
|
echo "**Time:** ${DURATION}ms "
|
|
echo ""
|
|
echo "---"
|
|
echo ""
|
|
|
|
# Extract and format each result
|
|
# Note: This is a simplified parser. For production, use jq or node.js
|
|
echo "$SEARCH_RESPONSE" | grep -o '"title":"[^"]*","url":"[^"]*","snippet":"[^"]*"' | while IFS= read -r result; do
|
|
local TITLE=$(echo "$result" | sed -n 's/.*"title":"\([^"]*\)".*/\1/p')
|
|
local URL=$(echo "$result" | sed -n 's/.*"url":"\([^"]*\)".*/\1/p')
|
|
local SNIPPET=$(echo "$result" | sed -n 's/.*"snippet":"\([^"]*\)".*/\1/p')
|
|
|
|
echo "## $TITLE"
|
|
echo ""
|
|
echo "**URL:** [$URL]($URL)"
|
|
echo ""
|
|
echo "$SNIPPET"
|
|
echo ""
|
|
echo "---"
|
|
echo ""
|
|
done
|
|
}
|
|
|
|
# Close browser after search completes
|
|
cleanup_browser() {
|
|
local CONNECTION_ID="$1"
|
|
|
|
# Disconnect the Playwright connection
|
|
if [ -n "$CONNECTION_ID" ]; then
|
|
http_post_json "$ACTIVE_SERVER_URL/api/browser/disconnect" "{\"connectionId\":\"$CONNECTION_ID\"}" > /dev/null 2>&1 || true
|
|
fi
|
|
|
|
# Close the browser process (only kills the browser spawned by web-search, not user's browser)
|
|
http_post_json "$ACTIVE_SERVER_URL/api/browser/close" "{}" > /dev/null 2>&1 || true
|
|
|
|
# Clear connection cache
|
|
rm -f "$CONNECTION_CACHE"
|
|
}
|
|
|
|
# Main execution
|
|
main() {
|
|
# Parse arguments
|
|
if [ $# -lt 1 ]; then
|
|
usage
|
|
fi
|
|
|
|
local QUERY_ARG="$1"
|
|
local QUERY="$QUERY_ARG"
|
|
local MAX_RESULTS="${2:-10}"
|
|
|
|
# Support @file syntax to avoid command-line encoding issues for non-ASCII query.
|
|
if [ "${QUERY_ARG#@}" != "$QUERY_ARG" ]; then
|
|
local QUERY_FILE="${QUERY_ARG#@}"
|
|
if [ ! -f "$QUERY_FILE" ]; then
|
|
echo -e "${RED}✗ Query file not found: $QUERY_FILE${NC}" >&2
|
|
exit 1
|
|
fi
|
|
QUERY="$(cat "$QUERY_FILE")"
|
|
fi
|
|
|
|
if ! ensure_http_client_available; then
|
|
exit 1
|
|
fi
|
|
|
|
# Check server
|
|
check_server
|
|
|
|
# Get connection
|
|
local CONNECTION_ID=""
|
|
if ! CONNECTION_ID="$(get_connection)"; then
|
|
exit 1
|
|
fi
|
|
|
|
# Perform search
|
|
local SEARCH_EXIT_CODE=0
|
|
if ! search "$QUERY" "$MAX_RESULTS" "$CONNECTION_ID" 1; then
|
|
SEARCH_EXIT_CODE=1
|
|
fi
|
|
|
|
# By default, keep the browser alive so subsequent searches can reuse the
|
|
# existing Chrome process and Playwright connection (avoids re-launching
|
|
# Chrome which steals window focus). Set WEB_SEARCH_CLEANUP=1 to force
|
|
# cleanup after each search.
|
|
# Legacy: WEB_SEARCH_NO_CLEANUP=1 is now a no-op (kept for compatibility).
|
|
if [ "${WEB_SEARCH_CLEANUP:-}" = "1" ]; then
|
|
cleanup_browser "$CONNECTION_ID"
|
|
fi
|
|
|
|
exit $SEARCH_EXIT_CODE
|
|
}
|
|
|
|
# Run main function
|
|
main "$@"
|