9.2 KiB
Script Plugin Development Guide (Generated by AI)
Script plugins are lightweight, single-file plugins that provide a simple way to extend Wox functionality. They are perfect for quick automation tasks, personal utilities, and learning plugin development.
Overview
Script plugins communicate with Wox using JSON-RPC over stdin/stdout. Each script is executed on-demand when a query is made, making them ideal for simple, stateless operations.
Getting Started
Creating a Script Plugin
Use the wpm
plugin to create a new script plugin:
wpm create <name>
Available templates:
python
- Python script templatejavascript
- JavaScript/Node.js script templatebash
- Bash script template
Script Plugin Structure
A script plugin consists of a single executable file with metadata defined in comments:
#!/usr/bin/env python3
# Required parameters:
# @wox.id my-calculator
# @wox.name My Calculator
# @wox.keywords calc
# Optional parameters:
# @wox.icon 🧮
# @wox.version 1.0.0
# @wox.author Your Name
# @wox.description A simple calculator plugin
# @wox.minWoxVersion 2.0.0
# Your plugin code here...
Metadata Parameters
Required Parameters
@wox.id
- Unique plugin identifier@wox.name
- Display name of the plugin@wox.keywords
- Trigger keywords (space-separated)
Optional Parameters
@wox.icon
- Plugin icon (emoji or file path)@wox.version
- Plugin version@wox.author
- Plugin author@wox.description
- Plugin description@wox.minWoxVersion
- Minimum required Wox version
JSON-RPC Communication
Script plugins communicate with Wox using JSON-RPC 2.0 protocol.
Request Format
Wox sends requests to your script via stdin:
{
"jsonrpc": "2.0",
"method": "query",
"params": {
"search": "user search term",
"trigger_keyword": "calc",
"command": "",
"raw_query": "calc 2+2"
},
"id": "request-id"
}
Response Format
Your script should respond via stdout:
{
"jsonrpc": "2.0",
"result": {
"items": [
{
"title": "Result: 4",
"subtitle": "2 + 2 = 4",
"score": 100,
"action": {
"id": "copy-result",
"data": "4"
}
}
]
},
"id": "request-id"
}
Available Methods
query Method
Handles user queries and returns search results.
Parameters:
search
- The search term entered by usertrigger_keyword
- The keyword that triggered this plugincommand
- Command if using plugin commandsraw_query
- The complete raw query string
action Method
Handles user selection of a result item.
Parameters:
id
- The action ID from the result itemdata
- The action data from the result item
Environment Variables
Script plugins have access to these environment variables:
WOX_DIRECTORY_USER_SCRIPT_PLUGINS
- Script plugins directoryWOX_DIRECTORY_USER_DATA
- User data directoryWOX_DIRECTORY_WOX_DATA
- Wox application data directoryWOX_DIRECTORY_PLUGINS
- Plugin directoryWOX_DIRECTORY_THEMES
- Theme directory
Action Types
Script plugins can return actions that Wox will handle:
open-url
Opens a URL in the default browser:
{
"action": "open-url",
"url": "https://example.com"
}
open-directory
Opens a directory in the file manager:
{
"action": "open-directory",
"path": "/path/to/directory"
}
Example: Simple Calculator
#!/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,
"action": {
"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))
More Examples
File Search Plugin
#!/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'"}'
Weather Plugin (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));
}
Best Practices
- Keep it Simple: Script plugins are best for simple, stateless operations
- Handle Errors: Always handle exceptions and return proper JSON-RPC responses
- Performance: Remember that scripts are executed for each query
- Security: Be careful with user input, especially when using
eval()
or executing commands - Testing: Test your script manually with JSON input before using in Wox
- Use Environment Variables: Leverage the provided WOX_DIRECTORY_* variables
- Validate Input: Always validate and sanitize user input
- Provide Meaningful Results: Use descriptive titles and subtitles
Debugging Tips
Manual Testing
Test your script manually:
# 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"}
Common Issues
- Script not executable: Run
chmod +x your-script.py
- JSON parsing errors: Validate your JSON output
- Timeout issues: Optimize your script for speed
- Missing shebang: Always include
#!/usr/bin/env python3
or similar
Limitations
- Execution Timeout: Scripts must complete within 10 seconds
- No Persistent State: Scripts are executed fresh for each query
- Limited API: No access to advanced Wox APIs like AI integration or settings UI
- Performance: Not suitable for high-frequency queries or complex operations
- No Settings UI: Cannot provide custom settings interface
Migration to Full-featured Plugin
If your script plugin grows complex, consider migrating to a full-featured plugin:
- Use Python SDK:
wox-plugin
- Use Node.js SDK:
@wox-launcher/wox-plugin
- Access to full Wox API
- Persistent state and better performance
- Support for settings UI and advanced features
- AI integration capabilities
- Custom preview support