-
Notifications
You must be signed in to change notification settings - Fork 0
Launcher Anatomy
The launcher has a contract too — and it lives on disk, next to the instances.
实例契约管的是「单个 Agent」。启动器契约管的是启动器自身:界面偏好、全局默认值、会话状态、侧栏分组。它们同样落盘、带版本、由后端拥有、在 src/types.ts 镜像——不再散落在 localStorage 或内存 ref 里。
~/.agentlauncher/
├─ config.json # 启动器配置(有版本)
├─ instgroups.json # 侧栏分组的表现层覆盖(有版本)
└─ instances/ # 各实例目录(见 Instance Anatomy)
两个文件都由 src-tauri/src/launcher_config.rs 拥有,命令 get/set_launcher_config、get/set_inst_groups 收口读写。缺失或损坏时回退内置默认值,绝不因为一个坏文件把启动器卡死。
-
ui:主题与语言。原先分别存在localStorage的agentlauncher.theme/agentlauncher.locale,现收口于此;localStorage仅保留一份极小缓存供首屏秒画,真相是本文件。 -
defaults:新建实例对话框的预填值(非密钥)。原agentlauncher.modelConfig的非密钥部分迁移至此。两个字段默认都是空串,含义是「用所选框架自己的默认」——与 adapter 的「空值即省略 flag」同一规则;写一个具体厂商默认对六个框架里的五个都是错的,连 dsh 也不对(它要的是deepseek-official而非deepseek)。-
已退役:
base_url(从未到达任何框架——base URL 走实例.env)与profile(dsh 专属旋钮且无 UI,永远只是它自己的默认值)。旧config.json仍带着它们也能正常读取(未知键被忽略)。
-
已退役:
-
session:跨启动恢复的瞬态 UX——上次选中的实例、上次使用的分组。
🔐 密钥永不进入本文件。 API Key 归各运行时所有(
dsh的~/.dsh/.credentials.yaml为其一,见 Configuration),统一通过实例.env注入。启动器契约只引用这条边界,不复制账号库。
{
"format_version": 1,
"order": ["未分类", "Web"],
"groups": {
"Web": { "collapsed": false, "instances": ["web-baseline", "test-agent"] }
}
}-
order:侧栏分组的上→下顺序。 -
groups[name].collapsed:该分组是否折叠(现在跨重启保留,不再是内存态)。 -
groups[name].instances:组内的手动排序覆盖。
混合模型 · 谁拥有什么: 成员归属的唯一真相始终是每个 instance.json 的 group 字段。本文件只是一层表现覆盖(顺序 / 折叠 / 组内排序),实例因此保持自描述,迁移代价最小。
健壮性铁律:绝不让过期文件把真实实例藏起来。
- 覆盖里引用了已删除的实例 / 分组 → 静默忽略。
- 某个实例 / 分组在覆盖里缺席 → 回退按名排序并追加到末尾。
- 渲染永远以「当前真实实例列表」为基准做重排,而非以覆盖文件为基准做筛选(见
src/lib/instGroups.ts::applyOverlay)。
-
万物带版本:
config.json/instgroups.json有format_version,instance.json有schema_version(缺失视为1,向后兼容)。 - 各域分文件:UI 偏好、分组表现、实例数据、密钥各自独立。
-
后端拥有 + 前端镜像:结构体定义在 Rust,
src/types.ts一一对应,改一边同步另一边。 -
密钥独立于配置:凭据属各运行时(如
dsh的~/.dsh),通过实例.env注入。
北极星是「通用 Agent 启动器」,但遵循渐进泛化:契约字段与它的消费者同时落地,绝不预留今天没人读的空字段。以下要素刻意推迟,各自等到功能真要做时连同实现一起补:
-
runtime.permissions(软墙):{ mode, allowed_commands, denied_commands }只有在存在强制点时才有意义;命令由 dsh 子进程内部执行,启动器目前无法拦截,需 dsh 侧提供可消费的权限入口后再定契约。 -
custom_python:dsh 是 Node 运行时,暂无消费者;待某引擎确实需要独立 Python/venv 路径时再加。 -
共享
cache/:MCP 插件 / Skill 的下载目前由 dsh / npx 负责,启动器不自行下载,无缓存对象。 -
model{}嵌套:把扁平的provider/model收进对象是纯重构,需schema_version1→2 迁移,收益低,暂不动。
记住:这些不是「缺失」,而是待消费者。加字段的门槛始终是「今天有谁读它」。
-
runtime.engine(多引擎):已落地。runtime/mod.rs::for_instance按runtime.engine分发到 6 个AgentRuntime实现(dsh/pi/omp/claude/codex/opencode,见 Instance Anatomy 的调用矩阵)。字段与 6 个消费者同时到位,正是「渐进泛化」的落地示范。 -
宿主引擎探测:原计划的
engines.json(引擎路径缓存)有意不落盘——改由detect_engines命令每次实时探测 PATH(复用runtime/env.rs的登录 shell 逻辑),与「PATH 现探不缓存」同一铁律:缓存会过期指向已删二进制。
「字段与消费者同时落地」也要反着执行:一个没有消费者的字段不该因为「已经写在文件里了」就留着。以下字段被收集、被落盘,却从未被任何 adapter 读过,故一并退役——未知键被忽略,所以删字段是向后兼容的,不需要升版本号(instance_manager.rs / launcher_config.rs 各有一条测试把这个行为钉住):
| 字段 | 原位置 | 为什么删 |
|---|---|---|
temperature、thinking_budget
|
instance.json |
没有任何框架 adapter 把它们下发给 CLI;补线就得逐引擎臆测 flag。 |
defaults.base_url |
config.json |
从未到达任何框架——base URL 走实例 .env。 |
defaults.profile |
config.json |
dsh 专属旋钮且没有 UI 能改,永远只是默认值。 |
同轮把事件名 dsh-log / dsh-status 改为 runtime-log / runtime-status:executor 本身与 Agent 无关,事件按接缝命名而非按 dsh 命名;「保持兼容命名」的理由只对第三方消费者成立,而它们的唯一消费者就是本仓库的前端。
📖 本 Wiki 由
agentlauncher/docs/wiki通过gh同步 · 同步脚本见scripts/sync-wiki.sh· 手动同步:pnpm wiki:sync灵感来自 Prism Launcher · 多引擎可扩展
{ "format_version": 1, "ui": { "theme": "catppuccin-mocha", "locale": "zh" }, "defaults": { "provider": "", "model": "" }, "session": { "selected_instance": "web-baseline", "last_used_group": "Web" } }