脚本插件开发指南
脚本插件是轻量级的单文件插件,提供了一种扩展 Wox 功能的简单方法。它们非常适合快速自动化任务、个人实用程序和学习插件开发。
概览
脚本插件使用 JSON-RPC 通过标准输入/输出 (stdin/stdout) 与 Wox 通信。每个脚本在进行查询时按需执行,这使得它们非常适合简单的无状态操作。
想要快速上手可以参考这个示例脚本:https://gist.github.com/qianlifeng/82a2f748177ce47a900b4c4da3abfd28
快速开始
创建脚本插件
使用 wpm 插件创建一个新的脚本插件:
wpm create <name>可用模板:
python- Python 脚本模板javascript- JavaScript/Node.js 脚本模板bash- Bash 脚本模板
脚本插件结构
脚本插件由单个可执行文件组成,元数据在注释中定义为 JSON 对象:
#!/usr/bin/env python3
# {
# "Id": "my-calculator",
# "Name": "My Calculator",
# "Author": "Your Name",
# "Version": "1.0.0",
# "MinWoxVersion": "2.0.0",
# "Description": "A simple calculator plugin",
# "Icon": "emoji:🧮",
# "TriggerKeywords": ["calc"],
# "SettingDefinitions": [
# {
# "Type": "textbox",
# "Value": {
# "Key": "precision",
# "Label": "Decimal Precision",
# "Tooltip": "Number of decimal places to show",
# "DefaultValue": "2",
# "Style": {
# "Width": 100
# }
# }
# }
# ],
# "Features": [
# {
# "Name": "debounce",
# "Params": {
# "intervalMs": "300"
# }
# }
# ]
# }
# Your plugin code here...JSON 元数据块必须:
- 放置在文件开头的注释中(在 shebang 行之后)
- Python/Bash 使用
#,JavaScript 使用// - 包含具有所有元数据字段的完整 JSON 对象
元数据字段
必填字段
Id- 唯一插件标识符(建议使用 UUID 格式)Name- 插件显示名称TriggerKeywords- 触发关键字数组
可选字段
Icon- 插件图标(emoji:🧮,relative:path/to/icon.png,或绝对路径)Version- 插件版本(默认:"1.0.0")Author- 插件作者(默认:"Unknown")Description- 插件描述(默认:"A script plugin")MinWoxVersion- 最低要求的 Wox 版本(默认:"2.0.0")SettingDefinitions- 设置定义数组(见下文设置部分)Features- 插件功能数组(debounce, querySelection 等)Commands- 插件命令数组SupportedOS- 支持的操作系统数组(默认:所有平台)
插件设置
您可以定义用户可以在 Wox 设置 UI 中配置的设置。设置在 SettingDefinitions 数组中定义:
#!/usr/bin/env python3
# {
# "Id": "weather-plugin",
# "Name": "Weather",
# "TriggerKeywords": ["weather"],
# "SettingDefinitions": [
# {
# "Type": "textbox",
# "Value": {
# "Key": "api_key",
# "Label": "API Key",
# "Tooltip": "Your weather API key",
# "DefaultValue": "",
# "Style": {
# "Width": 400
# }
# }
# },
# {
# "Type": "select",
# "Value": {
# "Key": "units",
# "Label": "Temperature Units",
# "DefaultValue": "celsius",
# "Options": [
# {"Label": "Celsius", "Value": "celsius"},
# {"Label": "Fahrenheit", "Value": "fahrenheit"}
# ]
# }
# },
# {
# "Type": "checkbox",
# "Value": {
# "Key": "show_forecast",
# "Label": "Show 7-day forecast",
# "DefaultValue": "true"
# }
# }
# ]
# }支持的设置类型
- textbox - 单行或多行文本输入
- checkbox - 布尔复选框
- select - 下拉选择
- label - 仅显示文本标签
- head - 章节标题
- newline - 换行符
- table - 带有可编辑行的表格
在脚本中访问设置
设置会自动作为环境变量传递给脚本插件。每个设置都以 WOX_SETTING_ 为前缀,并且键转换为大写。
例如,如果您定义了一个键为 api_key 的设置,它将作为环境变量 WOX_SETTING_API_KEY 可用。
Python 示例:
import os
# Get setting value
api_key = os.getenv('WOX_SETTING_API_KEY', '')
enable_feature = os.getenv('WOX_SETTING_ENABLE_FEATURE', 'false')
output_format = os.getenv('WOX_SETTING_OUTPUT_FORMAT', 'json')
# Use the settings
if api_key:
print(f"Using API key: {api_key[:4]}...")JavaScript 示例:
// Get setting value
const apiKey = process.env.WOX_SETTING_API_KEY || "";
const enableFeature = process.env.WOX_SETTING_ENABLE_FEATURE === "true";
const outputFormat = process.env.WOX_SETTING_OUTPUT_FORMAT || "json";
// Use the settings
if (apiKey) {
console.log(`Using API key: ${apiKey.substring(0, 4)}...`);
}Bash 示例:
# Get setting value
API_KEY="${WOX_SETTING_API_KEY:-}"
ENABLE_FEATURE="${WOX_SETTING_ENABLE_FEATURE:-false}"
OUTPUT_FORMAT="${WOX_SETTING_OUTPUT_FORMAT:-json}"
# Use the settings
if [ -n "$API_KEY" ]; then
echo "Using API key: ${API_KEY:0:4}..."
fi其他环境变量:
脚本插件还可以访问这些环境变量:
WOX_PLUGIN_ID- 插件的唯一 IDWOX_PLUGIN_NAME- 插件的显示名称WOX_DIRECTORY_USER_SCRIPT_PLUGINS- 脚本插件存储目录WOX_DIRECTORY_USER_DATA- 用户数据目录WOX_DIRECTORY_WOX_DATA- Wox 应用程序数据目录WOX_DIRECTORY_PLUGINS- 插件目录WOX_DIRECTORY_THEMES- 主题目录
JSON-RPC 通信
脚本插件使用 JSON-RPC 2.0 协议与 Wox 通信。
请求格式
Wox 通过 stdin 向您的脚本发送请求:
{
"jsonrpc": "2.0",
"method": "query",
"params": {
"search": "user search term",
"trigger_keyword": "calc",
"command": "",
"raw_query": "calc 2+2"
},
"id": "request-id"
}响应格式
您的脚本应通过 stdout 响应:
{
"jsonrpc": "2.0",
"result": {
"items": [
{
"title": "Result: 4",
"subtitle": "2 + 2 = 4",
"score": 100,
"actions": [
{
"id": "copy-result",
"data": "4"
}
]
}
]
},
"id": "request-id"
}可用方法
query 方法
处理用户查询并返回搜索结果。
参数:
search- 用户输入的搜索词trigger_keyword- 触发此插件的关键字command- 如果使用插件命令,则为命令raw_query- 完整的原始查询字符串
action 方法
处理用户对结果项的选择。
参数:
id- 结果项中的操作 IDdata- 结果项中的操作数据
能力与限制
query仅收到search、trigger_keyword、command、raw_query,不会包含 selection 或查询环境数据。- 每次调用都会启动全新进程,超时 10 秒;如需复用请自行落盘缓存。
- 预览、tails、MRU 恢复、结果动态更新等功能仅在全功能插件中提供。
环境变量
脚本插件可以访问这些环境变量:
WOX_DIRECTORY_USER_SCRIPT_PLUGINS- 脚本插件目录WOX_DIRECTORY_USER_DATA- 用户数据目录WOX_DIRECTORY_WOX_DATA- Wox 应用程序数据目录WOX_DIRECTORY_PLUGINS- 插件目录WOX_DIRECTORY_THEMES- 主题目录
操作 (Actions)
脚本插件可以使用两种类型的操作:
操作格式
每个结果必须有一个 actions 字段,其中包含操作对象数组(即使只有一个操作)。
每个操作对象可以有:
id(必填): 操作标识符name(可选): UI 中的显示名称(默认为 "Execute")- 其他字段取决于操作类型(例如,
text用于剪贴板,url用于打开 URL)
示例 - 单个操作:
{
"title": "Copy text",
"actions": [
{
"name": "Copy to Clipboard",
"id": "copy-to-clipboard",
"text": "Hello World"
}
]
}示例 - 多个操作:
{
"title": "Multiple options",
"actions": [
{
"name": "Copy",
"id": "copy-to-clipboard",
"text": "Hello World"
},
{
"name": "Open URL",
"id": "open-url",
"url": "https://example.com"
}
]
}内置操作
内置操作由 Wox 自动处理。您可以直接在查询结果中使用它们,而无需在脚本中实现 action 方法。
重要:使用内置操作时,您不需要在脚本中实现 handle_action()。Wox 会自动处理该操作。action 方法仍会作为钩子被调用,但您可以简单地返回一个空结果。
copy-to-clipboard
将文本复制到剪贴板:
{
"title": "Copy this text",
"subtitle": "Click to copy",
"actions": [
{
"name": "Copy",
"id": "copy-to-clipboard",
"text": "Text to copy"
}
]
}open-url
在默认浏览器中打开 URL:
{
"title": "Open website",
"subtitle": "Click to open",
"actions": [
{
"name": "Open in Browser",
"id": "open-url",
"url": "https://example.com"
}
]
}open-directory
在文件管理器中打开目录:
{
"title": "Open folder",
"subtitle": "Click to open",
"actions": [
{
"name": "Open Folder",
"id": "open-directory",
"path": "/path/to/directory"
}
]
}notify
显示通知消息:
{
"title": "Show notification",
"subtitle": "Click to notify",
"actions": [
{
"name": "Notify",
"id": "notify",
"message": "Notification message"
}
]
}自定义操作
对于自定义操作,您需要在脚本中实现 action 方法:
def handle_action(params, request_id):
action_id = params.get("id", "")
action_data = params.get("data", "")
if action_id == "my-custom-action":
# Handle your custom action
return {
"jsonrpc": "2.0",
"result": {},
"id": request_id
}
# For built-in actions or unknown actions, return empty result
return {
"jsonrpc": "2.0",
"result": {},
"id": request_id
}注意:action 方法会针对所有操作(内置和自定义)作为钩子被调用。这允许您在需要时甚至为内置操作添加额外的逻辑。但是,对于内置操作,您可以简单地返回一个空结果,Wox 将自动处理它们。
示例:简单计算器
#!/usr/bin/env python3
# @wox.id simple-calculator
# @wox.name Simple Calculator
# @wox.keywords calc
import json
import sys
import re
def handle_query(params, request_id):
search = params.get('search', '').strip()
if not search:
return {
"jsonrpc": "2.0",
"result": {"items": []},
"id": request_id
}
try:
# Simple math evaluation (be careful with eval in real plugins!)
if re.match(r'^[0-9+\-*/().\s]+$', search):
result = eval(search)
return {
"jsonrpc": "2.0",
"result": {
"items": [{
"title": f"Result: {result}",
"subtitle": f"{search} = {result}",
"score": 100,
"actions": [
{
"id": "copy-result",
"data": str(result)
}
]
}]
},
"id": request_id
}
except:
pass
return {
"jsonrpc": "2.0",
"result": {"items": []},
"id": request_id
}
def handle_action(params, request_id):
# Handle copy action
return {
"jsonrpc": "2.0",
"result": {},
"id": request_id
}
if __name__ == "__main__":
request = json.loads(sys.stdin.read())
method = request.get("method")
params = request.get("params", {})
request_id = request.get("id")
if method == "query":
response = handle_query(params, request_id)
elif method == "action":
response = handle_action(params, request_id)
else:
response = {
"jsonrpc": "2.0",
"error": {"code": -32601, "message": "Method not found"},
"id": request_id
}
print(json.dumps(response))更多示例
文件搜索插件
#!/bin/bash
# @wox.id file-search
# @wox.name File Search
# @wox.keywords fs
# Read JSON-RPC request
read -r request
# Parse request
search=$(echo "$request" | jq -r '.params.search // ""')
id=$(echo "$request" | jq -r '.id')
if [ -z "$search" ]; then
echo '{"jsonrpc":"2.0","result":{"items":[]},"id":"'$id'"}'
exit 0
fi
# Search files
results=()
while IFS= read -r -d '' file; do
basename=$(basename "$file")
results+=("{\"title\":\"$basename\",\"subtitle\":\"$file\",\"score\":90,\"action\":{\"id\":\"open-file\",\"data\":\"$file\"}}")
done < <(find "$HOME" -name "*$search*" -type f -print0 2>/dev/null | head -z -10)
# Build JSON response
items=$(IFS=,; echo "${results[*]}")
echo '{"jsonrpc":"2.0","result":{"items":['$items']},"id":"'$id'"}'天气插件 (JavaScript)
#!/usr/bin/env node
// @wox.id weather-plugin
// @wox.name Weather
// @wox.keywords weather
const https = require("https");
function handleQuery(params, requestId) {
const search = params.search || "";
if (!search) {
return {
jsonrpc: "2.0",
result: { items: [] },
id: requestId,
};
}
// Mock weather data (replace with real API)
const weatherData = {
temperature: "22°C",
condition: "Sunny",
location: search,
};
return {
jsonrpc: "2.0",
result: {
items: [
{
title: `${weatherData.temperature} - ${weatherData.condition}`,
subtitle: `Weather in ${weatherData.location}`,
score: 100,
action: {
id: "show-details",
data: JSON.stringify(weatherData),
},
},
],
},
id: requestId,
};
}
// Main execution
const input = process.stdin.read();
if (input) {
const request = JSON.parse(input.toString());
const response = handleQuery(request.params || {}, request.id);
console.log(JSON.stringify(response));
}最佳实践
- 保持简单:脚本插件最适合简单的无状态操作
- 处理错误:始终处理异常并返回正确的 JSON-RPC 响应
- 性能:记住脚本是为每个查询执行的
- 安全:小心用户输入,尤其是在使用
eval()或执行命令时 - 测试:在 Wox 中使用之前,使用 JSON 输入手动测试您的脚本
- 使用环境变量:利用提供的 WOXDIRECTORY* 变量
- 验证输入:始终验证和清理用户输入
- 提供有意义的结果:使用描述性的标题和副标题
调试技巧
手动测试
手动测试您的脚本:
# Create test input
echo '{"jsonrpc":"2.0","method":"query","params":{"search":"test"},"id":"1"}' | ./your-script.py
# Expected output format
{"jsonrpc":"2.0","result":{"items":[...]},"id":"1"}常见问题
- 脚本不可执行:运行
chmod +x your-script.py - JSON 解析错误:验证您的 JSON 输出
- 超时问题:优化您的脚本速度
- 缺少 shebang:始终包含
#!/usr/bin/env python3或类似内容
局限性
- 执行超时:脚本必须在 10 秒内完成
- 无持久状态:脚本为每个查询重新执行
- API 有限:无法访问高级 Wox API,如 AI 集成
- 性能:不适合高频查询或复杂操作
- 设置访问:虽然您可以定义设置 UI,但访问设置值需要额外的实现(存储在文件中或使用环境变量)
迁移到全功能插件
如果您的脚本插件变得复杂,请考虑迁移到全功能插件:
- 使用 Python SDK:
wox-plugin - 使用 Node.js SDK:
@wox-launcher/wox-plugin - 访问完整的 Wox API
- 持久状态和更好的性能
- 支持设置 UI 和高级功能
- AI 集成能力
- 自定义预览支持