355 lines
5.2 KiB
Markdown
355 lines
5.2 KiB
Markdown
|
|
# 📘 **ZDTT Plugin Development Guide**
|
|||
|
|
|
|||
|
|
*Create safe, powerful extensions for the ZDTT Terminal.*
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🟦 Introduction
|
|||
|
|
|
|||
|
|
ZDTT supports a secure, sandboxed plugin system that allows developers to extend the terminal with custom commands.
|
|||
|
|
|
|||
|
|
Plugins live inside:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
~/.zdtt/plugins/
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
A plugin is simply a Python file that:
|
|||
|
|
|
|||
|
|
1. Contains **no top-level executable code**
|
|||
|
|
2. Defines functions or classes
|
|||
|
|
3. Defines a mandatory function:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def register_commands():
|
|||
|
|
return { "command": callable }
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Plugins are automatically loaded when ZDTT starts or when you run:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
plugins reload
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 🛡️ Plugin Security Model
|
|||
|
|
|
|||
|
|
ZDTT has strict safety validation to protect users from malicious plugins.
|
|||
|
|
Every plugin must pass **six security layers**:
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## **1. AST Validation**
|
|||
|
|
|
|||
|
|
At load time, ZDTT parses the plugin using Python’s AST.
|
|||
|
|
|
|||
|
|
Allowed at the top level:
|
|||
|
|
|
|||
|
|
* Imports
|
|||
|
|
* From-imports
|
|||
|
|
* Function definitions
|
|||
|
|
* Class definitions
|
|||
|
|
* A file-level docstring
|
|||
|
|
|
|||
|
|
Forbidden (**causes quarantine**):
|
|||
|
|
|
|||
|
|
* Print statements
|
|||
|
|
* Assignments
|
|||
|
|
* Function calls
|
|||
|
|
* Loops
|
|||
|
|
* Try/except blocks
|
|||
|
|
* Any executable code
|
|||
|
|
* Anything that runs automatically
|
|||
|
|
|
|||
|
|
Example of an unsafe plugin:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
print("hacked!") # ❌ this will be quarantined
|
|||
|
|
os.system("rm -rf /") # ❌ also quarantined
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Unsafe plugins are moved to:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
~/.zdtt/quarantine/
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## **2. Import Trust Prompt**
|
|||
|
|
|
|||
|
|
Plugins may import modules:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
import os
|
|||
|
|
import subprocess
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
However:
|
|||
|
|
**Any plugin with imports triggers a trust confirmation.**
|
|||
|
|
|
|||
|
|
If the user does **not** trust the plugin, it is quarantined.
|
|||
|
|
|
|||
|
|
If the user approves, the plugin name is added to:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
config.json → trusted_plugins
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Only trusted plugins may use `__import__`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## **3. Sandboxed Execution**
|
|||
|
|
|
|||
|
|
Plugins run inside a restricted environment:
|
|||
|
|
|
|||
|
|
* Only safe builtins are exposed
|
|||
|
|
* Dangerous builtins (like `exec` or `eval`) are blocked
|
|||
|
|
* Imports only work for trusted plugins
|
|||
|
|
* Code cannot escape the sandbox
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## **4. register_commands() Validation**
|
|||
|
|
|
|||
|
|
Every plugin must define:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def register_commands():
|
|||
|
|
return {"name": function}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
ZDTT verifies:
|
|||
|
|
|
|||
|
|
* The function exists
|
|||
|
|
* The return value is a dict
|
|||
|
|
* All values are callable
|
|||
|
|
* Command names are valid strings
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## **5. Protected Command Names**
|
|||
|
|
|
|||
|
|
Plugins **cannot override** important commands such as:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
ssh, sudo, su, cp, mv, rm, ls, cat,
|
|||
|
|
chmod, chown, history, zps, zdtt,
|
|||
|
|
pip, python, python3, curl, wget
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Attempting to override them causes quarantine.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## **6. Runtime Registration**
|
|||
|
|
|
|||
|
|
Once the plugin passes all previous checks, ZDTT adds its commands to the shell environment.
|
|||
|
|
|
|||
|
|
Example:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
{
|
|||
|
|
"weather": cmd_weather,
|
|||
|
|
"hello": cmd_hello
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
These appear in autocomplete and `help`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 🟩 Plugin Structure
|
|||
|
|
|
|||
|
|
A minimal valid plugin:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
"""
|
|||
|
|
My Plugin
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def cmd_test(args):
|
|||
|
|
print("Hello from ZDTT plugin!")
|
|||
|
|
|
|||
|
|
def register_commands():
|
|||
|
|
return {"test": cmd_test}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 🟧 Arguments
|
|||
|
|
|
|||
|
|
ZDTT passes command arguments as a list.
|
|||
|
|
|
|||
|
|
Example:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
hello world how are you
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Results in:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
cmd_hello(["world", "how", "are", "you"])
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 🟪 Example Plugin (Official)
|
|||
|
|
|
|||
|
|
From `example_plugin.py`:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
"""
|
|||
|
|
Example ZDTT Plugin
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
import subprocess
|
|||
|
|
import os
|
|||
|
|
|
|||
|
|
def cmd_hello(args):
|
|||
|
|
...
|
|||
|
|
|
|||
|
|
def cmd_weather(args):
|
|||
|
|
...
|
|||
|
|
|
|||
|
|
def cmd_sysinfo(args):
|
|||
|
|
...
|
|||
|
|
|
|||
|
|
def register_commands():
|
|||
|
|
return {
|
|||
|
|
"hello": cmd_hello,
|
|||
|
|
"weather": cmd_weather,
|
|||
|
|
"sysinfo": cmd_sysinfo
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
All three commands use argument lists exactly as ZDTT passes them.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 🟫 Installing Plugins
|
|||
|
|
|
|||
|
|
### **Manual Install**
|
|||
|
|
|
|||
|
|
Place plugin file in:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
~/.zdtt/plugins/
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Then reload:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
plugins reload
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### **Install via ZPS**
|
|||
|
|
|
|||
|
|
Install directly from a URL:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
zps install https://raw.githubusercontent.com/user/repo/plugin.py
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
ZPS will:
|
|||
|
|
|
|||
|
|
* Download the file
|
|||
|
|
* Save it to your plugin directory
|
|||
|
|
* Warn if it already exists
|
|||
|
|
* Ask if you trust imports (if any)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 🟨 Debugging Plugins
|
|||
|
|
|
|||
|
|
ZDTT logs plugin errors to:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
~/.zdtt/plugin_errors.log
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Quarantined plugins appear in:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
~/.zdtt/quarantine/
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Reload plugins:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
plugins reload
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 🟩 Best Practices for Plugin Developers
|
|||
|
|
|
|||
|
|
✔ Wrap all code inside functions
|
|||
|
|
✔ Avoid imports unless required
|
|||
|
|
✔ Validate arg lists
|
|||
|
|
✔ Use try/except around risky operations
|
|||
|
|
✔ Keep commands short and simple
|
|||
|
|
✔ Document your commands
|
|||
|
|
✔ Never modify global state
|
|||
|
|
✔ Don’t override protected commands
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 🟦 Advanced Plugin Tips
|
|||
|
|
|
|||
|
|
### Show usage/help
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def cmd_calc(args):
|
|||
|
|
"""Usage: calc <expression>"""
|
|||
|
|
...
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
ZDTT automatically pulls these docstrings into the `help` system.
|
|||
|
|
|
|||
|
|
### Combine args into a string
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
expr = " ".join(args)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Use subprocess safely
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
try:
|
|||
|
|
subprocess.run(["ls"])
|
|||
|
|
except Exception as e:
|
|||
|
|
print("Error:", e)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Read files safely
|
|||
|
|
|
|||
|
|
Use try/except to avoid crashing the shell.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 🟣 Full Plugin Template
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
"""
|
|||
|
|
Plugin Name: <your plugin>
|
|||
|
|
Description: <what it does>
|
|||
|
|
Author: <you>
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
# Optional imports (will require trust prompt)
|
|||
|
|
import subprocess
|
|||
|
|
|
|||
|
|
def cmd_example(args):
|
|||
|
|
print("Example plugin working!")
|
|||
|
|
|
|||
|
|
def register_commands():
|
|||
|
|
return {
|
|||
|
|
"example": cmd_example
|
|||
|
|
}
|
|||
|
|
```
|