Host API
Extensions talk to the launcher over newline-delimited JSON on stdio (NDJSON), UTF-8 without BOM.
Handshake
Host → extension:
json
{"type":"hello","host":"1.1.37","dataDir":"C:\\...\\PCL"}Extension → host:
json
{"type":"ready","name":"Hello Go"}After that both directions use {"type": "...", "id": "...", "payload": {...}}. Requests carry an id and must be answered with the same id; the default timeout is 10 seconds.
Message types
Extension → host
| Type | Payload | Notes |
|---|---|---|
ready | {name} | Handshake done |
notify | {title, text, level} | info / warn / error |
log | {text} | Write to the extension log |
fs.read | {path} | Needs fs.read |
fs.write | {path, data} | Needs fs.write, base64 data |
http | {method, url, headers, body} | Needs net |
instances | {} | Instance list |
versions | {} | Version list |
launch | {instance, version} | Needs launch |
ui.page | {title, html} | Needs ui |
settings.get | {key} | Read a launcher setting |
settings.set | {key, value} | Write a launcher setting |
Host → extension
| Type | Payload | Notes |
|---|---|---|
hello | {host, dataDir, locale} | First message |
resp | {ok, data, error} | Response to a request |
event | {name, data} | launch.before, launch.after, instance.created, shutdown |
ping | {} | Every 30 s; answer with pong |
Permission failures look like:
json
{"type":"resp","id":"7","payload":{"ok":false,"error":"permission denied: net"}}Go example
go
package main
import tmlext "github.com/TYWStudio/TML/extensions/sdk/go"
// 业务入口:两个方法都可以留空
type ext struct{}
func (ext) Ready() {
tmlext.Log("info", "拓展已加载")
tmlext.Toast("Hello")
}
func (ext) OnEvent(e tmlext.Event) {
tmlext.Log("info", e.Type)
}
func main() { tmlext.Run(ext{}) }Rust example
rust
use tml_ext::{Event, Ext, Handler};
struct Ext0;
impl Handler for Ext0 {
fn ready(&mut self, ext: &mut Ext) {
ext.log("info", "拓展已加载");
ext.toast("Hello");
}
fn event(&mut self, ext: &mut Ext, ev: Event) {
ext.log("info", &ev.kind);
}
}
fn main() { tml_ext::run(Ext0); }Rules that save you pain
- Answer
hellowithreadyquickly; do heavy work later. - Keep the main loop reading stdin; push long tasks to a worker thread.
- Handle
shutdownand exit on your own. - Never print debug output to stdout — it is the protocol channel. Use
logor stderr.
Limits
- 1 MiB per message.
- 10 s per request.
- 4 concurrent requests per extension.
ui.pageHTML is sanitised: no<script>, noon*attributes, no external iframes.
