# 环境搭建 省略 # 初始文件树 `cargo init --bin --name tz .` #文件树1 ``` TZ/ ├── Cargo.toml ├── Cargo.lock └── src/ ├── main.rs # tz 可执行程序入口 └── lib.rs # 项目的主要代码入口 ``` ## 常用代码 ```bash # 检查代码,不生成最终程序 cargo check # 编译 cargo build # 编译并运行 cargo run # 向 tz 传入参数 cargo run -- status cargo run -- core list # 运行测试 cargo test # 格式代码 cargo fmt # 静态检查 cargo clippy # 添加依赖 cargo add clap --features derive # 删除依赖 cargo remove clap # 查看依赖树 cargo tree # 查看项目元数据 cargo metadata --format-version 1 ``` # CLI 解析层的第一个小闭环 1. 添加clap依赖 ```bash cargo add clap --features derive ``` `cargo add` 会修改当前 Package 的 `Cargo.toml`;`derive` feature 让我们可以使用 `#[derive(Parser)]` 和 `#[derive(Subcommand)]`。 2. 写文件功能 #文件树2 ```bash mkdir -p src/cli touch src/cli/mod.rs # 管理并导出 cli 模块 touch src/cli/args.rs # 定义用户可以输入什么 ``` ``` src/ ├── main.rs # 程序入口 ├── lib.rs # 整个项目的库模块入口 └── cli/ ├── mod.rs # 管理并导出 cli 模块 └── args.rs # 定义用户可以输入什么 ``` 3. `lib.rs`中添加声明 ```rust pub mod cli; ``` 声明项目中存在 `cli` 模块,Rust 会自动寻找`src/cli.rs`或者`src/cli/mod.rs` 4. `src/cli/mod.rs`中管理子模块 ```rust mod args; // cli 模块内部还有一个 args 子模块。 pub use args::{Cli, CliCommand, CoreCommand}; // 表示把三个类型重新导出到 cli 模块表面 ``` 没有写 `pub`,所以外部不能直接访问`args`/`tz::cli::args::Cli`; 然后补充[args.rs](../src/cli/args.rs)即可,注释也写在里面 即使`main.rs`和`lib.rs`在同一个 Cargo package 中, 仍然是两个独立 crate。Rust 官方文档明确说明:同时存在 `src/lib.rs` 和 `src/main.rs` 时,package 中包含一个库 crate 和一个二进制 crate。 ```tcl package tz ├── library crate tz │ └── cli └── binary crate tz └── main ``` 5. 补充main.rs ```rust use clap::Parser; use tz::cli::Cli; // cli 当前是由 lib.rs 管理的,它属于库 crate tz,不是 main.rs 所属二进制 crate 的直接模块。 fn main() { let cli = Cli::parse(); println!("{cli:#?}"); } ``` 6. 测试,`--`表示隔开,后面的参数是输入给二进制文件的 ```bash cargo run -- --help # 查看帮助 cargo run -- status # cargo run -- core --help # 查看 core 的帮助 cargo run -- unknown # 测试错误输入 ``` ## 常用代码 ```bash cargo tree # 检查依赖 cargo tree -i clap cargo fmt # 统一代码格式 cargo check # 类型检查和编译检查 cargo clippy # 检查潜在问题和不规范写法 ``` # CLI 解析run态 1. 新增命令分发层 #文件树3 ```bash src/ ├── main.rs ├── lib.rs ├── cli/ │ ├── mod.rs │ ├── args.rs # 用户可以使用什么命令 │ └── commands/ # 定义“收到命令之后调用什么” │ ├── mod.rs │ ├── status.rs │ ├── service.rs │ └── core.rs ``` 创建 ```bash mkdir -p src/cli/commands touch src/cli/commands/mod.rs touch src/cli/commands/status.rs touch src/cli/commands/service.rs touch src/cli/commands/core.rs ``` 2. 补充command代码 补充 [src/cli/mod.rs](../src/cli/mod.rs) 补充 [src/cli/commands/mod.rs](../src/cli/commands/mod.rs) 补充 [src/cli/commands/status.rs](../src/cli/commands/status.rs) 补充 [src/cli/commands/service.rs](../src/cli/commands/service.rs) 补充 [src/cli/commands/core.rs](../src/cli/commands/core.rs) 补充 [src/main.rs](../src/main.rs) 3. 测试 ```bash cargo fmt cargo check cargo clippy ``` 然后 ```bash cargo run -- status cargo run -- start cargo run -- stop cargo run -- restart cargo run -- core list ``` ``` 之前: 用户输入 → Debug 打印 现在: 用户输入 ↓ Cli ↓ CliCommand ↓ match ↓ 具体 command handler ``` # application ```bash mkdir -p src/application touch src/application/mod.rs touch src/application/service.rs ``` 文件树4 ````bash src/ ├── main.rs # 程序入口 ├── lib.rs # 整个项目的库模块入口 └── cli/ │ ├── mod.rs # 管理并导出 cli 模块 │ ├── args.rs # 定义用户可以输入什么 │ └── commands/ # 定义“收到命令之后调用什么” │ ├── mod.rs # commands 本体 │ ├── status.rs # commands 的 子模块 │ ├── service.rs │ └── core.rs │ └── application/ ├── mod.rs └── service.rs # 这个替换commands/service.rs的服务 ```` 随后需要补充的 `lib.rs`中定义新的模块 之后将commands中的相关模块替换成 # 前部小结 这里主要是创建两个模块 cli : 指令列表,以及管理\\调用 application : 真实实现 目前只是搭建了基础框架没有实际执行, 1. 搭建cli 2. 搭建application,改cli的调用路线为application 主要完成了指令的分发 # 路径系统 设定软件的路径系统, ```bash mkdir -p src/platform touch src/platform/mod.rs touch src/platform/paths.rs ``` 文件树5 ```bash src/ ├── main.rs # 程序入口 ├── lib.rs # 整个项目的库模块入口 └── cli/ │ ├── mod.rs # 管理并导出 cli 模块 │ ├── args.rs # 定义用户可以输入什么 │ └── commands/ # 定义“收到命令之后调用什么” │ ├── mod.rs # commands 本体 │ ├── status.rs # commands 的 子模块 │ ├── service.rs │ └── core.rs │ └── application/ │ ├── mod.rs │ └── service.rs # 这个替换commands/service.rs的服务 │ └── platform/ ├── mod.rs └── paths.rs ``` 要求: - 默认配置不依赖环境变量即可使用。 - 路径配置统一保存在 `paths.toml`,由 `TZ_PATHS_TOML` 指向;未设置时使用 `$HOME/.config/tz/paths.toml`。 - `tz init` 可以在用户确认后把 `TZ_PATHS_TOML` 的 export 追加到 `~/.bashrc`。 ## 文件系统初始化规则 `paths.toml` 是文件系统路径的唯一配置源,文件只保存一次选定的四个目录,不保存布局类型或多个方案: ```toml [layout] config_dir = "~/.config/tz" data_dir = "~/.local/share/tz" state_dir = "~/.local/state/tz" cache_dir = "~/.cache/tz" ``` 路径文件定位规则: 1. 设置 `TZ_PATHS_TOML` 时,读取该绝对路径文件;文件不存在或格式错误直接报错,不回退。 2. 未设置时,读取 `$HOME/.config/tz/paths.toml`。 3. 默认路径文件不存在时,提示运行 `tz init`。 程序固定读取 `[layout]` 下的四个字段。路径可以写成绝对路径或 `~/...`;读取时展开 `~`,展开后必须是绝对路径。普通命令不再读取 `TZ_ROOT_DIR` 或四个 `XDG_*` 环境变量。 `tz init` 的首次选择提供三个模板: 1. 默认 XDG:`~/.config/tz`、`~/.local/share/tz`、`~/.local/state/tz`、`~/.cache/tz`。 2. 默认 Unified:`~/.tz/config`、`~/.tz/data`、`~/.tz/state`、`~/.tz/cache`。 3. 开发测试:项目目录下的 `target/tz-dev/{config,data,state,cache}`。 选择模板后,交互询问四个目录,回车保留模板值,也可以修改。最终只写入一个 `[layout]` 表。 重复运行 `tz init` 时,如果 paths 文件已存在,先询问是否继续;拒绝则退出且不修改文件,确认后才重新选择和写入。写入路径文件后,初始化根据四个目录补齐结构和初始文件,已有文件一律跳过,不覆盖内容。 ## `tz init` 交互 - 第一次运行时选择三个内置模板:默认 XDG、默认 Unified、开发测试 `target/tz-dev`。 - 选择后分别询问 `config_dir`、`data_dir`、`state_dir`、`cache_dir`,回车使用模板值。 - 如果 paths 文件已经存在,先询问是否继续;默认退出,不修改现有路径。 - 初始化结束后始终打印 `export TZ_PATHS_TOML='...'` 提示。默认 `$HOME/.config/tz/paths.toml` 不需要设置变量;自定义 paths 文件可以确认后追加到 `~/.bashrc`。 ## 开发测试 开发测试模板使用项目目录下的 `target/tz-dev`,示例: ```bash cargo run -- init # 选择 3) 开发测试 target/tz-dev TZ_PATHS_TOML="$PWD/target/tz-paths.toml" cargo run -- status ``` 如果 paths 文件放在默认 `$HOME/.config/tz/paths.toml`,后续命令不需要设置环境变量;如果使用自定义路径文件,需要设置 `TZ_PATHS_TOML` 或按初始化结束时的提示加入 bashrc。 ## 本次修改的代码文件 - `src/platform/paths.rs` - 使用 `paths.toml` 作为唯一路径配置源。 - 支持 `TZ_PATHS_TOML` 和默认 `$HOME/.config/tz/paths.toml`。 - 解析 `[layout]` 的四个固定目录,支持 `~/` 展开和绝对路径校验。 - 保留初始化目录、五个初始文件和幂等补全。 - `src/application/init.rs` - 提供默认 XDG、默认 Unified、开发 `target/tz-dev` 三个模板。 - 已有 paths 文件先确认,再允许重新选择。 - 初始化结束打印 `TZ_PATHS_TOML` export;自定义路径可确认后追加到 `~/.bashrc`。 验证命令: ```bash cargo fmt --check cargo check cargo test --all-targets cargo clippy --all-targets -- -D warnings ``` # 文件作用 接下来固定文件作用 `paths.toml` TZ_PATHS_TOML ```toml [layout] config_dir = "/mnt/data_4t2/lht_self/TZ/target/tz-dev/config" data_dir = "/mnt/data_4t2/lht_self/TZ/target/tz-dev/data" state_dir = "/mnt/data_4t2/lht_self/TZ/target/tz-dev/state" cache_dir = "/mnt/data_4t2/lht_self/TZ/target/tz-dev/cache" ``` ## 三级配置 ### 一级配置 > 全局、长期、比较稳定的信息。 `settings.toml`:tz软件的统一配置信息,与core的运行不太相关。默认不会修改,只是展示一下 ```toml schema_version = 1 [bypass] enabled = true # bypass.list 之外的内联补充项,生成规则时合并并去重。 inline = [ "localhost", "127.0.0.0/8", ] [log] level = "warn" # tz.log 超限后直接清除重建,不保留归档。 max_size_mb = 10 [update.profiles] auto_update = false interval_minutes = 4320 [update.cores] auto_update = false interval_minutes = 14400 ``` `runtime.toml`:TZ 可以跨 core 表达的运行参数,包括端口、API、DNS 和 TUN 的复杂参数。是否开启 TUN 不在这里,开关保存在 `active.toml`。 ```toml schema_version = 1 [proxy] mode = "rule" listen = "127.0.0.1" mixed_port = 7890 http_port = 7892 socks_port = 7891 allow_lan = false ipv6 = false [api] enabled = true listen = "127.0.0.1" port = 9189 [dns] enabled = true listen = "127.0.0.1" port = 1053 ipv6 = false [tun] stack = "system" auto_route = true auto_detect_interface = true dns_hijack = true ``` ### 二级配置 `active.toml`:保存主页需要展示和切换的状态,只放当前 core 与三个开关。当前 profile 和节点选择由 `profiles.toml` 保存,PID 与锁由 `state/runtime/` 保存。 ```toml schema_version = 1 [current] core = "mihomo" [tun] enabled = false [shell_proxy] enabled = false bypass = true [system_proxy] enabled = false bypass = true ``` ### 三级配置 `state/generated//` 只保存根据配置生成的内核入口文件,可随时删除重建。`state/runtime//` 才是 `{workdir}` 指向的内核工作目录,用来隔离 cache.db 等副产物。 ## 介绍文档 > 除了上面的一些控制文档,还需要有一些总结性的文档,避免每次都重新扫描文件。 `profiles.toml` ```toml schema_version = 1 # 每个 family 各自保存当前 profile,切换 core 后可以恢复对应选择。 [current] clash = "home" sing-box = "sid" [[profiles]] name = "home" family = "clash" format = "yaml" source_file = "home/source.yaml" [profiles.origin] kind = "remote" url = "https://example.com/subscription" [profiles.update] updated_at = "2026-08-14T10:00:00Z" # 一个 profile 可以保存多个策略组的节点选择。 [profiles.state.selected] Proxy = "Hong Kong 01" Final = "Proxy" [[profiles]] name = "company" family = "clash" format = "yaml" source_file = "company/source.yaml" [profiles.origin] kind = "local" original_path = "/home/lht/config/company.yaml" ``` # cores标准设计 > 下面的不需要tz程序初始化,也不需要其修改,基本上手动制作,tz读取使用即可 ```bash cores/ └── mihomo/ ├── core.toml # Core 描述文件 └── mihomo # 二进制 ``` 这一部分还作为后续cores制作的参考标准 `core.toml` ```toml schema_version = 1 [core] name = "mihomo" family = "clash" version = "1.19.18" binary = "mihomo" os = "linux" arch = "x86_64" [runtime] entrypoint = "config.yaml" format = "yaml" [capabilities.config] mixed_proxy = true http_proxy = true socks_proxy = true api = true dns = true tun = true # start 必填;check/version/reload 为可选表。 # 某个命令表存在就表示支持对应 CLI 动作,不再维护重复的 actions 布尔值。 [commands.start] args = ["-d", "{workdir}", "-f", "{config}"] [commands.check] args = ["-t", "-d", "{workdir}", "-f", "{config}"] [commands.version] args = ["-v"] ``` `core.toml` 只允许 `{config}` 和 `{workdir}` 两个占位符。`binary` 与 `entrypoint` 必须是单个相对文件名;加载 core 时还会检查 schema、目录名、family/format 组合、二进制是否存在且可执行。 ## 最终文件树 > 这里是结合文件作用+cores的 ```toml TZ_PATHS_TOML └── paths.toml # 保存四个基础路径 config/ ├── settings.toml # TZ 自身的长期策略:bypass、日志、更新配置 ├── runtime.toml # 跨 core 的端口、API、DNS、TUN 参数 ├── shell/ # 后续生成 shell 代理脚本;当前阶段尚未实装 └── bypass.list data/ ├── profiles/ │ ├── profiles.toml # profile 索引、当前选择和策略组节点选择 │ ├── tnt/ │ │ └── source.yaml │ └── sid/ │ └── source.json │ └── cores/ # 手工制作、TZ 只读;目录名必须等于 core.name ├── mihomo/ │ ├── core.toml │ └── mihomo └── sing-box/ ├── core.toml └── sing-box state/ ├── active.toml # 当前 core 与主页开关 │ ├── generated/ # 只放可删除重建的生成配置 │ ├── mihomo/ │ │ └── config.yaml │ └── sing-box/ │ └── config.json │ ├── runtime/ │ ├── mihomo/ # Mihomo cache.db 等运行副产物,{workdir} 指向这里 │ ├── sing-box/ │ ├── tz.lock # 防止多个 TZ 同时修改运行状态 │ └── core.pid # 停止时还必须通过 /proc//exe 校验身份 │ └── logs/ ├── tz.log └── core.log cache/ ├── downloads/ └── speedtest/ ``` # 配置层 domain 与命令实装 ## 本次目标 把“文件作用 + cores 标准”定稿落到 Rust:用 domain 结构体描述 `settings.toml`、`runtime.toml`、`active.toml`、`profiles.toml` 和 `core.toml`,由 `tz init` 生成默认文件,由 `tz status` 与 `tz core list` 严格读取。配置 builder 和真实进程启停留到下一阶段。 ## 代码结构 `src/domain/` 只负责配置结构、序列化和业务校验;`application` 负责行为;`platform` 负责路径: ``` src/ ├── domain/ │ ├── mod.rs # domain 类型重导出 │ ├── settings.rs # settings.toml:[bypass]/[log]/[update] │ ├── runtime.rs # runtime.toml:[proxy]/[api]/[dns]/[tun] │ ├── active.rs # active.toml:当前 core + 三个主页开关 │ ├── profiles.rs # profiles.toml:当前 profile、索引、节点选择 │ └── core_manifest.rs # core.toml 加载、校验和 list_cores ├── platform/paths.rs # 四个根目录和固定子路径 ├── application/ │ ├── init.rs # 生成默认配置 │ └── service.rs # status 与未实现启停错误 └── cli/commands/ # CLI 薄分发 ``` ## 配置职责与校验 - `settings.toml` 保存 TZ 自身的长期策略。日志超限直接清除重建,因此没有 `keep` 字段。 - `runtime.toml` 保存端口、API、DNS 与 TUN 复杂参数;默认端口为 mixed `7890`、HTTP `7892`、SOCKS `7891`。 - `active.toml` 只保存 `[current].core`、`tun.enabled`、shell proxy 和 system proxy 开关。 - `profiles.toml` 在顶层 `[current]` 按 family 保存当前 profile;`[profiles.state.selected]` 可以保存多个策略组各自选择的节点。 - `state/generated//` 只保存可删除重建的入口配置;`state/runtime//` 是 `{workdir}`,用于隔离内核数据库和缓存。 - 四份配置都只支持 `schema_version = 1` 并拒绝未知字段。`Default` 只供 `tz init` 生成模板;普通命令严格读取,文件缺失、损坏或版本不支持都会报错。 - profile 会校验名称、family/format、来源字段、重复项、当前选择引用和 `source_file` 安全相对路径。 ## cores 标准 - `core.name` 必须等于目录名,名称只能使用 ASCII 字母、数字、点、下划线和连字符。 - `family/format` 当前支持 `clash/yaml` 与 `sing-box/json`。 - `binary` 和 `runtime.entrypoint` 必须是单个相对文件名;binary 还必须实际存在且可执行。 - `commands.start` 必填;`commands.check`、`commands.version`、`commands.reload` 可选。命令表存在即表示支持对应动作,不再使用重复的 `[capabilities.actions]` 布尔值。 - 命令参数只支持 `{config}` 和 `{workdir}`,由 `CoreDescriptor::render_args` 展开。 - `[capabilities.config].api` 只表示内核提供控制 API,不等同于支持 CLI reload。 ## paths.rs 与初始化 - `paths.toml` 仍是四个根目录的唯一来源;设置 `TZ_PATHS_TOML` 时必须是绝对路径。 - `AppPaths::from_env_or_none()` 让非 init 命令自己输出未初始化提示,不再提前重复解析路径。 - `initialize_files` 只建目录并幂等写 `bypass.list`;domain 默认值负责生成四份 TOML,已有文件不覆盖。 - 初始化先准备目录与默认配置,最后写 `paths.toml`,避免配置生成失败后留下已提交的路径文件。 - `generated_dir` 与 `core_workdir` 分离;没有独立的 `active.example.toml`。 ## 当前命令行为 - `tz init`:选择模板,确认四个路径,准备目录和默认配置,最后写 `paths.toml`,再提示 `TZ_PATHS_TOML` export。 - `tz status`:显示当前 core、profile、受管进程和当前节点;PID 检查会排除僵尸进程。 - `tz core list`:扫描 `data/cores/*/core.toml`,通过完整校验后按名称排序,并在 TTY 中允许直接选择。 - `tz start`、`tz stop`、`tz restart`:生成并校验配置后真实控制受管进程;停止前核对进程用户和可执行文件。 `stop` 不会仅凭 PID 发送信号;`/proc//exe` 与当前受管 core 不一致时直接拒绝。 ## 验证 ```bash cargo fmt --check cargo check cargo test --all-targets cargo clippy --all-targets -- -D warnings TZ_PATHS_TOML="$PWD/target/tz-paths.toml" cargo run -- status TZ_PATHS_TOML="$PWD/target/tz-paths.toml" cargo run -- core list ``` # 控制逻辑界面 TZ 的公开控制入口围绕对象和业务动作组织,不提供任意 TOML 编辑器。配置文件是内部存储格式,所有写操作都经过类型校验、运行状态检查、锁保护和原子保存。 ## 当前命令 ```bash tz init tz status tz setting tz setting list tz setting get tz setting set [value] tz setting reset [key] tz profile add --family clash|sing-box tz profile list [--family clash|sing-box] [--all] tz profile info tz profile use [name] tz profile update tz profile remove tz core list tz core info [name] tz core use [name] tz core remove tz completion generate bash|zsh|fish # 生成tab服务 ``` `tz setting` 无子命令时在 TTY 中进入选择界面;非交互调用使用 `list`。`set` 缺少 value 时只允许 TTY 交互。`start`、`stop`、`restart` 当前明确返回未实现错误,不打印成功状态。 ## 简明指令 ```bash tz start|on # 直接读取上一次的配置 tz off|stop|end # 关闭服务,清理环境 tz -l # 列出节点 tz -l # 不区分大小写的关键词搜索 tz completion generate bash|zsh|fish # 生成 shell 补全脚本 # eval "$(tz completion generate bash)" ``` ## 快捷键 ```bash tz # tz status ,需要展示当前使用的core,profile,节点及其测速 tz select # tz profile list ``` ## 状态归属 - `settings.toml` 与 `runtime.toml` 由 `tz setting` 的固定 key registry 管理,禁止任意路径写入。 - `active.toml` 只由 core 选择等业务命令维护,`tz status` 负责展示,不提供通用字段编辑。 - `profiles.toml` 与受管 profile source 由 `tz profile` 管理;远程来源保存 URL 和实际下载路径,下载优先使用环境代理,失败后回退直连。 - `profile list` 默认按当前 core 的 family 过滤;只有 `--all` 才显示全部 family。 - `core.toml` 与二进制由 `tz core` 只读校验和管理;PID、锁、日志属于运行状态。 - generated 配置由当前 family builder 生成并调用真实 core 校验;节点选择、测速、真实启停、TUN 独立开关及 shell/GNOME system proxy 已开放。 ## 修改规则 1. 命令先重新读取并校验当前状态,再持有 `tz.lock` 执行修改。 2. 文件使用同目录临时文件和原子替换;失败时保留旧状态。 3. profile/core 在受管进程运行时拒绝 `use`、`update`、`remove` 等可能改变运行输入的操作。 4. profile URL 只允许 HTTP(S),校验公网地址、DNS 全部结果和每次重定向;本地文件复制为受管副本。 5. core 只接受本地目录,不接受 URL;导入成功后不自动选择、不自动启动。 # core 制作 ## 目标与范围 TZ 当前支持统一的本地 core 包格式,用于识别和调用已经存在的 Mihomo 或 sing-box 二进制。core 包只描述运行契约,不包含 profile、用户配置、secret、PID、日志或缓存。 当前稳定槽位为 `mihomo` 和 `sing-box`,目录名必须与 `core.name` 相同: ```text data/cores/ ├── mihomo/ │ ├── core.toml │ └── mihomo └── sing-box/ ├── core.toml └── sing-box ``` ## 当前 schema v1 `core.toml` 必须声明 schema、名称、family、版本、二进制、目标平台、配置格式、能力和命令参数。Mihomo 使用 `family = "clash"`、`format = "yaml"`,sing-box 使用 `family = "sing-box"`、`format = "json"`。命令参数只支持 `{config}` 和 `{workdir}` 占位符,由 TZ 展开。完整字段以 [`docs/core-package.md`](core-package.md) 为准。 ```toml schema_version = 1 [core] name = "mihomo" family = "clash" version = "1.19.18" binary = "mihomo" os = "linux" arch = "x86_64" [runtime] entrypoint = "config.yaml" format = "yaml" [commands.start] args = ["-d", "{workdir}", "-f", "{config}"] ``` ## 当前制作和导入流程 1. 用户自行下载或制作二进制,在本地准备包含 `core.toml` 和可执行文件的目录。 2. 使用 `tz core add ` 导入;TZ 校验目录、manifest、平台、family、格式、二进制权限和命令占位符。 3. 如 manifest 声明 `commands.version`,导入前执行该命令;参数按数组传递,不经过 shell。 4. 目标名称已存在时拒绝覆盖;通过 staging 目录复制并原子重命名到 `data/cores/`。 5. 导入成功后不自动 `core use`,也不自动启动;后续用 `tz core list/info/use/remove` 管理。 `core remove` 在运行中拒绝,删除前检查当前选择和受管 PID,并清理该 core 的派生配置。手工复制到 `data/cores/` 也会被扫描,但不会绕过运行状态和安全校验。 ## 当前不做 core 不通过 URL 安装、不负责下载更新、不覆盖正在运行的 core。远端 registry、归档校验和 core update 不在当前路线内。任意其他代理二进制也不能只凭一份 `core.toml` 接入,必须先提供对应 family 的配置 builder 和控制 adapter。 ## 本轮实现结果 - 新增 `platform/storage.rs`:`tz.lock` 非阻塞独占锁、同目录临时文件、flush/fsync、原子 rename;profile 索引和 source 使用当前用户私有权限。 - 新增 `platform/process.rs`:统一正整数 PID、存活和僵尸状态判断,供 status、profile/core use/remove 共用。 - 新增 `platform/network.rs`:profile URL 只允许 HTTP/HTTPS,拒绝凭据、本机、环回、私有和保留地址;DNS 全结果与重定向逐次复核,优先使用环境代理并在失败后回退直连,限制超时、重定向和响应大小,错误不泄露订阅 URL。 - 新增 typed setting registry,完成 `setting/list/get/set/reset`;不允许任意 TOML 路径编辑,保存后明确提示需要重新 build/start。 - 完成 profile add/list/info/use/update/remove:URL 与本地文件都变成受管副本,family 固定映射格式,运行中禁止 use/update/remove,删除不触碰用户原文件。 - core schema v1 新增必填 os/arch,稳定槽位统一为 `mihomo`、`sing-box`。 - 完成 core add/list/info/use/remove:手工复制仍可直接扫描,add 只做本地安全导入,version 命令不经过 shell,运行中禁止 use/remove。 - 新增正式规范 `docs/control-interface.md`、`docs/core-package.md` 和 `examples/cores/mihomo/` 制作模板。 ## 当前边界与下一阶段 当前路线已完成 config builder、节点选择与测速、受管进程启停、TUN 独立控制及 shell/GNOME system proxy。core 在线更新与其他桌面环境 adapter 留在后续阶段。 # v0.1 可运行闭环 本章覆盖当前可运行行为;前文中的阶段性设计以这里和 `docs/control-interface.md` 为准。 ## 当前指令 ```bash tz status|start|stop|restart tz list [keyword] tz node test [keyword] [--url ] [--timeout ] [--select] tz tun status|on|off tz proxy status|on|off tz proxy terminal|system status|on|off tz proxy env|noenv [bash|zsh|fish] tz proxy shell-init bash|zsh|fish tz setting [list|get|set|reset] tz profile add|list|info|use|update|remove tz core add|list|info|use|remove tz config build|check|show tz completion generate bash|zsh|fish ``` `profile update` 不接名称,一次更新全部远程 profile。`profile list` 默认只看当前 core family,只有 `--all` 跨 family。profile/core/node 的 list 在终端中均可编号选择并以 `*` 标记当前项;`use` 保留给脚本和显式操作。列表保持简洁,路径和来源等详情由 `info` 或对应文件提供。 ```text nano_clash family=clash * mihomo version=1.19.18 family=clash ``` Mihomo 读取 Clash 配置,所以其 family 必须为 `clash`;sing-box 才使用 `family=sing-box`。 ## 简洁指令 ```bash tz # status,并测速当前节点 tz on # 使用上次的可用 profile 启动并显示 status tz off | tz end # stop tz -l [keyword] # 节点测速、延迟排序、搜索和选择 tz select # 当前 family 的 profile 列表和选择 ``` ## 快捷键 ```bash tz st # status tz r # restart tz set # setting tz p # profile tz c # core tz cfg # config tz comp # completion tz p a|l|i|u|up|rm tz c a|l|i|u|rm ``` Tab 提示由 completion generator 提供。节点选择会写入 profile state 并在下次启动后恢复。`tz -l` 和 `node test` 都走 controller delay API,最多 8 路并发、按延迟排序并缓存最新结果;`--select` 选择最快节点。`tz` 对当前节点实时测速,失败时才显示缓存结果。 Mihomo 标准 core 包携带 `Country.mmdb` 与 `GeoSite.dat`。Clash profile 实际引用 GEOIP/GEOSITE 时,builder 按需复制到该 core 的独立 runtime 工作目录,避免首次校验因 GitHub 下载受阻而误报超时;自制 core 包缺失资源时直接提示用户启用其他代理或补齐 core 包。`tz on/start` 始终使用当前 family 上次选择且 source 可用的 profile,启动后立即显示含节点测速的 status。 profile 下载的 User-Agent 按 family 选择:Clash 对齐 `mh` 的 Mihomo provider User-Agent,sing-box 对齐 `sb`。同一订阅 URL 若按客户端返回 Clash YAML 或 sing-box JSON,`add` 与批量 `update` 都能取得对应 family 的原生格式;`--family` 仍只负责选择契约,不做跨格式转换。 ## Proxy、TUN 与权限 - `proxy env/noenv` 输出可由当前 shell `eval/source` 的环境命令;`shell-init` 为 Bash、Zsh、Fish 生成持久 hook。 - `proxy system` 按 mh/sb 的当前参考路线使用 GNOME `gsettings`,读取 runtime/capability 端口并把 bypass 转成 ignore-hosts;开启前备份原桌面设置,关闭或失败时恢复。 - `tun on/off` 检查 core capability、`/dev/net/tun` 和 `CAP_NET_ADMIN`;运行中切换会重启,失败回滚,不自动执行 sudo/setcap。 配置生成已通过 Mihomo 1.19.18 和 sing-box 1.13.14 的真实 check。隔离测试覆盖 sing-box 的 start/status/list/select/stop,并继续覆盖 proxy 环境输出、TUN 状态和节点测速错误/超时路径。