browser-web-search 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -64,14 +64,15 @@ function buildOpenClawArgs(args, timeout) {
64
64
  if (!subcommand) {
65
65
  throw new Error("OpenClaw browser command requires a subcommand");
66
66
  }
67
- const globalFlags = ["--timeout", String(timeout)];
67
+ const globalFlags = [`--timeout=${timeout}`];
68
68
  const subcommandArgs = [];
69
69
  for (let i = 0; i < rest.length; i++) {
70
70
  const arg = rest[i];
71
71
  if (GLOBAL_FLAGS.has(arg)) {
72
- globalFlags.push(arg);
73
72
  if (arg !== "--json" && i + 1 < rest.length) {
74
- globalFlags.push(rest[++i]);
73
+ globalFlags.push(`${arg}=${rest[++i]}`);
74
+ } else {
75
+ globalFlags.push(arg);
75
76
  }
76
77
  } else {
77
78
  subcommandArgs.push(arg);
@@ -110,7 +111,7 @@ function ocOpenTab(url) {
110
111
  return data.id || data.targetId || "";
111
112
  }
112
113
  function ocEvaluate(targetId, fn) {
113
- const raw = runOpenClaw(["evaluate", "--fn", fn, "--target-id", targetId], OPENCLAW_EVALUATE_TIMEOUT_MS);
114
+ const raw = runOpenClaw(["evaluate", `--fn=${fn}`, `--target-id=${targetId}`], OPENCLAW_EVALUATE_TIMEOUT_MS);
114
115
  return parseOpenClawJson(raw);
115
116
  }
116
117
 
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/commands/site.ts","../src/openclaw-bridge.ts","../src/openclaw-json.ts","../src/jq.ts","../src/index.ts"],"sourcesContent":["/**\n * site 命令 - 管理和运行网站适配器\n *\n * 用法:\n * bws site list 列出所有可用 adapter\n * bws site search <query> 搜索 adapter\n * bws site info <name> 查看 adapter 详情\n * bws site <name> [args...] 运行 adapter\n *\n * 目录:\n * 内置 adapter:随项目发布\n * ~/.bws/sites/:用户私有 adapter(优先级更高)\n */\n\nimport { readFileSync, readdirSync, existsSync } from \"node:fs\";\nimport { join, relative } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { ocGetTabs, ocFindTabByDomain, ocOpenTab, ocEvaluate } from \"../openclaw-bridge.js\";\nimport { applyJq } from \"../jq.js\";\n\nconst BWS_DIR = join(homedir(), \".bws\");\nconst LOCAL_SITES_DIR = join(BWS_DIR, \"sites\"); // 用户私有 adapter(优先级最高)\n\n// 内置 adapter 目录(项目自带)\nimport { fileURLToPath } from \"node:url\";\nimport { dirname } from \"node:path\";\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\nconst BUILTIN_SITES_DIR = join(__dirname, \"../sites\"); // 项目内置 adapter\n\nexport interface SiteOptions {\n json?: boolean;\n jq?: string;\n}\n\n/** Adapter 参数定义 */\ninterface ArgDef {\n required?: boolean;\n description?: string;\n}\n\n/** Adapter 元数据 */\ninterface SiteMeta {\n name: string;\n description: string;\n domain: string;\n args: Record<string, ArgDef>;\n capabilities?: string[];\n readOnly?: boolean;\n example?: string;\n filePath: string;\n source: \"local\" | \"builtin\";\n}\n\nfunction exitJsonError(error: string, extra: Record<string, unknown> = {}): never {\n console.log(JSON.stringify({ success: false, error, ...extra }, null, 2));\n process.exit(1);\n}\n\n/**\n * 从 JS 文件的 /* @meta JSON * / 块解析元数据\n */\nfunction parseSiteMeta(filePath: string, source: \"local\" | \"builtin\"): SiteMeta | null {\n let content: string;\n try {\n content = readFileSync(filePath, \"utf-8\");\n } catch {\n return null;\n }\n\n // 从文件路径推断默认 name\n const sitesDir = source === \"local\" ? LOCAL_SITES_DIR : BUILTIN_SITES_DIR;\n const relPath = relative(sitesDir, filePath);\n const defaultName = relPath.replace(/\\.js$/, \"\").replace(/\\\\/g, \"/\");\n\n // 解析 /* @meta { ... } */ 块\n const metaMatch = content.match(/\\/\\*\\s*@meta\\s*\\n([\\s\\S]*?)\\*\\//);\n if (metaMatch) {\n try {\n const metaJson = JSON.parse(metaMatch[1]);\n return {\n name: metaJson.name || defaultName,\n description: metaJson.description || \"\",\n domain: metaJson.domain || \"\",\n args: metaJson.args || {},\n capabilities: metaJson.capabilities,\n readOnly: metaJson.readOnly,\n example: metaJson.example,\n filePath,\n source,\n };\n } catch {\n // JSON 解析失败,回退到 @tag 模式\n }\n }\n\n // 回退:解析 // @tag 格式(兼容旧格式)\n const meta: SiteMeta = {\n name: defaultName,\n description: \"\",\n domain: \"\",\n args: {},\n filePath,\n source,\n };\n\n const tagPattern = /\\/\\/\\s*@(\\w+)[ \\t]+(.*)/g;\n let match;\n while ((match = tagPattern.exec(content)) !== null) {\n const [, key, value] = match;\n switch (key) {\n case \"name\": meta.name = value.trim(); break;\n case \"description\": meta.description = value.trim(); break;\n case \"domain\": meta.domain = value.trim(); break;\n case \"args\":\n for (const arg of value.trim().split(/[,\\s]+/).filter(Boolean)) {\n meta.args[arg] = { required: true };\n }\n break;\n case \"example\": meta.example = value.trim(); break;\n }\n }\n\n return meta;\n}\n\n/**\n * 扫描目录下所有 .js 文件\n */\nfunction scanSites(dir: string, source: \"local\" | \"builtin\"): SiteMeta[] {\n if (!existsSync(dir)) return [];\n const sites: SiteMeta[] = [];\n\n function walk(currentDir: string): void {\n let entries;\n try { entries = readdirSync(currentDir, { withFileTypes: true }); } catch { return; }\n for (const entry of entries) {\n const fullPath = join(currentDir, entry.name);\n if (entry.isDirectory() && !entry.name.startsWith(\".\")) {\n walk(fullPath);\n } else if (entry.isFile() && entry.name.endsWith(\".js\")) {\n const meta = parseSiteMeta(fullPath, source);\n if (meta) sites.push(meta);\n }\n }\n }\n\n walk(dir);\n return sites;\n}\n\n/**\n * 获取所有 adapter(用户私有 > 内置)\n */\nfunction getAllSites(): SiteMeta[] {\n const builtin = scanSites(BUILTIN_SITES_DIR, \"builtin\");\n const local = scanSites(LOCAL_SITES_DIR, \"local\");\n\n const byName = new Map<string, SiteMeta>();\n for (const s of builtin) byName.set(s.name, s);\n for (const s of local) byName.set(s.name, s); // 用户私有覆盖内置\n\n return Array.from(byName.values()).sort((a, b) => a.name.localeCompare(b.name));\n}\n\n// ── 子命令 ──────────────────────────────────────────────────────\n\nfunction siteList(options: SiteOptions): void {\n const sites = getAllSites();\n\n if (sites.length === 0) {\n if (options.json) {\n console.log(\"[]\");\n return;\n }\n console.log(\"未找到任何 site adapter。\");\n console.log(` 内置 adapter 目录: ${BUILTIN_SITES_DIR}`);\n console.log(` 私有 adapter 目录: ${LOCAL_SITES_DIR}`);\n return;\n }\n\n if (options.json) {\n console.log(JSON.stringify(sites.map(s => ({\n name: s.name, description: s.description, domain: s.domain,\n args: s.args, source: s.source,\n })), null, 2));\n return;\n }\n\n const groups = new Map<string, SiteMeta[]>();\n for (const s of sites) {\n const platform = s.name.split(\"/\")[0];\n if (!groups.has(platform)) groups.set(platform, []);\n groups.get(platform)!.push(s);\n }\n\n for (const [platform, items] of groups) {\n console.log(`\\n${platform}/`);\n for (const s of items) {\n const cmd = s.name.split(\"/\").slice(1).join(\"/\");\n const src = s.source === \"local\" ? \" (local)\" : \"\";\n const desc = s.description ? ` - ${s.description}` : \"\";\n console.log(` ${cmd.padEnd(20)}${desc}${src}`);\n }\n }\n console.log();\n}\n\nfunction siteSearch(query: string, options: SiteOptions): void {\n const sites = getAllSites();\n const q = query.toLowerCase();\n const matches = sites.filter(s =>\n s.name.toLowerCase().includes(q) ||\n s.description.toLowerCase().includes(q) ||\n s.domain.toLowerCase().includes(q)\n );\n\n if (matches.length === 0) {\n if (options.json) {\n console.log(\"[]\");\n return;\n }\n console.log(`未找到匹配 \"${query}\" 的 adapter。`);\n console.log(\" 查看所有: bws site list\");\n return;\n }\n\n if (options.json) {\n console.log(JSON.stringify(matches.map(s => ({\n name: s.name, description: s.description, domain: s.domain, source: s.source,\n })), null, 2));\n return;\n }\n\n for (const s of matches) {\n const src = s.source === \"local\" ? \" (local)\" : \"\";\n console.log(`${s.name.padEnd(24)} ${s.description}${src}`);\n }\n}\n\nfunction siteInfo_showDirs(options: SiteOptions): void {\n const builtin = scanSites(BUILTIN_SITES_DIR, \"builtin\");\n const local = scanSites(LOCAL_SITES_DIR, \"local\");\n \n if (options.json) {\n console.log(JSON.stringify({\n builtinDir: BUILTIN_SITES_DIR,\n localDir: LOCAL_SITES_DIR,\n builtinCount: builtin.length,\n localCount: local.length,\n }, null, 2));\n return;\n }\n \n console.log(`内置 adapter: ${BUILTIN_SITES_DIR} (${builtin.length} 个)`);\n console.log(`私有 adapter: ${LOCAL_SITES_DIR} (${local.length} 个)`);\n}\n\nfunction findSiteByName(name: string): SiteMeta | undefined {\n return getAllSites().find((site) => site.name === name);\n}\n\nfunction siteInfo(name: string, options: SiteOptions): void {\n const site = findSiteByName(name);\n\n if (!site) {\n if (options.json) {\n exitJsonError(`adapter \"${name}\" not found`, { action: \"bws site list\" });\n }\n console.error(`[error] site info: adapter \"${name}\" not found.`);\n console.error(\" Try: bws site list\");\n process.exit(1);\n }\n\n const meta = {\n name: site.name,\n description: site.description,\n domain: site.domain,\n args: site.args,\n example: site.example,\n readOnly: site.readOnly,\n };\n\n if (options.json) {\n console.log(JSON.stringify(meta, null, 2));\n return;\n }\n\n console.log(`${site.name} — ${site.description}`);\n console.log();\n console.log(\"参数:\");\n\n const argEntries = Object.entries(site.args);\n if (argEntries.length === 0) {\n console.log(\" (无)\");\n } else {\n for (const [argName, argDef] of argEntries) {\n const requiredText = argDef.required ? \"必填\" : \"可选\";\n const description = argDef.description || \"\";\n console.log(` ${argName} (${requiredText}) ${description}`.trimEnd());\n }\n }\n\n console.log();\n console.log(\"示例:\");\n console.log(` ${site.example || `bws site ${site.name}`}`);\n console.log();\n console.log(`域名:${site.domain || \"(未声明)\"}`);\n console.log(`只读:${site.readOnly ? \"是\" : \"否\"}`);\n}\n\nasync function siteRun(\n name: string,\n args: string[],\n options: SiteOptions\n): Promise<void> {\n const sites = getAllSites();\n const site = sites.find(s => s.name === name);\n\n if (!site) {\n const fuzzy = sites.filter(s => s.name.includes(name));\n if (options.json) {\n exitJsonError(`site \"${name}\" not found`, {\n suggestions: fuzzy.slice(0, 5).map(s => s.name),\n action: fuzzy.length > 0 ? undefined : \"bws site update\",\n });\n }\n console.error(`[error] site: \"${name}\" not found.`);\n if (fuzzy.length > 0) {\n console.error(\" Did you mean:\");\n for (const s of fuzzy.slice(0, 5)) {\n console.error(` bws site ${s.name}`);\n }\n } else {\n console.error(\" Try: bws site list\");\n console.error(\" Or: bws site update\");\n }\n process.exit(1);\n }\n\n // 解析参数\n const argNames = Object.keys(site.args);\n const argMap: Record<string, string> = {};\n\n // 过滤掉 --flag value 对,收集位置参数\n const positionalArgs: string[] = [];\n for (let i = 0; i < args.length; i++) {\n if (args[i].startsWith(\"--\")) {\n const flagName = args[i].slice(2);\n if (flagName in site.args && args[i + 1]) {\n argMap[flagName] = args[i + 1];\n i++; // 跳过值\n }\n } else {\n positionalArgs.push(args[i]);\n }\n }\n\n // 位置参数按 argNames 顺序填入(跳过已通过 --flag 提供的)\n let posIdx = 0;\n for (const argName of argNames) {\n if (!argMap[argName] && posIdx < positionalArgs.length) {\n argMap[argName] = positionalArgs[posIdx++];\n }\n }\n\n // 只检查 required 参数\n for (const [argName, argDef] of Object.entries(site.args)) {\n if (argDef.required && !argMap[argName]) {\n const usage = argNames.map(a => {\n const def = site.args[a];\n return def.required ? `<${a}>` : `[${a}]`;\n }).join(\" \");\n if (options.json) {\n exitJsonError(`missing required argument \"${argName}\"`, {\n usage: `bws site ${name} ${usage}`,\n example: site.example,\n });\n }\n console.error(`[error] site ${name}: missing required argument \"${argName}\".`);\n console.error(` Usage: bws site ${name} ${usage}`);\n if (site.example) console.error(` Example: ${site.example}`);\n process.exit(1);\n }\n }\n\n // 读取并解析 JS\n const jsContent = readFileSync(site.filePath, \"utf-8\");\n\n // 移除 /* @meta ... */ 块,保留函数体\n const jsBody = jsContent.replace(/\\/\\*\\s*@meta[\\s\\S]*?\\*\\//, \"\").trim();\n\n // 构造执行脚本\n const argsJson = JSON.stringify(argMap);\n\n // OpenClaw 模式执行\n let targetId: string;\n\n if (site.domain) {\n const tabs = ocGetTabs();\n const existing = ocFindTabByDomain(tabs, site.domain);\n if (existing) {\n targetId = existing.targetId;\n } else {\n targetId = ocOpenTab(`https://${site.domain}`);\n await new Promise((resolve) => setTimeout(resolve, 3000));\n }\n } else {\n const tabs = ocGetTabs();\n if (tabs.length === 0) {\n throw new Error(\"No tabs open in OpenClaw browser\");\n }\n targetId = tabs[0].targetId;\n }\n\n const wrappedFn = `async () => { const __fn = ${jsBody}; return await __fn(${argsJson}); }`;\n const parsed = ocEvaluate(targetId, wrappedFn);\n\n if (typeof parsed === \"object\" && parsed !== null && \"error\" in parsed) {\n const errObj = parsed as { error: string; hint?: string };\n const checkText = `${errObj.error} ${errObj.hint || \"\"}`;\n const isAuthError = /401|403|unauthorized|forbidden|not.?logged|login.?required|sign.?in|auth/i.test(checkText);\n const loginHint = isAuthError && site.domain\n ? `Please log in to https://${site.domain} in your OpenClaw browser first, then retry.`\n : undefined;\n const hint = loginHint || errObj.hint;\n\n if (options.json) {\n console.log(JSON.stringify({ success: false, error: errObj.error, hint }));\n } else {\n console.error(`[error] site ${name}: ${errObj.error}`);\n if (hint) console.error(` Hint: ${hint}`);\n }\n process.exit(1);\n }\n\n if (options.jq) {\n const expr = options.jq.replace(/^\\.data\\./, '.');\n const results = applyJq(parsed, expr);\n for (const r of results) {\n console.log(typeof r === \"string\" ? r : JSON.stringify(r));\n }\n } else if (options.json) {\n console.log(JSON.stringify({ success: true, data: parsed }));\n } else {\n console.log(JSON.stringify(parsed, null, 2));\n }\n}\n\n// ── 入口 ────────────────────────────────────────────────────────\n\nexport async function siteCommand(\n args: string[],\n options: SiteOptions = {}\n): Promise<void> {\n const subCommand = args[0];\n\n if (!subCommand || subCommand === \"--help\" || subCommand === \"-h\") {\n console.log(`bws site - 网站 CLI 化\n\n用法:\n bws site list 列出所有可用 adapter\n bws site info <name> 查看 adapter 详情\n bws site search <query> 搜索 adapter\n bws site <name> [args...] 运行 adapter\n\n示例:\n bws site list\n bws site zhihu/hot\n bws site xiaohongshu/search \"旅行\"\n bws site bilibili/popular`);\n return;\n }\n\n switch (subCommand) {\n case \"list\": siteList(options); break;\n case \"search\":\n if (!args[1]) {\n console.error(\"[error] site search: <query> is required.\");\n console.error(\" Usage: bws site search <query>\");\n process.exit(1);\n }\n siteSearch(args[1], options);\n break;\n case \"info\":\n if (!args[1]) {\n console.error(\"[error] site info: <name> is required.\");\n console.error(\" Usage: bws site info <name>\");\n process.exit(1);\n }\n siteInfo(args[1], options);\n break;\n case \"run\":\n if (!args[1]) {\n console.error(\"[error] site run: <name> is required.\");\n console.error(\" Usage: bws site run <name> [args...]\");\n console.error(\" Try: bws site list\");\n process.exit(1);\n }\n await siteRun(args[1], args.slice(2), options);\n break;\n default:\n if (subCommand.includes(\"/\")) {\n await siteRun(subCommand, args.slice(1), options);\n } else {\n console.error(`[error] site: unknown subcommand \"${subCommand}\".`);\n console.error(\" Available: list, info, search, run\");\n console.error(\" Try: bws site --help\");\n process.exit(1);\n }\n break;\n }\n\n}\n","import { execFileSync } from \"node:child_process\";\nimport { parseOpenClawJson } from \"./openclaw-json.js\";\n\nconst OPENCLAW_EVALUATE_TIMEOUT_MS = 120000;\nconst EXEC_TIMEOUT_BUFFER_MS = 5000;\n\nexport interface OCTab {\n targetId: string;\n url: string;\n title: string;\n type: string;\n}\n\n// Global flags that should be placed after \"browser\" but before subcommand\nconst GLOBAL_FLAGS = new Set([\"--json\", \"--timeout\"]);\n\nexport function buildOpenClawArgs(args: string[], timeout: number): string[] {\n const [subcommand, ...rest] = args;\n if (!subcommand) {\n throw new Error(\"OpenClaw browser command requires a subcommand\");\n }\n\n // Separate global flags from subcommand-specific args\n const globalFlags: string[] = [\"--timeout\", String(timeout)];\n const subcommandArgs: string[] = [];\n\n for (let i = 0; i < rest.length; i++) {\n const arg = rest[i];\n if (GLOBAL_FLAGS.has(arg)) {\n globalFlags.push(arg);\n // If this flag takes a value (not --json), include the next arg too\n if (arg !== \"--json\" && i + 1 < rest.length) {\n globalFlags.push(rest[++i]);\n }\n } else {\n subcommandArgs.push(arg);\n }\n }\n\n return [\"openclaw\", \"browser\", ...globalFlags, subcommand, ...subcommandArgs];\n}\n\nexport function getOpenClawExecTimeout(timeout: number): number {\n return timeout + EXEC_TIMEOUT_BUFFER_MS;\n}\n\nfunction runOpenClaw(args: string[], timeout: number): string {\n return execFileSync(\"npx\", buildOpenClawArgs(args, timeout), {\n encoding: \"utf-8\",\n timeout: getOpenClawExecTimeout(timeout),\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n }).trim();\n}\n\nexport function ocGetTabs(): OCTab[] {\n const raw = runOpenClaw([\"tabs\", \"--json\"], 15000);\n const data = parseOpenClawJson<{ tabs?: OCTab[] }>(raw);\n return (data.tabs || []).filter((tab: OCTab) => tab.type === \"page\");\n}\n\nexport function ocFindTabByDomain(tabs: OCTab[], domain: string): OCTab | undefined {\n return tabs.find((tab) => {\n try {\n const hostname = new URL(tab.url).hostname;\n return hostname === domain || hostname.endsWith(`.${domain}`);\n } catch {\n return false;\n }\n });\n}\n\nexport function ocOpenTab(url: string): string {\n const raw = runOpenClaw([\"open\", url, \"--json\"], 30000);\n const data = parseOpenClawJson<{ id?: string; targetId?: string }>(raw);\n return data.id || data.targetId || \"\";\n}\n\nexport function ocEvaluate(targetId: string, fn: string): unknown {\n const raw = runOpenClaw([\"evaluate\", \"--fn\", fn, \"--target-id\", targetId], OPENCLAW_EVALUATE_TIMEOUT_MS);\n return parseOpenClawJson(raw);\n}\n","interface ParseSuccess<T> {\n ok: true;\n value: T;\n}\n\ninterface ParseFailure {\n ok: false;\n error: Error;\n}\n\nfunction buildPreview(raw: string): string {\n return raw.length > 200 ? `${raw.slice(0, 200)}...` : raw;\n}\n\nfunction tryParseJson<T>(raw: string): ParseSuccess<T> | ParseFailure {\n try {\n return { ok: true, value: JSON.parse(raw) as T };\n } catch (error) {\n return {\n ok: false,\n error: error instanceof Error ? error : new Error(String(error)),\n };\n }\n}\n\nfunction tryParseLastJsonLineBlock<T>(raw: string): ParseSuccess<T> | null {\n const lines = raw.split(/\\r?\\n/);\n\n // OpenClaw diagnostics are emitted as extra lines around the JSON payload,\n // so prefer the last contiguous block of lines that parses cleanly as JSON.\n for (let end = lines.length; end > 0; end -= 1) {\n for (let start = end - 1; start >= 0; start -= 1) {\n const candidate = lines.slice(start, end).join(\"\\n\").trim();\n if (!candidate) {\n continue;\n }\n\n const parsed = tryParseJson<T>(candidate);\n if (parsed.ok) {\n return parsed;\n }\n }\n }\n\n return null;\n}\n\nexport function parseOpenClawJson<T>(raw: string): T {\n const trimmed = raw.trim();\n if (!trimmed) {\n throw new Error(\"OpenClaw returned empty output\");\n }\n\n const direct = tryParseJson<T>(trimmed);\n if (direct.ok) {\n return direct.value;\n }\n\n const lineBlock = tryParseLastJsonLineBlock<T>(trimmed);\n if (lineBlock) {\n return lineBlock.value;\n }\n\n throw new Error(`Failed to parse OpenClaw JSON output: ${direct.error.message}\\nRaw (preview): ${buildPreview(trimmed)}`);\n}\n","function splitTopLevel(input: string, separator: string): string[] {\n const parts: string[] = [];\n let current = \"\";\n let depth = 0;\n let inString = false;\n\n for (let i = 0; i < input.length; i++) {\n const char = input[i];\n const prev = input[i - 1];\n if (char === '\"' && prev !== '\\\\') inString = !inString;\n if (!inString) {\n if (char === '{' || char === '(' || char === '[') depth++;\n if (char === '}' || char === ')' || char === ']') depth--;\n if (depth === 0 && input.slice(i, i + separator.length) === separator) {\n parts.push(current.trim());\n current = \"\";\n i += separator.length - 1;\n continue;\n }\n }\n current += char;\n }\n\n if (current.trim()) parts.push(current.trim());\n return parts;\n}\n\nfunction parseLiteral(value: string): unknown {\n const trimmed = value.trim();\n if (trimmed.startsWith('\"') && trimmed.endsWith('\"')) return JSON.parse(trimmed);\n if (trimmed === \"true\") return true;\n if (trimmed === \"false\") return false;\n if (trimmed === \"null\") return null;\n return Number(trimmed);\n}\n\nfunction getField(value: unknown, field: string): unknown {\n return value !== null && typeof value === \"object\" ? (value as Record<string, unknown>)[field] : undefined;\n}\n\nfunction applySegment(inputs: unknown[], expr: string): unknown[] {\n if (expr === \".\") return inputs;\n if (expr.startsWith(\"select(\")) {\n const match = expr.match(/^select\\((.+?)\\s*(==|>)\\s*(.+)\\)$/);\n if (!match) throw new Error(`不支持的 jq 表达式: ${expr}`);\n const [, leftExpr, op, rightExpr] = match;\n const expected = parseLiteral(rightExpr);\n return inputs.filter((item) => {\n const left = applyExpression([item], leftExpr)[0];\n return op === \"==\" ? left === expected : Number(left) > Number(expected);\n });\n }\n if (expr.startsWith(\"{\") && expr.endsWith(\"}\")) {\n const body = expr.slice(1, -1).trim();\n if (!body) return inputs.map(() => ({}));\n const entries = splitTopLevel(body, \",\");\n return inputs.map((item) => {\n const obj: Record<string, unknown> = {};\n for (const entry of entries) {\n const colon = entry.indexOf(\":\");\n if (colon === -1) {\n const key = entry.trim().replace(/^\\./, \"\");\n obj[key] = applyExpression([item], `.${key}`)[0];\n } else {\n const key = entry.slice(0, colon).trim();\n const valueExpr = entry.slice(colon + 1).trim();\n obj[key] = applyExpression([item], valueExpr)[0];\n }\n }\n return obj;\n });\n }\n if (!expr.startsWith(\".\")) throw new Error(`不支持的 jq 表达式: ${expr}`);\n\n let current = inputs;\n let remaining = expr.slice(1);\n while (remaining.length > 0) {\n if (remaining.startsWith(\"[]\")) {\n current = current.flatMap((item) => Array.isArray(item) ? item : []);\n remaining = remaining.slice(2);\n } else if (remaining.startsWith(\"[\")) {\n const match = remaining.match(/^\\[(-?\\d+)\\]/);\n if (!match) throw new Error(`不支持的 jq 表达式: .${remaining}`);\n const index = Number(match[1]);\n current = current.map((item) => {\n if (!Array.isArray(item)) return undefined;\n return item[index >= 0 ? index : item.length + index];\n });\n remaining = remaining.slice(match[0].length);\n } else if (remaining.startsWith(\".\")) {\n remaining = remaining.slice(1);\n } else {\n const match = remaining.match(/^([A-Za-z_][A-Za-z0-9_]*)/);\n if (!match) throw new Error(`不支持的 jq 表达式: .${remaining}`);\n const field = match[1];\n current = current.map((item) => getField(item, field));\n remaining = remaining.slice(field.length);\n }\n }\n return current;\n}\n\nfunction applyExpression(inputs: unknown[], expression: string): unknown[] {\n const segments = splitTopLevel(expression.trim(), \"|\");\n return segments.reduce((current, segment) => applySegment(current, segment.trim()), inputs);\n}\n\nexport function applyJq(data: unknown, expression: string): unknown[] {\n return applyExpression([data], expression).filter((item) => item !== undefined);\n}\n","#!/usr/bin/env node\n\n/**\n * Browser Web Search (BWS) - Turn any website into a CLI command\n * \n * 基于 OpenClaw 浏览器,复用用户登录态访问网站数据\n */\n\nimport { siteCommand } from \"./commands/site.js\";\n\nconst VERSION = \"0.2.0\";\n\nconst HELP_TEXT = `\nBrowser Web Search (BWS) v${VERSION}\n\n把任何网站变成命令行 API,使用 OpenClaw 浏览器 + 用户登录态。\n\n用法:\n bws site list 列出所有可用 adapter\n bws site info <name> 查看 adapter 元信息\n bws site search <query> 搜索 adapter\n bws site <name> [args...] 运行 adapter\n bws <name> [args...] 运行 adapter(简写)\n\n示例:\n bws site list # 查看所有可用命令\n bws zhihu/hot # 知乎热榜\n bws xiaohongshu/search \"旅行\" # 小红书搜索\n bws bilibili/popular # B站热门\n\n选项:\n --json 以 JSON 格式输出\n --jq <expr> 对 JSON 输出应用 jq 过滤\n --help, -h 显示帮助信息\n --version, -v 显示版本号\n\n需要 OpenClaw 环境运行。\n`.trim();\n\ninterface ParsedArgs {\n command: string;\n args: string[];\n flags: {\n json?: boolean;\n jq?: string;\n };\n}\n\nfunction parseArgs(args: string[]): ParsedArgs {\n const result: ParsedArgs = {\n command: \"\",\n args: [],\n flags: {},\n };\n\n let skipNext = false;\n\n for (let i = 0; i < args.length; i++) {\n if (skipNext) {\n skipNext = false;\n continue;\n }\n\n const arg = args[i];\n\n if (arg === \"--json\") {\n result.flags.json = true;\n } else if (arg === \"--jq\") {\n skipNext = true;\n const nextIdx = i + 1;\n if (nextIdx < args.length) {\n result.flags.jq = args[nextIdx];\n result.flags.json = true;\n }\n } else if (arg === \"--help\" || arg === \"-h\") {\n console.log(HELP_TEXT);\n process.exit(0);\n } else if (arg === \"--version\" || arg === \"-v\") {\n console.log(VERSION);\n process.exit(0);\n } else if (!result.command) {\n result.command = arg;\n } else {\n result.args.push(arg);\n }\n }\n\n return result;\n}\n\nasync function main(): Promise<void> {\n const args = process.argv.slice(2);\n\n if (args.length === 0) {\n console.log(HELP_TEXT);\n process.exit(0);\n }\n\n const parsed = parseArgs(args);\n\n switch (parsed.command) {\n case \"site\":\n await siteCommand(parsed.args, {\n json: parsed.flags.json,\n jq: parsed.flags.jq,\n });\n break;\n\n default:\n // 如果命令包含 /,当作 site 命令的简写\n if (parsed.command.includes(\"/\")) {\n await siteCommand([parsed.command, ...parsed.args], {\n json: parsed.flags.json,\n jq: parsed.flags.jq,\n });\n } else {\n console.error(`[error] 未知命令: ${parsed.command}`);\n console.error(\" 运行 bws --help 查看帮助\");\n process.exit(1);\n }\n break;\n }\n}\n\nmain().catch((error) => {\n console.error(`[error] ${error.message || error}`);\n process.exit(1);\n});\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/commands/site.ts","../src/openclaw-bridge.ts","../src/openclaw-json.ts","../src/jq.ts","../src/index.ts"],"sourcesContent":["/**\n * site 命令 - 管理和运行网站适配器\n *\n * 用法:\n * bws site list 列出所有可用 adapter\n * bws site search <query> 搜索 adapter\n * bws site info <name> 查看 adapter 详情\n * bws site <name> [args...] 运行 adapter\n *\n * 目录:\n * 内置 adapter:随项目发布\n * ~/.bws/sites/:用户私有 adapter(优先级更高)\n */\n\nimport { readFileSync, readdirSync, existsSync } from \"node:fs\";\nimport { join, relative } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { ocGetTabs, ocFindTabByDomain, ocOpenTab, ocEvaluate } from \"../openclaw-bridge.js\";\nimport { applyJq } from \"../jq.js\";\n\nconst BWS_DIR = join(homedir(), \".bws\");\nconst LOCAL_SITES_DIR = join(BWS_DIR, \"sites\"); // 用户私有 adapter(优先级最高)\n\n// 内置 adapter 目录(项目自带)\nimport { fileURLToPath } from \"node:url\";\nimport { dirname } from \"node:path\";\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\nconst BUILTIN_SITES_DIR = join(__dirname, \"../sites\"); // 项目内置 adapter\n\nexport interface SiteOptions {\n json?: boolean;\n jq?: string;\n}\n\n/** Adapter 参数定义 */\ninterface ArgDef {\n required?: boolean;\n description?: string;\n}\n\n/** Adapter 元数据 */\ninterface SiteMeta {\n name: string;\n description: string;\n domain: string;\n args: Record<string, ArgDef>;\n capabilities?: string[];\n readOnly?: boolean;\n example?: string;\n filePath: string;\n source: \"local\" | \"builtin\";\n}\n\nfunction exitJsonError(error: string, extra: Record<string, unknown> = {}): never {\n console.log(JSON.stringify({ success: false, error, ...extra }, null, 2));\n process.exit(1);\n}\n\n/**\n * 从 JS 文件的 /* @meta JSON * / 块解析元数据\n */\nfunction parseSiteMeta(filePath: string, source: \"local\" | \"builtin\"): SiteMeta | null {\n let content: string;\n try {\n content = readFileSync(filePath, \"utf-8\");\n } catch {\n return null;\n }\n\n // 从文件路径推断默认 name\n const sitesDir = source === \"local\" ? LOCAL_SITES_DIR : BUILTIN_SITES_DIR;\n const relPath = relative(sitesDir, filePath);\n const defaultName = relPath.replace(/\\.js$/, \"\").replace(/\\\\/g, \"/\");\n\n // 解析 /* @meta { ... } */ 块\n const metaMatch = content.match(/\\/\\*\\s*@meta\\s*\\n([\\s\\S]*?)\\*\\//);\n if (metaMatch) {\n try {\n const metaJson = JSON.parse(metaMatch[1]);\n return {\n name: metaJson.name || defaultName,\n description: metaJson.description || \"\",\n domain: metaJson.domain || \"\",\n args: metaJson.args || {},\n capabilities: metaJson.capabilities,\n readOnly: metaJson.readOnly,\n example: metaJson.example,\n filePath,\n source,\n };\n } catch {\n // JSON 解析失败,回退到 @tag 模式\n }\n }\n\n // 回退:解析 // @tag 格式(兼容旧格式)\n const meta: SiteMeta = {\n name: defaultName,\n description: \"\",\n domain: \"\",\n args: {},\n filePath,\n source,\n };\n\n const tagPattern = /\\/\\/\\s*@(\\w+)[ \\t]+(.*)/g;\n let match;\n while ((match = tagPattern.exec(content)) !== null) {\n const [, key, value] = match;\n switch (key) {\n case \"name\": meta.name = value.trim(); break;\n case \"description\": meta.description = value.trim(); break;\n case \"domain\": meta.domain = value.trim(); break;\n case \"args\":\n for (const arg of value.trim().split(/[,\\s]+/).filter(Boolean)) {\n meta.args[arg] = { required: true };\n }\n break;\n case \"example\": meta.example = value.trim(); break;\n }\n }\n\n return meta;\n}\n\n/**\n * 扫描目录下所有 .js 文件\n */\nfunction scanSites(dir: string, source: \"local\" | \"builtin\"): SiteMeta[] {\n if (!existsSync(dir)) return [];\n const sites: SiteMeta[] = [];\n\n function walk(currentDir: string): void {\n let entries;\n try { entries = readdirSync(currentDir, { withFileTypes: true }); } catch { return; }\n for (const entry of entries) {\n const fullPath = join(currentDir, entry.name);\n if (entry.isDirectory() && !entry.name.startsWith(\".\")) {\n walk(fullPath);\n } else if (entry.isFile() && entry.name.endsWith(\".js\")) {\n const meta = parseSiteMeta(fullPath, source);\n if (meta) sites.push(meta);\n }\n }\n }\n\n walk(dir);\n return sites;\n}\n\n/**\n * 获取所有 adapter(用户私有 > 内置)\n */\nfunction getAllSites(): SiteMeta[] {\n const builtin = scanSites(BUILTIN_SITES_DIR, \"builtin\");\n const local = scanSites(LOCAL_SITES_DIR, \"local\");\n\n const byName = new Map<string, SiteMeta>();\n for (const s of builtin) byName.set(s.name, s);\n for (const s of local) byName.set(s.name, s); // 用户私有覆盖内置\n\n return Array.from(byName.values()).sort((a, b) => a.name.localeCompare(b.name));\n}\n\n// ── 子命令 ──────────────────────────────────────────────────────\n\nfunction siteList(options: SiteOptions): void {\n const sites = getAllSites();\n\n if (sites.length === 0) {\n if (options.json) {\n console.log(\"[]\");\n return;\n }\n console.log(\"未找到任何 site adapter。\");\n console.log(` 内置 adapter 目录: ${BUILTIN_SITES_DIR}`);\n console.log(` 私有 adapter 目录: ${LOCAL_SITES_DIR}`);\n return;\n }\n\n if (options.json) {\n console.log(JSON.stringify(sites.map(s => ({\n name: s.name, description: s.description, domain: s.domain,\n args: s.args, source: s.source,\n })), null, 2));\n return;\n }\n\n const groups = new Map<string, SiteMeta[]>();\n for (const s of sites) {\n const platform = s.name.split(\"/\")[0];\n if (!groups.has(platform)) groups.set(platform, []);\n groups.get(platform)!.push(s);\n }\n\n for (const [platform, items] of groups) {\n console.log(`\\n${platform}/`);\n for (const s of items) {\n const cmd = s.name.split(\"/\").slice(1).join(\"/\");\n const src = s.source === \"local\" ? \" (local)\" : \"\";\n const desc = s.description ? ` - ${s.description}` : \"\";\n console.log(` ${cmd.padEnd(20)}${desc}${src}`);\n }\n }\n console.log();\n}\n\nfunction siteSearch(query: string, options: SiteOptions): void {\n const sites = getAllSites();\n const q = query.toLowerCase();\n const matches = sites.filter(s =>\n s.name.toLowerCase().includes(q) ||\n s.description.toLowerCase().includes(q) ||\n s.domain.toLowerCase().includes(q)\n );\n\n if (matches.length === 0) {\n if (options.json) {\n console.log(\"[]\");\n return;\n }\n console.log(`未找到匹配 \"${query}\" 的 adapter。`);\n console.log(\" 查看所有: bws site list\");\n return;\n }\n\n if (options.json) {\n console.log(JSON.stringify(matches.map(s => ({\n name: s.name, description: s.description, domain: s.domain, source: s.source,\n })), null, 2));\n return;\n }\n\n for (const s of matches) {\n const src = s.source === \"local\" ? \" (local)\" : \"\";\n console.log(`${s.name.padEnd(24)} ${s.description}${src}`);\n }\n}\n\nfunction siteInfo_showDirs(options: SiteOptions): void {\n const builtin = scanSites(BUILTIN_SITES_DIR, \"builtin\");\n const local = scanSites(LOCAL_SITES_DIR, \"local\");\n \n if (options.json) {\n console.log(JSON.stringify({\n builtinDir: BUILTIN_SITES_DIR,\n localDir: LOCAL_SITES_DIR,\n builtinCount: builtin.length,\n localCount: local.length,\n }, null, 2));\n return;\n }\n \n console.log(`内置 adapter: ${BUILTIN_SITES_DIR} (${builtin.length} 个)`);\n console.log(`私有 adapter: ${LOCAL_SITES_DIR} (${local.length} 个)`);\n}\n\nfunction findSiteByName(name: string): SiteMeta | undefined {\n return getAllSites().find((site) => site.name === name);\n}\n\nfunction siteInfo(name: string, options: SiteOptions): void {\n const site = findSiteByName(name);\n\n if (!site) {\n if (options.json) {\n exitJsonError(`adapter \"${name}\" not found`, { action: \"bws site list\" });\n }\n console.error(`[error] site info: adapter \"${name}\" not found.`);\n console.error(\" Try: bws site list\");\n process.exit(1);\n }\n\n const meta = {\n name: site.name,\n description: site.description,\n domain: site.domain,\n args: site.args,\n example: site.example,\n readOnly: site.readOnly,\n };\n\n if (options.json) {\n console.log(JSON.stringify(meta, null, 2));\n return;\n }\n\n console.log(`${site.name} — ${site.description}`);\n console.log();\n console.log(\"参数:\");\n\n const argEntries = Object.entries(site.args);\n if (argEntries.length === 0) {\n console.log(\" (无)\");\n } else {\n for (const [argName, argDef] of argEntries) {\n const requiredText = argDef.required ? \"必填\" : \"可选\";\n const description = argDef.description || \"\";\n console.log(` ${argName} (${requiredText}) ${description}`.trimEnd());\n }\n }\n\n console.log();\n console.log(\"示例:\");\n console.log(` ${site.example || `bws site ${site.name}`}`);\n console.log();\n console.log(`域名:${site.domain || \"(未声明)\"}`);\n console.log(`只读:${site.readOnly ? \"是\" : \"否\"}`);\n}\n\nasync function siteRun(\n name: string,\n args: string[],\n options: SiteOptions\n): Promise<void> {\n const sites = getAllSites();\n const site = sites.find(s => s.name === name);\n\n if (!site) {\n const fuzzy = sites.filter(s => s.name.includes(name));\n if (options.json) {\n exitJsonError(`site \"${name}\" not found`, {\n suggestions: fuzzy.slice(0, 5).map(s => s.name),\n action: fuzzy.length > 0 ? undefined : \"bws site update\",\n });\n }\n console.error(`[error] site: \"${name}\" not found.`);\n if (fuzzy.length > 0) {\n console.error(\" Did you mean:\");\n for (const s of fuzzy.slice(0, 5)) {\n console.error(` bws site ${s.name}`);\n }\n } else {\n console.error(\" Try: bws site list\");\n console.error(\" Or: bws site update\");\n }\n process.exit(1);\n }\n\n // 解析参数\n const argNames = Object.keys(site.args);\n const argMap: Record<string, string> = {};\n\n // 过滤掉 --flag value 对,收集位置参数\n const positionalArgs: string[] = [];\n for (let i = 0; i < args.length; i++) {\n if (args[i].startsWith(\"--\")) {\n const flagName = args[i].slice(2);\n if (flagName in site.args && args[i + 1]) {\n argMap[flagName] = args[i + 1];\n i++; // 跳过值\n }\n } else {\n positionalArgs.push(args[i]);\n }\n }\n\n // 位置参数按 argNames 顺序填入(跳过已通过 --flag 提供的)\n let posIdx = 0;\n for (const argName of argNames) {\n if (!argMap[argName] && posIdx < positionalArgs.length) {\n argMap[argName] = positionalArgs[posIdx++];\n }\n }\n\n // 只检查 required 参数\n for (const [argName, argDef] of Object.entries(site.args)) {\n if (argDef.required && !argMap[argName]) {\n const usage = argNames.map(a => {\n const def = site.args[a];\n return def.required ? `<${a}>` : `[${a}]`;\n }).join(\" \");\n if (options.json) {\n exitJsonError(`missing required argument \"${argName}\"`, {\n usage: `bws site ${name} ${usage}`,\n example: site.example,\n });\n }\n console.error(`[error] site ${name}: missing required argument \"${argName}\".`);\n console.error(` Usage: bws site ${name} ${usage}`);\n if (site.example) console.error(` Example: ${site.example}`);\n process.exit(1);\n }\n }\n\n // 读取并解析 JS\n const jsContent = readFileSync(site.filePath, \"utf-8\");\n\n // 移除 /* @meta ... */ 块,保留函数体\n const jsBody = jsContent.replace(/\\/\\*\\s*@meta[\\s\\S]*?\\*\\//, \"\").trim();\n\n // 构造执行脚本\n const argsJson = JSON.stringify(argMap);\n\n // OpenClaw 模式执行\n let targetId: string;\n\n if (site.domain) {\n const tabs = ocGetTabs();\n const existing = ocFindTabByDomain(tabs, site.domain);\n if (existing) {\n targetId = existing.targetId;\n } else {\n targetId = ocOpenTab(`https://${site.domain}`);\n await new Promise((resolve) => setTimeout(resolve, 3000));\n }\n } else {\n const tabs = ocGetTabs();\n if (tabs.length === 0) {\n throw new Error(\"No tabs open in OpenClaw browser\");\n }\n targetId = tabs[0].targetId;\n }\n\n const wrappedFn = `async () => { const __fn = ${jsBody}; return await __fn(${argsJson}); }`;\n const parsed = ocEvaluate(targetId, wrappedFn);\n\n if (typeof parsed === \"object\" && parsed !== null && \"error\" in parsed) {\n const errObj = parsed as { error: string; hint?: string };\n const checkText = `${errObj.error} ${errObj.hint || \"\"}`;\n const isAuthError = /401|403|unauthorized|forbidden|not.?logged|login.?required|sign.?in|auth/i.test(checkText);\n const loginHint = isAuthError && site.domain\n ? `Please log in to https://${site.domain} in your OpenClaw browser first, then retry.`\n : undefined;\n const hint = loginHint || errObj.hint;\n\n if (options.json) {\n console.log(JSON.stringify({ success: false, error: errObj.error, hint }));\n } else {\n console.error(`[error] site ${name}: ${errObj.error}`);\n if (hint) console.error(` Hint: ${hint}`);\n }\n process.exit(1);\n }\n\n if (options.jq) {\n const expr = options.jq.replace(/^\\.data\\./, '.');\n const results = applyJq(parsed, expr);\n for (const r of results) {\n console.log(typeof r === \"string\" ? r : JSON.stringify(r));\n }\n } else if (options.json) {\n console.log(JSON.stringify({ success: true, data: parsed }));\n } else {\n console.log(JSON.stringify(parsed, null, 2));\n }\n}\n\n// ── 入口 ────────────────────────────────────────────────────────\n\nexport async function siteCommand(\n args: string[],\n options: SiteOptions = {}\n): Promise<void> {\n const subCommand = args[0];\n\n if (!subCommand || subCommand === \"--help\" || subCommand === \"-h\") {\n console.log(`bws site - 网站 CLI 化\n\n用法:\n bws site list 列出所有可用 adapter\n bws site info <name> 查看 adapter 详情\n bws site search <query> 搜索 adapter\n bws site <name> [args...] 运行 adapter\n\n示例:\n bws site list\n bws site zhihu/hot\n bws site xiaohongshu/search \"旅行\"\n bws site bilibili/popular`);\n return;\n }\n\n switch (subCommand) {\n case \"list\": siteList(options); break;\n case \"search\":\n if (!args[1]) {\n console.error(\"[error] site search: <query> is required.\");\n console.error(\" Usage: bws site search <query>\");\n process.exit(1);\n }\n siteSearch(args[1], options);\n break;\n case \"info\":\n if (!args[1]) {\n console.error(\"[error] site info: <name> is required.\");\n console.error(\" Usage: bws site info <name>\");\n process.exit(1);\n }\n siteInfo(args[1], options);\n break;\n case \"run\":\n if (!args[1]) {\n console.error(\"[error] site run: <name> is required.\");\n console.error(\" Usage: bws site run <name> [args...]\");\n console.error(\" Try: bws site list\");\n process.exit(1);\n }\n await siteRun(args[1], args.slice(2), options);\n break;\n default:\n if (subCommand.includes(\"/\")) {\n await siteRun(subCommand, args.slice(1), options);\n } else {\n console.error(`[error] site: unknown subcommand \"${subCommand}\".`);\n console.error(\" Available: list, info, search, run\");\n console.error(\" Try: bws site --help\");\n process.exit(1);\n }\n break;\n }\n\n}\n","import { execFileSync } from \"node:child_process\";\nimport { parseOpenClawJson } from \"./openclaw-json.js\";\n\nconst OPENCLAW_EVALUATE_TIMEOUT_MS = 120000;\nconst EXEC_TIMEOUT_BUFFER_MS = 5000;\n\nexport interface OCTab {\n targetId: string;\n url: string;\n title: string;\n type: string;\n}\n\n// Global flags that should be placed after \"browser\" but before subcommand\nconst GLOBAL_FLAGS = new Set([\"--json\", \"--timeout\"]);\n\nexport function buildOpenClawArgs(args: string[], timeout: number): string[] {\n const [subcommand, ...rest] = args;\n if (!subcommand) {\n throw new Error(\"OpenClaw browser command requires a subcommand\");\n }\n\n // Separate global flags from subcommand-specific args\n // Use --flag=value format to avoid npx argument forwarding issues\n const globalFlags: string[] = [`--timeout=${timeout}`];\n const subcommandArgs: string[] = [];\n\n for (let i = 0; i < rest.length; i++) {\n const arg = rest[i];\n if (GLOBAL_FLAGS.has(arg)) {\n // Use --flag=value format to avoid npx argument forwarding issues\n if (arg !== \"--json\" && i + 1 < rest.length) {\n globalFlags.push(`${arg}=${rest[++i]}`);\n } else {\n globalFlags.push(arg);\n }\n } else {\n subcommandArgs.push(arg);\n }\n }\n\n return [\"openclaw\", \"browser\", ...globalFlags, subcommand, ...subcommandArgs];\n}\n\nexport function getOpenClawExecTimeout(timeout: number): number {\n return timeout + EXEC_TIMEOUT_BUFFER_MS;\n}\n\nfunction runOpenClaw(args: string[], timeout: number): string {\n return execFileSync(\"npx\", buildOpenClawArgs(args, timeout), {\n encoding: \"utf-8\",\n timeout: getOpenClawExecTimeout(timeout),\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n }).trim();\n}\n\nexport function ocGetTabs(): OCTab[] {\n const raw = runOpenClaw([\"tabs\", \"--json\"], 15000);\n const data = parseOpenClawJson<{ tabs?: OCTab[] }>(raw);\n return (data.tabs || []).filter((tab: OCTab) => tab.type === \"page\");\n}\n\nexport function ocFindTabByDomain(tabs: OCTab[], domain: string): OCTab | undefined {\n return tabs.find((tab) => {\n try {\n const hostname = new URL(tab.url).hostname;\n return hostname === domain || hostname.endsWith(`.${domain}`);\n } catch {\n return false;\n }\n });\n}\n\nexport function ocOpenTab(url: string): string {\n const raw = runOpenClaw([\"open\", url, \"--json\"], 30000);\n const data = parseOpenClawJson<{ id?: string; targetId?: string }>(raw);\n return data.id || data.targetId || \"\";\n}\n\nexport function ocEvaluate(targetId: string, fn: string): unknown {\n // Use --flag=value format to avoid npx argument forwarding issues\n const raw = runOpenClaw([\"evaluate\", `--fn=${fn}`, `--target-id=${targetId}`], OPENCLAW_EVALUATE_TIMEOUT_MS);\n return parseOpenClawJson(raw);\n}\n","interface ParseSuccess<T> {\n ok: true;\n value: T;\n}\n\ninterface ParseFailure {\n ok: false;\n error: Error;\n}\n\nfunction buildPreview(raw: string): string {\n return raw.length > 200 ? `${raw.slice(0, 200)}...` : raw;\n}\n\nfunction tryParseJson<T>(raw: string): ParseSuccess<T> | ParseFailure {\n try {\n return { ok: true, value: JSON.parse(raw) as T };\n } catch (error) {\n return {\n ok: false,\n error: error instanceof Error ? error : new Error(String(error)),\n };\n }\n}\n\nfunction tryParseLastJsonLineBlock<T>(raw: string): ParseSuccess<T> | null {\n const lines = raw.split(/\\r?\\n/);\n\n // OpenClaw diagnostics are emitted as extra lines around the JSON payload,\n // so prefer the last contiguous block of lines that parses cleanly as JSON.\n for (let end = lines.length; end > 0; end -= 1) {\n for (let start = end - 1; start >= 0; start -= 1) {\n const candidate = lines.slice(start, end).join(\"\\n\").trim();\n if (!candidate) {\n continue;\n }\n\n const parsed = tryParseJson<T>(candidate);\n if (parsed.ok) {\n return parsed;\n }\n }\n }\n\n return null;\n}\n\nexport function parseOpenClawJson<T>(raw: string): T {\n const trimmed = raw.trim();\n if (!trimmed) {\n throw new Error(\"OpenClaw returned empty output\");\n }\n\n const direct = tryParseJson<T>(trimmed);\n if (direct.ok) {\n return direct.value;\n }\n\n const lineBlock = tryParseLastJsonLineBlock<T>(trimmed);\n if (lineBlock) {\n return lineBlock.value;\n }\n\n throw new Error(`Failed to parse OpenClaw JSON output: ${direct.error.message}\\nRaw (preview): ${buildPreview(trimmed)}`);\n}\n","function splitTopLevel(input: string, separator: string): string[] {\n const parts: string[] = [];\n let current = \"\";\n let depth = 0;\n let inString = false;\n\n for (let i = 0; i < input.length; i++) {\n const char = input[i];\n const prev = input[i - 1];\n if (char === '\"' && prev !== '\\\\') inString = !inString;\n if (!inString) {\n if (char === '{' || char === '(' || char === '[') depth++;\n if (char === '}' || char === ')' || char === ']') depth--;\n if (depth === 0 && input.slice(i, i + separator.length) === separator) {\n parts.push(current.trim());\n current = \"\";\n i += separator.length - 1;\n continue;\n }\n }\n current += char;\n }\n\n if (current.trim()) parts.push(current.trim());\n return parts;\n}\n\nfunction parseLiteral(value: string): unknown {\n const trimmed = value.trim();\n if (trimmed.startsWith('\"') && trimmed.endsWith('\"')) return JSON.parse(trimmed);\n if (trimmed === \"true\") return true;\n if (trimmed === \"false\") return false;\n if (trimmed === \"null\") return null;\n return Number(trimmed);\n}\n\nfunction getField(value: unknown, field: string): unknown {\n return value !== null && typeof value === \"object\" ? (value as Record<string, unknown>)[field] : undefined;\n}\n\nfunction applySegment(inputs: unknown[], expr: string): unknown[] {\n if (expr === \".\") return inputs;\n if (expr.startsWith(\"select(\")) {\n const match = expr.match(/^select\\((.+?)\\s*(==|>)\\s*(.+)\\)$/);\n if (!match) throw new Error(`不支持的 jq 表达式: ${expr}`);\n const [, leftExpr, op, rightExpr] = match;\n const expected = parseLiteral(rightExpr);\n return inputs.filter((item) => {\n const left = applyExpression([item], leftExpr)[0];\n return op === \"==\" ? left === expected : Number(left) > Number(expected);\n });\n }\n if (expr.startsWith(\"{\") && expr.endsWith(\"}\")) {\n const body = expr.slice(1, -1).trim();\n if (!body) return inputs.map(() => ({}));\n const entries = splitTopLevel(body, \",\");\n return inputs.map((item) => {\n const obj: Record<string, unknown> = {};\n for (const entry of entries) {\n const colon = entry.indexOf(\":\");\n if (colon === -1) {\n const key = entry.trim().replace(/^\\./, \"\");\n obj[key] = applyExpression([item], `.${key}`)[0];\n } else {\n const key = entry.slice(0, colon).trim();\n const valueExpr = entry.slice(colon + 1).trim();\n obj[key] = applyExpression([item], valueExpr)[0];\n }\n }\n return obj;\n });\n }\n if (!expr.startsWith(\".\")) throw new Error(`不支持的 jq 表达式: ${expr}`);\n\n let current = inputs;\n let remaining = expr.slice(1);\n while (remaining.length > 0) {\n if (remaining.startsWith(\"[]\")) {\n current = current.flatMap((item) => Array.isArray(item) ? item : []);\n remaining = remaining.slice(2);\n } else if (remaining.startsWith(\"[\")) {\n const match = remaining.match(/^\\[(-?\\d+)\\]/);\n if (!match) throw new Error(`不支持的 jq 表达式: .${remaining}`);\n const index = Number(match[1]);\n current = current.map((item) => {\n if (!Array.isArray(item)) return undefined;\n return item[index >= 0 ? index : item.length + index];\n });\n remaining = remaining.slice(match[0].length);\n } else if (remaining.startsWith(\".\")) {\n remaining = remaining.slice(1);\n } else {\n const match = remaining.match(/^([A-Za-z_][A-Za-z0-9_]*)/);\n if (!match) throw new Error(`不支持的 jq 表达式: .${remaining}`);\n const field = match[1];\n current = current.map((item) => getField(item, field));\n remaining = remaining.slice(field.length);\n }\n }\n return current;\n}\n\nfunction applyExpression(inputs: unknown[], expression: string): unknown[] {\n const segments = splitTopLevel(expression.trim(), \"|\");\n return segments.reduce((current, segment) => applySegment(current, segment.trim()), inputs);\n}\n\nexport function applyJq(data: unknown, expression: string): unknown[] {\n return applyExpression([data], expression).filter((item) => item !== undefined);\n}\n","#!/usr/bin/env node\n\n/**\n * Browser Web Search (BWS) - Turn any website into a CLI command\n * \n * 基于 OpenClaw 浏览器,复用用户登录态访问网站数据\n */\n\nimport { siteCommand } from \"./commands/site.js\";\n\nconst VERSION = \"0.2.0\";\n\nconst HELP_TEXT = `\nBrowser Web Search (BWS) v${VERSION}\n\n把任何网站变成命令行 API,使用 OpenClaw 浏览器 + 用户登录态。\n\n用法:\n bws site list 列出所有可用 adapter\n bws site info <name> 查看 adapter 元信息\n bws site search <query> 搜索 adapter\n bws site <name> [args...] 运行 adapter\n bws <name> [args...] 运行 adapter(简写)\n\n示例:\n bws site list # 查看所有可用命令\n bws zhihu/hot # 知乎热榜\n bws xiaohongshu/search \"旅行\" # 小红书搜索\n bws bilibili/popular # B站热门\n\n选项:\n --json 以 JSON 格式输出\n --jq <expr> 对 JSON 输出应用 jq 过滤\n --help, -h 显示帮助信息\n --version, -v 显示版本号\n\n需要 OpenClaw 环境运行。\n`.trim();\n\ninterface ParsedArgs {\n command: string;\n args: string[];\n flags: {\n json?: boolean;\n jq?: string;\n };\n}\n\nfunction parseArgs(args: string[]): ParsedArgs {\n const result: ParsedArgs = {\n command: \"\",\n args: [],\n flags: {},\n };\n\n let skipNext = false;\n\n for (let i = 0; i < args.length; i++) {\n if (skipNext) {\n skipNext = false;\n continue;\n }\n\n const arg = args[i];\n\n if (arg === \"--json\") {\n result.flags.json = true;\n } else if (arg === \"--jq\") {\n skipNext = true;\n const nextIdx = i + 1;\n if (nextIdx < args.length) {\n result.flags.jq = args[nextIdx];\n result.flags.json = true;\n }\n } else if (arg === \"--help\" || arg === \"-h\") {\n console.log(HELP_TEXT);\n process.exit(0);\n } else if (arg === \"--version\" || arg === \"-v\") {\n console.log(VERSION);\n process.exit(0);\n } else if (!result.command) {\n result.command = arg;\n } else {\n result.args.push(arg);\n }\n }\n\n return result;\n}\n\nasync function main(): Promise<void> {\n const args = process.argv.slice(2);\n\n if (args.length === 0) {\n console.log(HELP_TEXT);\n process.exit(0);\n }\n\n const parsed = parseArgs(args);\n\n switch (parsed.command) {\n case \"site\":\n await siteCommand(parsed.args, {\n json: parsed.flags.json,\n jq: parsed.flags.jq,\n });\n break;\n\n default:\n // 如果命令包含 /,当作 site 命令的简写\n if (parsed.command.includes(\"/\")) {\n await siteCommand([parsed.command, ...parsed.args], {\n json: parsed.flags.json,\n jq: parsed.flags.jq,\n });\n } else {\n console.error(`[error] 未知命令: ${parsed.command}`);\n console.error(\" 运行 bws --help 查看帮助\");\n process.exit(1);\n }\n break;\n }\n}\n\nmain().catch((error) => {\n console.error(`[error] ${error.message || error}`);\n process.exit(1);\n});\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "browser-web-search",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "把任何网站变成命令行 API,专为 OpenClaw 设计,复用浏览器登录态",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,51 @@
1
+ /* @meta
2
+ {
3
+ "name": "36kr/article",
4
+ "description": "36氪文章详情",
5
+ "domain": "36kr.com",
6
+ "args": {
7
+ "id": {"required": true, "description": "文章ID"}
8
+ },
9
+ "readOnly": true,
10
+ "example": "bws 36kr/article 123456789"
11
+ }
12
+ */
13
+
14
+ async function(args) {
15
+ if (!args.id) return {error: 'Missing argument: id', hint: 'Provide an article ID'};
16
+
17
+ const resp = await fetch('https://36kr.com/api/post/' + args.id, {
18
+ credentials: 'include'
19
+ });
20
+
21
+ if (!resp.ok) {
22
+ return {error: 'HTTP ' + resp.status, hint: 'Article may not exist or 36kr.com needs login'};
23
+ }
24
+
25
+ let data;
26
+ try {
27
+ data = await resp.json();
28
+ } catch (e) {
29
+ return {error: 'Failed to parse response', hint: '36kr API may have changed'};
30
+ }
31
+
32
+ if (!data || data.code !== 0 || !data.data) {
33
+ return {error: data.message || 'Article not found', hint: 'Check the article ID'};
34
+ }
35
+
36
+ const article = data.data;
37
+ return {
38
+ id: article.id,
39
+ title: article.title || '',
40
+ summary: article.summary || '',
41
+ content: article.content || article.text || '',
42
+ author: article.author?.name || article.user?.name || '',
43
+ published_at: article.published_at || article.created_at || '',
44
+ url: 'https://36kr.com/p/' + article.id,
45
+ cover: article.cover || '',
46
+ views: article.views_count || 0,
47
+ comments: article.comments_count || 0,
48
+ likes: article.likes_count || 0,
49
+ tags: (article.tags || []).map(t => t.name || t)
50
+ };
51
+ }
@@ -0,0 +1,55 @@
1
+ /* @meta
2
+ {
3
+ "name": "36kr/search",
4
+ "description": "36氪文章搜索",
5
+ "domain": "36kr.com",
6
+ "args": {
7
+ "keyword": {"required": true, "description": "搜索关键词"},
8
+ "count": {"required": false, "description": "返回数量 (默认 20, 最多 50)"}
9
+ },
10
+ "readOnly": true,
11
+ "example": "bws 36kr/search AI"
12
+ }
13
+ */
14
+
15
+ async function(args) {
16
+ if (!args.keyword) return {error: 'Missing argument: keyword', hint: 'Provide a search keyword'};
17
+ const count = Math.min(parseInt(args.count) || 20, 50);
18
+
19
+ const resp = await fetch('https://36kr.com/api/search-column/mainsite?per_page=' + count + '&page=1&keyword=' + encodeURIComponent(args.keyword), {
20
+ credentials: 'include'
21
+ });
22
+
23
+ if (!resp.ok) {
24
+ return {error: 'HTTP ' + resp.status, hint: 'Open 36kr.com in browser first'};
25
+ }
26
+
27
+ let data;
28
+ try {
29
+ data = await resp.json();
30
+ } catch (e) {
31
+ return {error: 'Failed to parse response', hint: '36kr API may have changed'};
32
+ }
33
+
34
+ if (!data || data.code !== 0 || !data.data) {
35
+ return {error: data.message || 'No data returned', hint: 'Try a different keyword'};
36
+ }
37
+
38
+ const items = (data.data.items || []).map(item => ({
39
+ id: item.id,
40
+ title: item.title || '',
41
+ summary: item.summary || item.description || '',
42
+ author: item.author || item.user?.name || '',
43
+ published_at: item.published_at || item.created_at || '',
44
+ url: 'https://36kr.com/p/' + item.id,
45
+ cover: item.cover || '',
46
+ views: item.views_count || 0,
47
+ comments: item.comments_count || 0
48
+ }));
49
+
50
+ return {
51
+ keyword: args.keyword,
52
+ count: items.length,
53
+ items
54
+ };
55
+ }
@@ -0,0 +1,51 @@
1
+ /* @meta
2
+ {
3
+ "name": "netease/hot",
4
+ "description": "网易新闻热榜",
5
+ "domain": "www.163.com",
6
+ "args": {
7
+ "count": {"required": false, "description": "返回数量 (默认 20, 最多 50)"}
8
+ },
9
+ "readOnly": true,
10
+ "example": "bws netease/hot"
11
+ }
12
+ */
13
+
14
+ async function(args) {
15
+ const maxCount = Math.min(parseInt(args.count) || 20, 50);
16
+
17
+ const resp = await fetch('https://m.163.com/fe/api/hot/news/flow', {
18
+ credentials: 'include'
19
+ });
20
+
21
+ if (!resp.ok) {
22
+ return {error: 'HTTP ' + resp.status, hint: 'Open www.163.com in browser first'};
23
+ }
24
+
25
+ let data;
26
+ try {
27
+ data = await resp.json();
28
+ } catch (e) {
29
+ return {error: 'Failed to parse response', hint: 'Netease API may have changed'};
30
+ }
31
+
32
+ if (!data || !data.data || !data.data.list) {
33
+ return {error: 'No data returned', hint: 'Netease API may have changed'};
34
+ }
35
+
36
+ const items = data.data.list.slice(0, maxCount).map((item, i) => ({
37
+ rank: i + 1,
38
+ id: item.docid,
39
+ title: item.title || '',
40
+ cover: item.imgsrc || '',
41
+ source: item.source || '',
42
+ time: item.ptime || '',
43
+ url: 'https://www.163.com/dy/article/' + item.docid + '.html',
44
+ mobileUrl: 'https://m.163.com/dy/article/' + item.docid + '.html'
45
+ }));
46
+
47
+ return {
48
+ count: items.length,
49
+ items
50
+ };
51
+ }
@@ -0,0 +1,53 @@
1
+ /* @meta
2
+ {
3
+ "name": "qqnews/hot",
4
+ "description": "腾讯新闻热榜",
5
+ "domain": "news.qq.com",
6
+ "args": {
7
+ "count": {"required": false, "description": "返回数量 (默认 20, 最多 50)"}
8
+ },
9
+ "readOnly": true,
10
+ "example": "bws qqnews/hot"
11
+ }
12
+ */
13
+
14
+ async function(args) {
15
+ const maxCount = Math.min(parseInt(args.count) || 20, 50);
16
+
17
+ const resp = await fetch('https://r.inews.qq.com/gw/event/hot_ranking_list?page_size=50', {
18
+ credentials: 'include'
19
+ });
20
+
21
+ if (!resp.ok) {
22
+ return {error: 'HTTP ' + resp.status, hint: 'Open news.qq.com in browser first'};
23
+ }
24
+
25
+ let data;
26
+ try {
27
+ data = await resp.json();
28
+ } catch (e) {
29
+ return {error: 'Failed to parse response', hint: 'QQ News API may have changed'};
30
+ }
31
+
32
+ if (!data || !data.idlist || !data.idlist[0] || !data.idlist[0].newslist) {
33
+ return {error: 'No data returned', hint: 'QQ News API may have changed'};
34
+ }
35
+
36
+ const list = data.idlist[0].newslist.slice(1, maxCount + 1);
37
+ const items = list.map((item, i) => ({
38
+ rank: i + 1,
39
+ id: item.id,
40
+ title: item.title || '',
41
+ desc: item.abstract || '',
42
+ cover: item.miniProShareImage || '',
43
+ source: item.source || '',
44
+ hot: item.hotEvent?.hotScore || 0,
45
+ url: 'https://new.qq.com/rain/a/' + item.id,
46
+ mobileUrl: 'https://view.inews.qq.com/k/' + item.id
47
+ }));
48
+
49
+ return {
50
+ count: items.length,
51
+ items
52
+ };
53
+ }
@@ -0,0 +1,86 @@
1
+ /* @meta
2
+ {
3
+ "name": "sina/hot",
4
+ "description": "新浪新闻热榜",
5
+ "domain": "news.sina.com.cn",
6
+ "args": {
7
+ "type": {"required": false, "description": "分类: all/china/world/society/sports/finance/ent/tech/mil (默认 all)"},
8
+ "count": {"required": false, "description": "返回数量 (默认 20, 最多 50)"}
9
+ },
10
+ "readOnly": true,
11
+ "example": "bws sina/hot tech"
12
+ }
13
+ */
14
+
15
+ async function(args) {
16
+ const typeMap = {
17
+ 'all': {www: 'news', params: 'www_www_all_suda_suda'},
18
+ 'china': {www: 'news', params: 'news_china_suda'},
19
+ 'world': {www: 'news', params: 'news_world_suda'},
20
+ 'society': {www: 'news', params: 'news_society_suda'},
21
+ 'sports': {www: 'sports', params: 'sports_suda'},
22
+ 'finance': {www: 'finance', params: 'finance_0_suda'},
23
+ 'ent': {www: 'ent', params: 'ent_suda'},
24
+ 'tech': {www: 'tech', params: 'tech_news_suda'},
25
+ 'mil': {www: 'news', params: 'news_mil_suda'}
26
+ };
27
+
28
+ const type = args.type || 'all';
29
+ const config = typeMap[type] || typeMap['all'];
30
+ const maxCount = Math.min(parseInt(args.count) || 20, 50);
31
+
32
+ const now = new Date();
33
+ const year = now.getFullYear();
34
+ const month = String(now.getMonth() + 1).padStart(2, '0');
35
+ const day = String(now.getDate()).padStart(2, '0');
36
+ const dateStr = '' + year + month + day;
37
+
38
+ const url = 'https://top.' + config.www + '.sina.com.cn/ws/GetTopDataList.php?top_type=day&top_cat=' + config.params + '&top_time=' + dateStr + '&top_show_num=50';
39
+
40
+ const resp = await fetch(url, {credentials: 'include'});
41
+
42
+ if (!resp.ok) {
43
+ return {error: 'HTTP ' + resp.status, hint: 'Open news.sina.com.cn in browser first'};
44
+ }
45
+
46
+ let text;
47
+ try {
48
+ text = await resp.text();
49
+ } catch (e) {
50
+ return {error: 'Failed to fetch response', hint: 'Sina API may have changed'};
51
+ }
52
+
53
+ // Parse JSONP: var data = {...};
54
+ let data;
55
+ try {
56
+ const prefix = 'var data = ';
57
+ if (!text.startsWith(prefix)) {
58
+ return {error: 'Invalid response format', hint: 'Sina API may have changed'};
59
+ }
60
+ let jsonStr = text.slice(prefix.length).trim();
61
+ if (jsonStr.endsWith(';')) jsonStr = jsonStr.slice(0, -1);
62
+ data = JSON.parse(jsonStr);
63
+ } catch (e) {
64
+ return {error: 'Failed to parse response', hint: 'Sina API may have changed'};
65
+ }
66
+
67
+ if (!data || !data.data) {
68
+ return {error: 'No data returned', hint: 'Sina API may have changed'};
69
+ }
70
+
71
+ const items = data.data.slice(0, maxCount).map((item, i) => ({
72
+ rank: i + 1,
73
+ id: item.id,
74
+ title: item.title || '',
75
+ source: item.media || '',
76
+ hot: parseFloat((item.top_num || '0').replace(/,/g, '')),
77
+ time: (item.create_date || '') + ' ' + (item.create_time || ''),
78
+ url: item.url || ''
79
+ }));
80
+
81
+ return {
82
+ type: type,
83
+ count: items.length,
84
+ items
85
+ };
86
+ }
@@ -0,0 +1,50 @@
1
+ /* @meta
2
+ {
3
+ "name": "thepaper/hot",
4
+ "description": "澎湃新闻热榜",
5
+ "domain": "www.thepaper.cn",
6
+ "args": {
7
+ "count": {"required": false, "description": "返回数量 (默认 20, 最多 50)"}
8
+ },
9
+ "readOnly": true,
10
+ "example": "bws thepaper/hot"
11
+ }
12
+ */
13
+
14
+ async function(args) {
15
+ const maxCount = Math.min(parseInt(args.count) || 20, 50);
16
+
17
+ const resp = await fetch('https://cache.thepaper.cn/contentapi/wwwIndex/rightSidebar', {
18
+ credentials: 'include'
19
+ });
20
+
21
+ if (!resp.ok) {
22
+ return {error: 'HTTP ' + resp.status, hint: 'Open www.thepaper.cn in browser first'};
23
+ }
24
+
25
+ let data;
26
+ try {
27
+ data = await resp.json();
28
+ } catch (e) {
29
+ return {error: 'Failed to parse response', hint: 'Thepaper API may have changed'};
30
+ }
31
+
32
+ if (!data || !data.data || !data.data.hotNews) {
33
+ return {error: 'No data returned', hint: 'Thepaper API may have changed'};
34
+ }
35
+
36
+ const items = data.data.hotNews.slice(0, maxCount).map((item, i) => ({
37
+ rank: i + 1,
38
+ id: item.contId,
39
+ title: item.name || '',
40
+ cover: item.pic || '',
41
+ hot: parseInt(item.praiseTimes) || 0,
42
+ url: 'https://www.thepaper.cn/newsDetail_forward_' + item.contId,
43
+ mobileUrl: 'https://m.thepaper.cn/newsDetail_forward_' + item.contId
44
+ }));
45
+
46
+ return {
47
+ count: items.length,
48
+ items
49
+ };
50
+ }