lilyco
Health Uyari
- License — License: MIT
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 6 GitHub stars
Code Gecti
- Code scan — Scanned 12 files during light audit, no dangerous patterns found
Permissions Gecti
- Permissions — No dangerous permissions requested
Bu listing icin henuz AI raporu yok.
让人类远离计算机,AI 可做任何事 —— 一个 struct 派生 CLI/TUI/Web/MCP 四端,天生 AI-callable:MCP 服务器 + 进度通知 + 采样桥(工具可反向调用 Agent 的 LLM)。一套代码,Windows/Linux/Android 全平台。
Lilyco
One struct. Four interfaces (CLI / TUI / Web / MCP). Zero boilerplate. Cross-platform (Windows / Linux / Android).
Lilyco is a Rust framework that generates CLI, TUI, Web UI, and a standard MCP server — from a single struct definition. Every app is an AI tool by default: agents (DeepSeek Harness, Claude Code, Cursor…) call it directly through MCP or JSON-stream. Same binary runs on Windows, Linux, and Android (Termux). You write the business logic once; the framework handles everything else.
Table of Contents
- Why Lilyco
- Quick Start
- Architecture
- Codegraph (for AI agents)
- Crate Reference
- Type -> Widget Mapping
- AI Integration
- Progress Protocol
- Examples
- DSH Integration
- Testing
- Installation
- Limitations & Roadmap
Why Lilyco
A typical Rust CLI tool needs about 200 lines of clap boilerplate before the first line of actual logic. Add a TUI? Another 400 lines. A web dashboard? A different codebase entirely. Want LLMs to call your tool? You're writing JSON Schema by hand.
Lilyco collapses all of this into a single #[derive]:
#[derive(App)]
#[app(about = "Compress image files", run = "compress")]
struct ImgCompress {
#[arg(about = "Input file", must_exist = true)]
input: PathBuf,
#[arg(about = "Quality 1-100", default = 75, range = 1..=100)]
quality: u8,
#[arg(about = "Output format", default = "jpeg")]
format: Format,
#[arg(about = "Dry run")]
dry_run: bool,
}
From this you get:
imgpress --input photo.jpg --quality 50 --format webp— CLI- Interactive TUI form with live command preview — TUI
- Browser-based form with SSE progress — Web
- Valid Anthropic/OpenAI tool definition — AI
imgpress --mcp— a standard MCP server any Agent can call (2024-11-05)
Same binary, four interfaces — the backend is chosen automatically by the environment.
Quick Start
Create a new project and add the dependencies:
cargo new imgpress && cd imgpress
cargo add lilyco serde serde_json image # 一个框架依赖即可(宏经 facade 解析路径)
Paste this into src/main.rs:
use std::path::PathBuf;
use std::time::Instant;
use image::{DynamicImage, GenericImageView};
use image::imageops::FilterType;
use lilyco::prelude::*;
// 1. Define your types
#[derive(Debug, ValueEnum)]
enum Format { Jpeg, Png, Webp }
#[derive(App)]
#[app(about = "Compress image files", run = "compress")]
struct ImgCompress {
#[arg(about = "Input image", must_exist = true)]
input: PathBuf,
#[arg(about = "Quality 1-100", default = 75, range = 1..=100)]
quality: u8,
#[arg(about = "Output format", default = "jpeg")]
format: Format,
#[arg(about = "Max width, 0 = no resize")]
width: u32,
#[arg(about = "Dry run")]
dry_run: bool,
}
// 2. Write your business logic
fn compress(app: &ImgCompress, ctx: &Context) -> Result<serde_json::Value, AppError> {
let start = Instant::now();
ctx.emit(Progress::Started { total: Some(3), message: None });
let data = std::fs::read(&app.input)?;
let img = image::load_from_memory(&data)
.map_err(|e| AppError::Runtime(format!("decode: {e}")))?;
ctx.tick(1, Some(3), "Resizing...");
let img = if app.width > 0 && app.width < img.width() {
let ratio = app.width as f64 / img.width() as f64;
let h = (img.height() as f64 * ratio) as u32;
img.resize_exact(app.width, h.max(1), FilterType::Lanczos3)
} else { img };
ctx.tick(2, Some(3), "Encoding...");
let out_path = app.input.with_file_name(format!("compressed.{}",
if matches!(app.format, Format::Jpeg) { "jpg" } else { "png" }));
img.save(&out_path).map_err(|e| AppError::Runtime(format!("save: {e}")))?;
ctx.tick(3, Some(3), "Done");
ctx.done(serde_json::json!({"output": out_path.to_string_lossy()}),
start.elapsed().as_millis() as u64);
Ok(serde_json::json!({"status": "ok"}))
}
// 3. Wire up — one line, four interfaces
fn main() {
lilyco::run::<ImgCompress>();
}
Run it:
$ cargo run -- --input photo.jpg --quality 50 --format webp
$ cargo run -- --schema # JSON Schema
$ cargo run -- --anthropic-tool # AI tool definition
$ cargo run -- --json-stream # Machine-readable progress
$ cargo run -- --gui # Web GUI (SSE progress)
$ cargo run -- --mcp # MCP stdio server (Agent-ready)
lilyco::run::<A>() 按环境自动选端(借鉴 mininterface 的接口工厂):
交互终端 → TUI;管道/脚本 → CLI;--gui → Web;--mcp → MCP。
TUI 起不来时自动回退 CLI,绝不裸崩。
Architecture
高内聚低耦合:执行语义只存在于 core(executor),四个后端只做"渲染/传输",
门面 lilyco 是唯一的组合根(依赖所有后端),用户只依赖 lilyco。
+----------------------------------------------------------+
| Your Struct #[derive(App)] |
+----------------------------------------------------------+
|
lilyco (facade):自动后端选择(显式参数 > LILYCO_UI > 探测)
|
+--------------+----------------+----------------+-----+
| lilyco-cli | lilyco-tui | lilyco-gui | lilyco-mcp
| clap | ratatui 表单 | axum + SSE | stdio JSON-RPC
| | | | tools/list·call
+--------------+----------------+----------------+-----+
| |
+-----------v------------------v-----------------+
| lilyco-core |
| App trait · CommandSchema · Registry(别名/隐藏) |
| executor(唯一执行宿主:参数→执行→进度事件) |
| Progress 协议 · Context · AppError |
+--------------------------------------------------+
Design Principles
- Type-driven:
bool-> checkbox,u8-> number input, custom enum -> dropdown. No manual widget mapping. - CLI-first: CLI is the most structured interface. TUI and Web are derived from the same schema.
- Progress as first-class citizen: Every interface understands
Progress::Tick/Log/Done. - One execution host:
core::executor是唯一的"参数→执行→进度事件"实现,
CLI / TUI / GUI / MCP 只渲染事件流,不再各自实现宿主循环(消灭了三份重复代码)。 - AI-native: 导出 LLM function-calling schema + 标准 MCP 服务器(
--mcp),Agent 直接调用。 - Facade 自动选端: 借鉴 mininterface 的接口工厂 —— 显式参数 >
LILYCO_UI> 自动探测,
TUI 起不来回退 CLI。 - Registry 动态注册: 借鉴 unilang —— 运行期注册命令(插件 / AI 动态注册 / REPL),
声明式 JSON 加载(Registry::register_from_json)。
Crate Reference
lilyco-core
The foundation. No UI dependencies.
use lilyco_core::prelude::*;
Core Traits
| Trait | Method | Purpose |
|---|---|---|
App |
schema() -> CommandSchema |
Returns the full command schema |
App |
from_args(&HashMap) -> Result<Self, AppError> |
Construct from parsed CLI/AI args |
App |
run(&self, &Context) -> Result<Value, AppError> |
Execute business logic |
Renderer |
render(&self, &CommandSchema) -> Output |
Convert schema to a UI representation |
ValueEnum |
variants() -> Vec<&str> |
All possible string values |
ValueEnum |
from_str(&str) -> Option<Self> |
Parse from string |
Core Types
| Type | Purpose |
|---|---|
CommandSchema |
Full command description: name, about, args, subcommands |
ArgSchema |
Single argument: name, about, kind, required, default |
ArgKind |
`Flag |
Progress |
`Started |
LogLevel |
`Debug |
Context |
Runtime: progress channel, cancel signal, output format |
OutputFormat |
`Human |
AppError |
`InvalidArg |
Registry |
运行期命令注册表:注册 / 别名 / 隐藏 / JSON 声明式加载 |
RegisteredCommand |
name + aliases + hidden + schema + handler |
Handler |
Fn(&Context, &Value) -> Result<Value, AppError>(统一执行入口) |
executor |
共享执行宿主:spawn(流式)/ execute(同步收集),保证事件流以 Done/Error 结尾 |
CommandSchema JSON Export
schema.to_json_schema() // JSON Schema (generic)
schema.to_openai_tool() // OpenAI function calling format
schema.to_anthropic_tool() // Anthropic tool use format
lilyco-macros
Proc macros for deriving boilerplate.
use lilyco_macros::{App, ValueEnum};
#[derive(App)]
Generates schema(), from_args(), and run(). Reads these attributes:
Struct-level:
| Attribute | Example | Purpose |
|---|---|---|
#[app(about = "...")] |
#[app(about = "Compress images")] |
Command description |
#[app(run = "fn")] |
#[app(run = "compress")] |
Wire up run() to a business-logic function |
Field-level:
| Attribute | Example | Purpose |
|---|---|---|
#[arg(about = "...")] |
#[arg(about = "Input file")] |
Argument description |
#[arg(default = expr)] |
#[arg(default = 75)] |
Default value |
#[arg(range = lo..=hi)] |
#[arg(range = 1..=100)] |
Number range |
#[arg(min = n)] |
#[arg(min = 0)] |
Min value |
#[arg(max = n)] |
#[arg(max = 255)] |
Max value |
#[arg(must_exist = bool)] |
#[arg(must_exist = true)] |
Path existence check |
#[derive(ValueEnum)]
Auto-converts PascalCase variants to snake_case strings:
#[derive(ValueEnum)]
enum Codec { H264, H265, Av1 }
// -> variants: ["h264", "h265", "av1"]
// -> from_str("h265") -> Some(Codec::H265)
Type Inference
| Rust Type | Inferred ArgKind |
required |
|---|---|---|
bool |
Flag |
false |
String |
Text |
true |
u8/i32/f64/... |
Number |
true |
PathBuf |
Path |
true |
Option<T> |
same as T |
false |
Vec<T> |
List { item: infer(T) } |
true |
Custom enum |
Enum |
true |
lilyco-cli
Generates a clap::Command from CommandSchema. Adds built-in flags automatically.
let schema = MyTool::schema();
let renderer = lilyco_cli::CliRenderer::new();
let cmd = renderer.render(&schema);
let matches = cmd.get_matches();
Built-in Flags (auto-added to every command)
| Flag | Behavior |
|---|---|
--schema |
Print JSON Schema and exit |
--openai-tool |
Print OpenAI function definition and exit |
--anthropic-tool |
Print Anthropic tool definition and exit |
--json |
OutputFormat::Json |
--json-stream |
OutputFormat::JsonStream (one JSON per line) |
Public API
impl CliRenderer {
fn new() -> Self;
fn render(&self, schema: &CommandSchema) -> clap::Command;
fn handle_builtin_flags(schema: &CommandSchema, matches: &ArgMatches) -> bool;
fn output_format(matches: &ArgMatches) -> OutputFormat;
fn extract_args(schema: &CommandSchema, matches: &ArgMatches)
-> HashMap<String, serde_json::Value>;
}
多命令(Registry → clap 子命令)
一个二进制挂多个命令,子命令名 / 别名 / 隐藏语义全部来自 Registry:
let mut registry = Registry::new();
registry.register(RegisteredCommand::from_app::<Compress>())?;
registry.register(RegisteredCommand::from_app::<Resize>())?;
lilyco_cli::run_registry("imgtool", registry); // crate 入口
lilyco::run_cli_registry("imgtool", registry); // 门面入口
imgtool compress --input a.png # 子命令
imgtool resize --width 800 # 子命令
imgtool --schema # 注册表清单(全部命令 schema,Agent 可消费)
#[derive(App)]默认用结构体名做命令名;多命令场景建议#[app(name = "img-compress")]指定 kebab-case 名。
lilyco-tui
Interactive terminal form built on ratatui.
Transcode -- Transcode video files
$ transcode --input video.mp4 --codec h265 --quality 18
---------------------------------------------------------
(*) input: [video.mp4________________________]
codec: [h264] h265 [Av1] <->
quality: [18] ^v
dry_run: [x] Space
---------------------------------------------------------
[Tab] Switch [Enter] Confirm [Esc] Quit [F1] Help
Widget Behaviors
| ArgKind | Key | Behavior |
|---|---|---|
| Flag | Space |
Toggle on/off |
| Text | Type + Backspace |
Edit text |
| Number | ^ v |
+/-1(自动夹在 schema 范围内). Type digits to edit(越界由提交校验拦截) |
| Enum | < > |
Cycle through options |
| Path | Type + Tab |
目录补全:循环候选(目录带 / 后缀);空值时 Tab 切换字段 |
| List | Enter / Delete |
Add/remove item |
State Machine
[多命令] CommandSelect --Enter--> Form --Enter--> Confirm --Enter--> Running --done--> Done
^ | |
| | |
+------<--- Done/Error 任意键返回(单命令退出)
^ | | |
| Esc | |
+---------------+ v v
Error <---------- Enter
CLI Preview
The bottom bar shows a live CLI command preview that updates as you edit values. It auto-omits:
falseflags (e.g.,--dry-runonly appears when checked)- Values matching their defaults
- Empty optional fields
- Path values are auto-quoted if they contain spaces
lilyco-gui
Web server with embedded HTML, similar to Gradio in spirit.
let gui = lilyco_gui::GuiRenderer::new(8080);
gui.serve(schema, Arc::new(|args| Box::pin(async move {
// process args, return result
Ok(serde_json::json!({"status": "ok"}))
}))).await;
+-------------------------------------+
| ImgCompress -- Compress images |
| |
| Input: [___________________] |
| Quality: [75_______________] |
| Format: [jpeg v] |
| Width: [0________________] |
| Dry run: [ ] |
| |
| [> Run] [Copy CLI] |
| |
| $ imgcompress --quality 75 |
+-------------------------------------+
| Output |
| ████████░░░░░░░ 50% |
| Encoding frame 50/100 |
| Done in 1.2s |
+-------------------------------------+
Flow: Form POST -> spawn task -> SSE stream -> progress bar + log
lilyco (facade)
一个依赖搞定四端。用户代码只依赖这一个 crate,后端按环境自动选择。
use lilyco::prelude::*;
fn main() {
lilyco::run::<ImgCompress>(); // 自动选端
// lilyco::run_with::<ImgCompress>(Backend::Mcp); // 显式指定
}
| 触发方式 | 后端 |
|---|---|
--mcp |
MCP stdio 服务器(Agent 直接调用) |
--gui / --web |
Web GUI |
LILYCO_UI=cli|tui|web|mcp |
环境变量强制 |
交互终端 + TERM |
TUI 表单(起不来自动回退 CLI) |
| 其余(管道 / CI / 脚本) | CLI(--json-stream 供 AI 消费) |
多命令场景(Registry → 四端导航):
lilyco::serve_mcp(registry); // MCP:整个注册表 = tools/list
lilyco::run_cli_registry("app", registry); // CLI:注册表 → clap 子命令
lilyco::run_tui_registry("app", registry); // TUI:命令选择页 → 表单(回退 CLI)
| 触发方式(单命令) | 后端 |
|---|---|
--mcp |
MCP stdio 服务器(Agent 直接调用) |
--gui / --web |
Web GUI |
LILYCO_UI=cli|tui|web|mcp |
环境变量强制 |
交互终端 + TERM |
TUI 表单(起不来自动回退 CLI) |
| 其余(管道 / CI / 脚本) | CLI(--json-stream 供 AI 消费) |
lilyco-mcp
把命令注册表暴露为标准 Model Context Protocol 服务器(2024-11-05),
实现 initialize / ping / tools/list / tools/call,零额外依赖。tools/call 携带 _meta.progressToken 时,执行期间流式返回notifications/progress(Progress::Started/Tick → 通知),长任务对 Agent 不再是黑盒。
let mut registry = Registry::new();
registry.register(RegisteredCommand::from_app::<MyTool>())?;
lilyco_mcp::McpServer::new(registry).serve_stdio()?;
核心是纯函数 handle_line(一行请求 → 一行响应),serve 可挂任意 Read + Write,
协议逻辑全部可单元测试。
lilyco-ultra-ui
Experimental JSON-to-React declarative UI generator. Write a Chinese-language JSON spec; get a full React frontend — no Rust code required.
use lilyco_ultra_ui::UltraUiServer;
#[tokio::main]
async fn main() {
UltraUiServer::new(9090).serve().await;
}
The JSON spec uses Chinese field names for an Excel-like feel:
{
"窗口": {
"标题": "My App",
"大小": "中等",
"元素": [
{ "类型": "标题", "内容": "Welcome" },
{ "类型": "文本输入", "标签": "Name", "占位符": "Enter name..." },
{ "类型": "数字", "标签": "Quantity", "最小值": 0, "最大值": 100 },
{ "类型": "按钮", "文本": "Submit", "样式": "primary" },
{ "类型": "进度", "标签": "Progress" }
]
}
}
Supported Element Types
| Type (Chinese) | English | Description |
|---|---|---|
文本 |
Text | Static text block |
标题 |
Heading | H1-H4 heading |
按钮 |
Button | Clickable button with style variants |
文本输入 |
Text Input | Single-line text input |
数字 |
Number | Numeric input with min/max |
下拉 |
Select | Dropdown select |
复选框 |
Checkbox | Boolean toggle |
多行文本 |
Textarea | Multi-line text input |
图片 |
Image | Image display |
分割线 |
Divider | Visual separator |
进度 |
Progress | Progress bar |
链接 |
Link | Hyperlink |
计算器 |
Calculator | Built-in calculator widget |
Type -> Widget Mapping
| Rust Type | CLI | TUI | Web |
|---|---|---|---|
bool |
--flag |
[x] Space toggle |
<input type=checkbox> |
String |
--name <val> |
text input | <input type=text> |
u8/i32/f64/... |
--count <num> |
^v +/-1 + digit input | <input type=number> |
| Custom enum | --mode <choice> |
<-> cycle | <select> |
PathBuf |
--file <path> |
text input | <input type=text> |
Vec<T> |
--tag a --tag b |
Enter/Delete multi-line | dynamic inputs |
Option<T> |
optional | optional (not required) | optional |
AI Integration
Every Lilyco app is an AI tool:
$ imgpress --anthropic-tool
{
"name": "ImgCompress",
"description": "Compress image files",
"input_schema": {
"type": "object",
"properties": {
"input": { "type": "string", "description": "Input image file" },
"quality": { "type": "number", "minimum": 1, "maximum": 100, "description": "Quality" },
"format": { "type": "string", "enum": ["jpeg", "png", "webp"], "description": "Format" },
"dry_run": { "type": "boolean", "description": "Dry run" }
},
"required": ["input"]
}
}
This is a valid Anthropic tool-use definition. Drop it into your Claude API call, and the model can invoke your Rust tool directly.
$ imgpress --openai-tool # Chat Completions 格式(嵌套 function)
$ imgpress --openai-responses-tool # Responses API 格式(扁平,strict:false)
$ imgpress --openai-strict-tool # strict mode(结构化输出:剥约束关键词 +
# additionalProperties:false + 全字段 required)
$ imgpress --gemini-tool # Gemini functionDeclarations(OpenAPI 子集)
$ imgpress --anthropic-tool # Anthropic tool_use
$ imgpress --schema # Generic JSON Schema (for other LLMs)
$ imgpress --mcp # 标准 MCP 服务器:Agent 直接调用(含进度通知)
更进一步 —— 采样桥(HostBridge):MCP 形态下,工具执行中途可以反向调用
Agent 客户端的 LLM(sampling/createMessage),让"工具用上模型"而不只是
"模型用工具":
fn run(app: &VisionTool, ctx: &Context) -> Result<serde_json::Value, AppError> {
let caption = ctx.sample("描述这张图片的内容", 256)?; // 反向采样
ctx.done(serde_json::json!({ "caption": caption }), 0);
Ok(serde_json::Value::Null)
}
客户端未声明 sampling 能力时返回带指引的错误(审批权始终在客户端手里)。
$ imgpress --json-stream # Each Progress event as one JSON line — ideal for agent consumption
### MCP Server(AI 调用的事实标准)
```bash
$ imgpress --mcp
lilyco-mcp 把命令注册表暴露为标准 MCP stdio 服务器(协议 2024-11-05)。
任何支持 MCP 的 Agent(Claude Desktop、Cursor、OpenHands 等)都可以直接调用你的 Rust 工具:
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}
{"jsonrpc":"2.0","id":2,"method":"tools/list"}
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"ImgCompress","arguments":{"input":"photo.jpg","quality":50}}}
相比手写 --anthropic-tool / --openai-tool 单次 schema,MCP 是标准化的长连接协议,
一次 tools/list 拿全量工具定义,tools/call 直接执行并返回结构化结果。
AI Agent Consumption Pattern
{"type":"started","total":5,"message":"Loading photo.jpg..."}
{"type":"tick","current":1,"total":5,"message":"Reading input file","percent":0.2}
{"type":"tick","current":2,"total":5,"message":"Original: 4000x3000","percent":0.4}
{"type":"tick","current":3,"total":5,"message":"Encoding...","percent":0.6}
{"type":"tick","current":4,"total":5,"message":"Writing compressed.jpg","percent":0.8}
{"type":"done","result":{"output_size":142000,"compression_ratio":35.5},"duration_ms":1200}
Progress Protocol
Every interface consumes the same Progress events:
ctx.emit(Progress::Started { total: Some(100), message: Some("Starting...".into()) });
for i in 0..=100 {
if ctx.is_cancelled() { return Err(AppError::Cancelled); }
ctx.tick(i, Some(100), format!("Processing frame {i}"));
}
ctx.log(LogLevel::Info, "Compression complete");
ctx.done(serde_json::json!({"size_mb": 4.2}), 3200);
| Interface | Started |
Tick |
Log |
Done |
|---|---|---|---|---|
CLI (--json-stream) |
JSON line | JSON line with percent | JSON line | JSON line + exit |
| CLI (Human) | -- | \r progress line |
[INFO] line |
summary + exit |
| TUI | Progress bar at 0% | Bar fills + message | Scroll log | Result screen |
| Web | SSE: bar at 0% | SSE: bar fills | SSE: log append | SSE: result JSON |
Examples
Image Compressor (lilyco-example)
cd lilyco-example
cargo run -- --input photo.jpg --quality 50 --format webp
cargo run -- --input photo.jpg --dry-run --json
cargo run -- --schema
See lilyco-example/src/main.rs for the full source (~230 lines).
Grep (lilyco-grep)
Simple recursive grep — the DSH ecosystem test vehicle:
cargo run -p lilyco-grep -- --pattern hello --path src
cargo run -p lilyco-grep -- --pattern TODO --path . --ignore-case --count
cargo run -p lilyco-grep -- --pattern hello --path src --json-stream # AI 消费
cargo run -p lilyco-grep -- --pattern hello --path src --mcp # MCP 服务器
Brush (lilyco-brush)
brush(bash 兼容 shell)用 lilyco 重写 —— 给 AI 的 shell 工具:
lbrush --command "x=1; echo $x; ls | wc -l" # CLI
lbrush --command "sleep 5" --timeout-secs 1 # 超时 kill
lbrush --command "ls" --json-stream # AI 消费(JSONL)
lbrush --mcp # MCP 服务器
- 每次调用全新 shell(
--no-config),非零退出码不是工具错误(结构化返回exit_code) - DSH 接入:
lilyco-brush/dsh/cordis.patch.yml(dsh-mcp-client stdio 直连,模型看到mcp__lbrush__Brush) - CI 产出
lbrush-windowsartifact,本机不装 Rust 也能拿二进制
Android(Termux)
lbrush 跨平台,Android 用 headless 构建(CLI + MCP):
# CI artifact:lbrush-android-arm64;或本地交叉编译:
cargo build -p lilyco-brush --no-default-features --features android --target aarch64-linux-android
- headless 关掉 TUI/Web 后端(crossterm 的
cfg(unix)不含 android;lilycofacade 按 feature 门控) - shell 解析自动命中 Termux bash(
/data/data/com.termux/files/usr/bin/bash) - Termux 里直接跑:
./lbrush --command "echo hi",或./lbrush --mcp给 AI agent 连
Vision Toolkit (lilyco-vision)
DSH Vision Toolkit 的 Rust 重写 —— 8 个本地视觉操作,Registry 注册 + --mcp 给 DSH 提供视觉:
lvision --list # 打印全部工具 schema
lvision --mcp # MCP 服务器(8 个原生工具)
| 工具 | 功能 | 依赖 |
|---|---|---|
ImageInfo |
尺寸 / 格式 / 大小 | image |
Crop |
像素框裁剪 + 缩放(LANCZOS) | image |
Resize |
等比缩放 | image |
DominantColors |
主色提取(贪心聚类 + 容差) | image(自研算法) |
PixelDiff |
网格级像素差异排行 + 热力图 | image(自研算法) |
ExtractForeground |
前景抠图(边界泛洪,透明 PNG) | image(自研算法) |
Trace |
位图矢量化 → SVG | vtracer |
HtmlScreenshot |
HTML → PNG(headless Chrome/Edge) | 浏览器 |
- 全部本地计算,无 Python 运行时(原版是 Pillow+numpy+vtracer 的 uv 环境)
- DSH 接入:
lilyco-vision/dsh/cordis.patch.yml(模型看到mcp__lvision__*工具) - CI 产物:
lvision-windows/lvision-android-arm64 - 服务类工具(glance/ground/detect/OCR)依赖外部视觉服务,v1 不做
FFmpeg Transcode (lilyco-ffmpeg)
lffmpeg —— ffmpeg 包装:转码 / 缩放 / 裁剪,实时进度 + 取消,四端 + AI 可调:
cargo binstall lilyco-ffmpeg # 免编译安装(见下)
lffmpeg --input a.mp4 --output b.mp4 --codec h265 --crf 28 # CLI
lffmpeg --input a.mp4 --output b.mp4 --width 1280 # 缩放(高度自动等比)
lffmpeg --input a.mp4 --output clip.mp4 --start 10 --duration 5 # 裁剪
lffmpeg --input a.mp4 --output b.mp4 --json-stream # AI 消费(JSONL 事件流)
lffmpeg --mcp # MCP 服务器
- 实时进度:解析 ffmpeg
-progress pipe:1;ffprobe(可选)算完成百分比,缺失降级不确定进度 - 取消:CLI
Ctrl-C、TUICtrl-C/c/q/Esc,kill 正在运行的 ffmpeg - 非零退出不是工具错误:结构化返回
exit_code+ stderr 摘要 - cargo binstall:
[package.metadata.binstall]pkg-url = "https://github.com/lilyco-42/lilyco/releases/download/v{ version }/{ name }-{ target }{ binary-ext }",pkg-fmt = "bin"—— 打 tag 时 CI 发布lffmpeg-x86_64-pc-windows-msvc.exe/lffmpeg-aarch64-linux-android,{ target }全量 triple 供 binstall 匹配。crate 已发布 crates.io,故cargo binstall lilyco-ffmpeg可简写;未发布预编译资产时自动回退源码安装 - 完整用法:见
docs/lffmpeg.md - 依赖系统
ffmpeg(必须在 PATH 上)
Transcode (TUI demo)
use lilyco_macros::{App, ValueEnum};
use lilyco_core::prelude::*;
use std::path::PathBuf;
#[derive(ValueEnum)]
enum Codec { H264, H265, Av1 }
#[derive(App)]
#[app(about = "Transcode video files")]
struct Transcode {
#[arg(about = "Input file", must_exist = true)]
input: PathBuf,
#[arg(about = "Codec", default = "h264")]
codec: Codec,
#[arg(about = "Quality 0-51", default = 23, range = 0..=51)]
quality: u8,
}
Ultra UI (lilyco-ultra-ui-example)
cargo run -p lilyco-ultra-ui-example
# Open http://localhost:9090 in your browser
Edit the JSON spec in the browser; the React UI updates in real time.
DSH Integration
DeepSeek Harness 接入(实测验证):lilyco 应用以 MCP 服务器形态挂进 dsh,模型直接获得原生工具。
原理
dsh 的能力扩展单元是 cordis 插件;外部 Rust 二进制经官方 @deepseek-ai/dsh-mcp-client 插件桥接(spawn 进程 + 注册 ctx.tools),模型看到 mcp__<server>__<tool> 原生工具。
一键接入
curl -fsSL https://raw.githubusercontent.com/lilyco-42/lilyco/main/install.sh | bash
脚本自动:下载 release 二进制 → 安装 dsh-mcp-client 插件 → 写 profile patch。重启 dsh web 后,模型获得:
| 服务器 | 工具 |
|---|---|
mcp__lbrush |
Brush(真 bash 执行器:变量/管道/重定向/&&/` |
mcp__lvision |
Crop / Resize / DominantColors / PixelDiff / ExtractForeground / Trace / HtmlScreenshot / ImageInfo |
已知坑(实测)
- 首轮不可见:若 profile 使用了 router-flash 类 agent preset(首轮 core 工具过滤),新会话第一轮看不到 mcp 工具 —— 让模型先调用任意一次工具,下一轮全目录放开。
- 插件无
dsh.bundle时是 plain dependency,patch insert 显式引用其 name 即可加载。 - 加/改插件后必须重启 dsh web(可用
tasklist验证lbrush-windows.exe --mcp子进程确认插件已连接)。
Testing
本地:
# Run all tests
cargo test --workspace
# Run a specific crate
cargo test -p lilyco-core
cargo test -p lilyco-cli
cargo test -p lilyco-tui
cargo test -p lilyco-macros
cargo test -p lilyco-ultra-ui
cargo test -p lilyco-mcp
cargo test -p lilyco-vision
CI(GitHub Actions):push / PR 自动跑 ubuntu + windows 双矩阵 —cargo fmt --check + cargo clippy --workspace --all-targets + cargo test --workspace + cargo doc;
打 tag v* 自动构建 4 个二进制(Windows + Android-arm64)并发布 GitHub Release。
见 .github/workflows/ci.yml。Windows TUI 从此由 CI 持续验证编译与单元测试。
Current coverage: 241 tests across all crates (ubuntu + windows 双平台) + 端到端冒烟(examples/multi.rs)+ 性能基准(cargo bench -p lilyco-example).
Installation
From crates.io (published)
[dependencies]
lilyco = "0.2" # 推荐:一个依赖搞定四端
lilyco-core = "0.2" # derive(App) 宏展开需要
From git
[dependencies]
lilyco = { git = "https://github.com/lilyco-42/lilyco" }
lilyco-core = { git = "https://github.com/lilyco-42/lilyco" }
Limitations & Roadmap
Current Limitations
#[derive(App)]only works on named-field structs (no tuple structs or enums)#[app(run = "fn")]requires the function to be in scope. Without this attribute,run()panics with a helpful message directing you to add it.- Input validation is shared:
CommandSchema::validate_args(required / number range / enum / path must-exist) runs server-side in MCP (INVALID_PARAMS) and Web GUI (400), and pre-submit in TUI; CLI validates via clap at parse time - Subcommands are supported in CLI only — TUI and Web renderers do not handle them yet
- Ultra UI is experimental — JSON spec format may change
Roadmap
已完成(v0.1 → v0.2.x)
- Real
run()dispatch in Web GUI with progress streaming — done viaGuiRenderer::serve_app::<A>() -
#[app(run = "fn")]macro attribute — wire business logic with zero boilerplate - Integration tests that exercise all three interfaces end-to-end — 12 tests in
lilyco-example - 共享执行宿主 —
core::executor,CLI/TUI/GUI/MCP 同一执行路径 - 运行期命令注册表 —
core::Registry(别名 / 隐藏 / JSON 声明式加载) - MCP 输出面 —
lilyco-mcp:--mcp启动标准 stdio 服务器 - 门面自动选端 —
lilyco::run::<A>()(借鉴 mininterface 工厂) - CI 双矩阵 — GitHub Actions ubuntu + windows:fmt / clippy / test / doc
- CLI 多命令:注册表 → clap 子命令 —
lilyco_cli::run_registry(name, registry)/ 门面lilyco::run_cli_registry;隐藏命令可调用不显示,根级--schema打印注册表清单 - MCP 进度通知 —
tools/call携带_meta.progressToken时流式返回notifications/progress(零依赖实现) - MCP 采样 / roots — 零依赖实现(不引 rust-sdk):
HostBridge(core)+ 双向 JSON-RPC 分流(serve);handler 经ctx.sample()反向调用 Agent 的 LLM(sampling/createMessage),ctx.roots()获取宿主工作根;客户端能力门控 +srv-N字符串请求 id + 超时保护 - Subcommand navigation in TUI and Web GUI — TUI 命令选择页(
TuiApp::new_multi+run_tui_registry,mininterface subcommand picker 模式;隐藏命令不显示);Web GUIserve_registry+?cmd=切换下拉;/run显式执行未知命令 400 - Input validation in TUI/Web widgets (range, required, enum) — 共享校验
CommandSchema::validate_args:MCPtools/call前置校验(INVALID_PARAMS)、Web GUI 服务端 400 + 浏览器required/min/max、TUI 提交前拦截并红色提示;TUI 数字 ↑↓ 夹紧到 schema 范围 - TUI 执行异步化(进度渲染 + 取消) — 非阻塞事件循环;取消键
Ctrl-C/c/q/Esc;executor 自动补发Done/Error终止事件(防卡死) - Path auto-complete in TUI (Tab triggers directory listing) — Path 字段有输入时 Tab 触发目录补全,重复 Tab 循环候选(目录带
/后缀);空值时 Tab 照常切换字段;兼容/与\分隔符 -
#[app(name = "...")]macro attribute — 多命令场景自定义 kebab-case 命令名(默认取结构体名) -
#[app(subcommands)]macro support - TUI 执行异步化(进度渲染 + 取消)
- Publish to crates.io —
lilyco-core/macros/cli/tui/gui 0.2.1、lilyco-mcp/lilyco 0.2.0、lilyco-ffmpeg 0.1.0(旧 core 0.1.0/0.2.0 因缺executor/registry已 yank) - Performance benchmarks for schema generation — 零依赖基准
cargo bench -p lilyco-example(schema 生成 / JSON 导出 / validate_args / Registry 装配,release 实测 0.03–3 µs/op)
License
MIT OR Apache-2.0, at your option.
Yorumlar (0)
Yorum birakmak icin giris yap.
Yorum birakSonuc bulunamadi