mirror of
https://github.com/aiclientproxy/proxycast.git
synced 2026-09-01 15:30:52 +08:00
Release v1.61.0
This commit is contained in:
+29
-32
@@ -1,53 +1,50 @@
|
||||
## Lime v1.60.0
|
||||
## Lime v1.61.0
|
||||
|
||||
<sub>The Simplified Chinese release notes are the primary version. This English page is a companion for international readers.</sub>
|
||||
|
||||
### New Features
|
||||
|
||||
- Made Electron Desktop Host the primary desktop entry point, adding main process, preload, IPC channels, window configuration, DevBridge HTTP bridge, and App Server host bridge foundations.
|
||||
- Moved release and updater work onto the Electron Forge current path, covering macOS DMG/ZIP, Windows Squirrel, Forge release asset staging, package-resource verification, and local ZIP feed validation.
|
||||
- Expanded App Server JSON-RPC coverage across Agent sessions, read/list/update, turn cancel, Connect deep links, workspace, model, knowledge, skill, artifact, and evidence surfaces.
|
||||
- Advanced the Agent Runtime / Claw path onto the App Server current read model, including Electron fixture coverage for session restore, code artifacts, stop generation, history reads, and completion projection.
|
||||
- Improved Agent App runtime lifecycle support for UI runtime start/status/stop, runtime packages, SDK contracts, native shell configuration, and standalone release helpers.
|
||||
- Added current GUI smoke coverage for settings, sidebar sessions, Connect deep links, Agent App UI runtime, Claw current fixture, code artifact workbench, and session history fixtures.
|
||||
- Added App Server JSON-RPC current protocols for automation jobs, covering scheduler config, scheduler status, job list/read/create/update/delete, run now, health, run history, schedule preview, and schedule validation.
|
||||
- Moved model Provider management onto the App Server current path, covering Provider list, catalog, read, create, update, delete, sort order, config import/export, connection tests, chat tests, model fetching, API key management, key rotation, usage/error records, and UI state persistence.
|
||||
- Expanded both the TypeScript and Rust App Server clients with typed automation and model Provider methods backed by shared protocol constants and schemas.
|
||||
- Added an Electron runtime wrapper and continued routing Electron Desktop Host commands into App Server current data surfaces.
|
||||
|
||||
### Fixes
|
||||
|
||||
- Fixed Electron `safeInvoke` JSON-RPC result-envelope handling so real App Server `result.lines` responses are no longer treated as empty.
|
||||
- Fixed sends from stale restored sessions by confirming restored ids with `agentSession/read` before submitting a new turn.
|
||||
- Fixed recent and archived sidebar sessions being filtered by stale remembered workspace ids.
|
||||
- Fixed Claw first-token waiting state so task cards and input status no longer show completed before a real terminal projection is available.
|
||||
- Fixed stop-generation behavior so `agentSession/turn/cancel` quickly writes a canceled read model and late completion events cannot overwrite the canceled state.
|
||||
- Fixed Electron startup and second-instance handling for `lime://connect` deep links, including current resolve, save, and callback paths.
|
||||
- Fixed automation settings and job APIs so missing required App Server results fail closed instead of falling back to legacy commands.
|
||||
- Fixed API Key Provider and model Provider flows so reads, writes, tests, imports, and exports no longer depend on legacy `safeInvoke` paths.
|
||||
- Fixed unregistered desktop-host mock commands silently succeeding; explicit test mocks now fail closed unless a command is registered.
|
||||
- Fixed diagnostic-only APIs being mixed with production API behavior by introducing a dedicated diagnostic facade.
|
||||
- Fixed the risk of automation `runNow` falling back to the old Tauri executor before the App Server automation executor is fully migrated.
|
||||
|
||||
### Improvements And Refactors
|
||||
|
||||
- Consolidated desktop host facts around Electron Desktop Host, App Server JSON-RPC, and `src/lib/desktop-host/` current mock boundaries.
|
||||
- Split large Agent Chat, workbench, sidebar, Harness, Skill selector, and Agent App tests into focused View Model and fixture layers.
|
||||
- Tightened App Server client contracts, command catalog, DevBridge policy, and governance catalogs to reduce mock-priority and legacy-command regressions.
|
||||
- Moved Electron release/updater scripts into `scripts/electron/` and continued organizing App Server, Agent Runtime, Agent App, i18n, Harness, Agent QC, and Knowledge scripts by domain.
|
||||
- Removed old Tauri naming and legacy updater/builder references from current release paths, making Forge-only release/updater the active source of truth.
|
||||
- Continued consolidating frontend API gateways for automation, model Providers, file browsing, session files, knowledge, memory, MCP, voice, updates, and workspaces around App Server and Electron current boundaries.
|
||||
- Removed old automation Rust command and DevBridge dispatcher paths, classifying the legacy automation command family as dead surface.
|
||||
- Reduced the default `src/lib/desktop-host/*Mocks` surface so production paths cannot fake App Server capabilities through default mocks.
|
||||
- Removed obsolete `webview-api` exposure and types to reduce overlap between browser runtime helpers and the desktop current path.
|
||||
- Moved more automation, Provider, and file-browser responsibilities into the App Server local data source, reducing old compatibility wrappers in the services crate.
|
||||
- Updated command-boundary governance with current automation and Provider method lists plus legacy-command regression guards.
|
||||
|
||||
### Tests And Quality
|
||||
|
||||
- Added App Server client contracts, command contracts, modality contracts, scripts governance, and Electron release workflow guards to `npm run test:contracts`.
|
||||
- Added a structured Electron release workflow guard for Forge makers, signing/notarization, Windows Squirrel, R2 updater assets, and legacy release-path rejection.
|
||||
- Added `npm run governance:scripts` to freeze the root `scripts/` directory and track domain migrations; the root release bucket is now cleared.
|
||||
- Added App Server, Electron, and Agent Runtime fixture smokes for stdio, sidecar lifecycle, packaged backend failure, package resources, Claw current fixture, cancel fixture, and history restore.
|
||||
- Strengthened live Provider / WebSearch / WebFetch smoke gates so unauthorized runs fail closed and authorized runs require turn-scoped provider, model, routing, completed tools, and output evidence.
|
||||
- Added targeted Rust and frontend regressions for App Server cancel/read model/JSON-RPC/external backend, App Server gateway, Agent Runtime clients, Connect, Agent App runtime, and i18n loading.
|
||||
- Updated the root app, Rust workspace, CLI npm package, Agent App runtime package, App Server client package, and lockfiles to `1.60.0`.
|
||||
- Expanded App Server client contracts for automation and model Provider protocols, schemas, TypeScript client methods, and Rust client methods.
|
||||
- Added command-contract guards that block legacy automation and Provider commands from returning through Tauri commands, DevBridge dispatchers, mock priority commands, or runtime surfaces.
|
||||
- Added targeted regressions for automation APIs, API Key Provider, model Providers, execution runs, session files, hint routes, document export, image search, video generation, and voice models.
|
||||
- Added desktop-host mock-boundary tests to ensure unregistered mocks fail closed and test fixtures explicitly register commands.
|
||||
- Expanded Electron host command, IPC channel, update host, App Server host, and current entrypoint coverage.
|
||||
- Improved Vitest smart-runner and test-file filtering coverage for more stable focused validation.
|
||||
- Updated the root app, Rust workspace, CLI npm package, Agent App runtime package, App Server client package, and lockfiles to `1.61.0`.
|
||||
|
||||
### Documentation
|
||||
|
||||
- Updated the App Server implementation plan with Electron migration, Claw current fixture, Connect deep link, Agent App UI runtime, cancel semantics, and release/updater governance progress.
|
||||
- Updated the quality workflow with Electron Desktop Host, App Server JSON-RPC, GUI smoke, current fixture, live Provider authorization, and localization validation rules.
|
||||
- Updated scripts governance documentation for Electron, App Server, Agent Runtime, Agent App, i18n, Harness, Agent QC, and Knowledge domain entry points.
|
||||
- Updated command boundary, governance, Playwright/E2E, App Server release/updater, and frontend migration docs with legacy / compat / dead surface exit criteria.
|
||||
- Updated Desktop Host / App Server command-boundary documentation for automation, model Providers, file browsing, mocks, DevBridge, and legacy surface classification.
|
||||
- Updated the App Server frontend integration matrix and implementation plan with automation and Provider migration progress.
|
||||
- Updated quality workflow and Playwright/E2E guidance to keep GUI validation centered on real Electron Desktop Host plus App Server paths.
|
||||
- Updated Agent App, Agent UI, Managed Objective, and Skill Forge roadmap notes where current command boundaries changed.
|
||||
|
||||
### Other
|
||||
|
||||
- Continued the repository structure migration from `src-tauri` to `lime-rs`, aligning Rust workspace and desktop backend fact sources.
|
||||
- Clarified release runners and asset staging: macOS arm64 uses `macos-15`, x64 uses `macos-15-intel`, and Windows uses `windows-2022`.
|
||||
- Continued reducing old Tauri, legacy desktop facade, and renderer mock influence on production paths so release facts, command boundaries, and GUI validation stay on the current single path.
|
||||
|
||||
**Full changes**: `v1.59.0` -> `v1.60.0`
|
||||
**Full changes**: `v1.60.0` -> `v1.61.0`
|
||||
|
||||
+29
-32
@@ -1,45 +1,42 @@
|
||||
## Lime v1.60.0
|
||||
## Lime v1.61.0
|
||||
|
||||
### 新功能
|
||||
- Electron Desktop Host 成为桌面主入口,新增主进程、preload、IPC channel、窗口配置、DevBridge HTTP bridge 与 App Server host bridge。
|
||||
- 发布与更新链路迁移到 Electron Forge current 主路径,覆盖 macOS DMG/ZIP、Windows Squirrel、Forge release asset staging、包资源校验与本地 ZIP feed 验证。
|
||||
- App Server JSON-RPC 能力继续扩展,补齐 Agent session/read/list/update/turn/cancel、Connect deep link、workspace、model、knowledge、skill、artifact 与 evidence 等 current 协议面。
|
||||
- Agent Runtime / Claw 主链进一步接入 App Server current read model,支持真实 Electron fixture 下的会话恢复、代码产物工作台、停止生成、历史读取和完成态投影。
|
||||
- Agent App runtime 增强 UI runtime 生命周期与 Electron fixture 能力,覆盖 start/status/stop、runtime package、SDK contract、native shell 配置与 standalone release 辅助链路。
|
||||
- 新增多组 current GUI smoke:设置页全量、侧栏会话、Connect deep link、Agent App UI runtime、Claw current fixture、代码产物工作台和 session history fixture。
|
||||
- App Server JSON-RPC 新增自动化任务 current 协议,覆盖调度器配置、状态读取、任务列表 / 详情 / 创建 / 更新 / 删除、立即运行、健康检查、运行历史、计划预览与计划校验。
|
||||
- 模型 Provider 管理迁移到 App Server current 主链,覆盖 Provider 列表、目录、详情、新建、更新、删除、排序、配置导入导出、连接测试、聊天测试、模型拉取、API Key 管理、Key 轮转、使用 / 错误记录与 UI state 持久化。
|
||||
- TypeScript 与 Rust App Server client 补齐自动化和模型 Provider 的类型化客户端方法,前端、Electron 与 Rust 侧共享同一组协议常量和 schema。
|
||||
- Electron Desktop Host 新增 Electron runtime 封装,并继续把 host command 收敛到 App Server current 数据面。
|
||||
|
||||
### 修复
|
||||
- 修复 Electron `safeInvoke` JSON-RPC result envelope 解包问题,避免 App Server 返回真实 `result.lines` 时被前端误判为空。
|
||||
- 修复恢复到 App Server 中不存在的历史 session 后继续发送导致无输出的问题,发送前会先用 `agentSession/read` 确认 session 存在并匹配 workspace。
|
||||
- 修复最近对话 / 归档列表被陈旧 remembered workspace 污染过滤条件的问题,侧栏查询不再把未验证 workspace 传给 `agentSession/list`。
|
||||
- 修复 Claw 首 token 等待态中任务卡和输入栏过早显示已完成的问题,只有真实终态和正文投影完成后才展示完成状态。
|
||||
- 修复停止生成语义,`agentSession/turn/cancel` 会先写入 canceled read model 并快速恢复输入框,迟到的完成事件不再覆盖取消态。
|
||||
- 修复 Connect deep link 在 Electron 首启参数和二次实例参数中的分发,覆盖 `lime://connect` 的 current resolve、保存和 callback 路径。
|
||||
- 修复自动化设置和任务 API 缺少 App Server 必需返回值时可能被旧路径伪装成功的问题,现在会 fail closed,不再回退 legacy 命令。
|
||||
- 修复 API Key Provider / 模型 Provider 读取、写入、测试、导入导出等路径对 legacy `safeInvoke` 的依赖,App Server 不可用时明确暴露错误。
|
||||
- 修复未注册 desktop-host mock 命令可静默通过的风险,测试 mock 遇到未知命令会直接失败。
|
||||
- 修复诊断类 API 与生产 API 混用的问题,新增 diagnostic facade,避免把诊断降级误认为真实业务链路。
|
||||
- 修复自动化 `runNow` 在 App Server 执行器迁移未完成时回退旧 Tauri 执行器的风险,current 协议会拒绝旧执行器回流。
|
||||
|
||||
### 优化与重构
|
||||
- 将桌面宿主事实源从 Tauri / legacy facade 收敛到 Electron Desktop Host、App Server JSON-RPC 与 `src/lib/desktop-host/` current mock 边界。
|
||||
- 大规模拆分 Agent Chat、工作台、侧栏、Harness 面板、Skill 选择器、Agent App 页面等复杂测试和 View Model,降低 React 挂载测试承载业务状态机的比例。
|
||||
- App Server client contract、command catalog、DevBridge policy 与 governance catalog 继续收敛,减少 mock priority command 和 legacy command 回流。
|
||||
- Electron release/updater 脚本迁入 `scripts/electron/`,App Server、Agent Runtime、Agent App、i18n、Harness、Agent QC、Knowledge 脚本按领域目录治理。
|
||||
- 清理旧 Tauri 命名与旧 updater/builder 入口,Forge-only release/updater 成为当前发布事实源。
|
||||
- 自动化、模型 Provider、文件浏览、会话文件、知识、记忆、MCP、语音、更新、工作区等前端 API 网关继续收敛到 App Server / Electron current 边界。
|
||||
- 删除旧自动化 Rust command 与 DevBridge dispatcher,旧自动化命令族进入治理层 dead surface。
|
||||
- 大幅收缩 `src/lib/desktop-host/*Mocks` 默认 mock 面,生产路径不再通过 default mock 伪造 App Server 能力。
|
||||
- 移除旧 `webview-api` 暴露面中的过时类型和入口,减少浏览器运行时与桌面 current 主链的重叠。
|
||||
- App Server 本地数据源吸收自动化、Provider 与文件浏览相关能力,降低 services crate 中旧兼容包装的职责。
|
||||
- 命令边界文档和治理目录补充自动化 / Provider current 方法清单及旧命令禁回流规则。
|
||||
|
||||
### 测试与质量
|
||||
- `npm run test:contracts` 接入 App Server client contract、command contract、modality contract、scripts governance 与 Electron release workflow guard。
|
||||
- 新增 Electron release workflow 结构化守卫,校验 Forge maker、签名/公证、Windows Squirrel、R2 updater asset 计划,并阻止 electron-builder、NSIS、旧 updater metadata 回流。
|
||||
- 新增 `npm run governance:scripts`,冻结 `scripts/` 根目录并跟踪领域迁移,根目录 release bucket 已清零。
|
||||
- 新增 App Server / Electron / Agent Runtime fixture smoke,覆盖 app-server stdio、sidecar lifecycle、packaged backend failure、Electron package resources、Claw current fixture、cancel fixture 与历史恢复。
|
||||
- 增强 live Provider / WebSearch / WebFetch smoke gate,授权前 fail closed,授权后要求 turn-scoped provider/model/routing、工具完成态和输出证据同时满足。
|
||||
- 补充 Rust App Server cancel、read model、JSON-RPC、external backend,以及前端 App Server gateway、Agent Runtime client、Connect、Agent App runtime、i18n loader 等定向回归。
|
||||
- 根应用、Rust workspace、CLI npm package、Agent App runtime package、App Server client package 与锁文件版本统一更新到 `1.60.0`。
|
||||
- App Server client contract 扩展自动化与模型 Provider 覆盖,校验新协议、schema、TypeScript 客户端和 Rust 客户端保持同步。
|
||||
- 命令契约守卫新增自动化 / Provider 旧命令禁回流检查,阻止 legacy Tauri command、DevBridge dispatcher、mock priority 与 runtime surface 重新接入。
|
||||
- 新增自动化 API、API Key Provider、模型 Provider、执行运行、会话文件、提示路由、文档导出、图片搜索、视频生成、语音模型等定向回归。
|
||||
- 新增 desktop-host mock 边界测试,确认未注册 mock fail closed,测试夹具必须显式注册命令。
|
||||
- 扩展 Electron host command、IPC channel、update host、App Server host 与 current entrypoint 测试。
|
||||
- Vitest smart runner 与测试文件过滤逻辑补充回归,提升局部测试选择和守卫稳定性。
|
||||
- 根应用、Rust workspace、CLI npm package、Agent App runtime package、App Server client package 与锁文件版本统一更新到 `1.61.0`。
|
||||
|
||||
### 文档
|
||||
- 更新 App Server 实施计划,记录 Electron 迁移、Claw current fixture、Connect deep link、Agent App UI runtime、cancel 语义和 release/updater 治理进展。
|
||||
- 更新工程质量工作流,明确 Electron Desktop Host、App Server JSON-RPC、GUI smoke、current fixture、live Provider 授权和本地化验证口径。
|
||||
- 更新脚本治理 README,沉淀 Electron、App Server、Agent Runtime、Agent App、i18n、Harness、Agent QC、Knowledge 领域脚本入口。
|
||||
- 更新命令边界、治理、Playwright/E2E、App Server release/updater 与前端迁移矩阵,明确 legacy / compat / dead surface 的退出条件。
|
||||
- 更新 Desktop Host / App Server 命令边界,明确自动化、模型 Provider、文件浏览、mock、DevBridge 与 legacy surface 的 current / dead 分类。
|
||||
- 更新 App Server 前端集成矩阵和实施计划,记录自动化与 Provider 迁移到 App Server current 主链的进度。
|
||||
- 更新质量工作流与 Playwright / E2E 指南,继续强调 GUI 桌面产品必须通过真实 Electron Desktop Host + App Server 路径验证。
|
||||
- 更新 Agent App、Agent UI、Managed Objective、Skill Forge 等路线图中与 current 命令边界相关的说明。
|
||||
|
||||
### 其他
|
||||
- 继续推进 `src-tauri` 到 `lime-rs` 的仓库结构迁移,统一 Rust workspace manifest 与桌面后端事实源。
|
||||
- 发布工作流 runner 和 asset staging 更明确:macOS arm64 固定 `macos-15`,x64 固定 `macos-15-intel`,Windows 固定 `windows-2022`。
|
||||
- 继续减少旧 Tauri / legacy desktop facade / renderer mock 对生产路径的影响,让发版事实源、命令边界和 GUI 验证保持 current 单主链。
|
||||
|
||||
**完整变更**: `v1.59.0` -> `v1.60.0`
|
||||
**完整变更**: `v1.60.0` -> `v1.61.0`
|
||||
|
||||
@@ -105,7 +105,7 @@ const {
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("electron", () => ({
|
||||
vi.mock("./electronRuntime", () => ({
|
||||
app: {
|
||||
getAppPath: () => process.cwd(),
|
||||
getVersion: () => "0.0.0-test",
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
type RequestId,
|
||||
type SidecarLaunchConfig,
|
||||
} from "app-server-client";
|
||||
import { app } from "electron";
|
||||
import { app } from "./electronRuntime";
|
||||
import { readFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
import { createServer } from "node:net";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { ElectronDevHttpBridge } from "./devHttpBridge";
|
||||
|
||||
const activeBridges: ElectronDevHttpBridge[] = [];
|
||||
type DevHttpBridgeInvoke = ConstructorParameters<
|
||||
typeof ElectronDevHttpBridge
|
||||
>[0]["invoke"];
|
||||
|
||||
async function createStartedBridge(
|
||||
invoke: DevHttpBridgeInvoke = vi.fn<DevHttpBridgeInvoke>(
|
||||
async () => undefined,
|
||||
),
|
||||
): Promise<{ bridge: ElectronDevHttpBridge; invoke: DevHttpBridgeInvoke }> {
|
||||
const port = await reserveLocalPort();
|
||||
const bridge = new ElectronDevHttpBridge({
|
||||
host: "127.0.0.1",
|
||||
port,
|
||||
invoke,
|
||||
});
|
||||
activeBridges.push(bridge);
|
||||
bridge.start();
|
||||
await waitForHealth(bridge.url);
|
||||
return { bridge, invoke };
|
||||
}
|
||||
|
||||
async function reserveLocalPort(): Promise<number> {
|
||||
const server = createServer();
|
||||
return new Promise((resolve, reject) => {
|
||||
server.once("error", reject);
|
||||
server.listen(0, "127.0.0.1", () => {
|
||||
const address = server.address();
|
||||
if (!address || typeof address === "string") {
|
||||
server.close(() => reject(new Error("failed to reserve local port")));
|
||||
return;
|
||||
}
|
||||
server.close(() => resolve(address.port));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function waitForHealth(baseUrl: string): Promise<void> {
|
||||
const deadline = Date.now() + 2_000;
|
||||
let lastError: unknown;
|
||||
while (Date.now() < deadline) {
|
||||
try {
|
||||
const response = await fetch(`${baseUrl}/health`);
|
||||
if (response.ok) {
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
lastError = error;
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||
}
|
||||
throw lastError instanceof Error
|
||||
? lastError
|
||||
: new Error("dev HTTP bridge did not become healthy");
|
||||
}
|
||||
|
||||
async function postJson(
|
||||
baseUrl: string,
|
||||
payload: unknown,
|
||||
): Promise<{ status: number; body: unknown }> {
|
||||
const response = await fetch(`${baseUrl}/invoke`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
return {
|
||||
status: response.status,
|
||||
body: await response.json(),
|
||||
};
|
||||
}
|
||||
|
||||
describe("electron/devHttpBridge", () => {
|
||||
beforeEach(() => {
|
||||
vi.spyOn(console, "log").mockImplementation(() => {});
|
||||
vi.spyOn(console, "warn").mockImplementation(() => {});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
for (const bridge of activeBridges.splice(0)) {
|
||||
bridge.stop();
|
||||
}
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("health 端点必须声明当前 transport 为 electron-host", async () => {
|
||||
const { bridge } = await createStartedBridge();
|
||||
|
||||
const response = await fetch(`${bridge.url}/health`);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.headers.get("access-control-allow-origin")).toBe("*");
|
||||
await expect(response.json()).resolves.toEqual({
|
||||
status: "ok",
|
||||
transport: "electron-host",
|
||||
});
|
||||
});
|
||||
|
||||
it("/invoke 只调用注入的 Electron Host invoke 并返回 result envelope", async () => {
|
||||
const invoke = vi.fn().mockResolvedValue({ accepted: true });
|
||||
const { bridge } = await createStartedBridge(invoke);
|
||||
|
||||
const response = await postJson(bridge.url, {
|
||||
cmd: " agent_runtime_create_session ",
|
||||
args: { workspaceRoot: "/tmp/workspace" },
|
||||
});
|
||||
|
||||
expect(response).toEqual({
|
||||
status: 200,
|
||||
body: { result: { accepted: true } },
|
||||
});
|
||||
expect(invoke).toHaveBeenCalledTimes(1);
|
||||
expect(invoke).toHaveBeenCalledWith("agent_runtime_create_session", {
|
||||
workspaceRoot: "/tmp/workspace",
|
||||
});
|
||||
});
|
||||
|
||||
it("/invoke 缺 command 或 body 非对象时 fail closed 且不调用 invoke", async () => {
|
||||
const invoke = vi.fn();
|
||||
const { bridge } = await createStartedBridge(invoke);
|
||||
|
||||
await expect(postJson(bridge.url, { args: { ignored: true } })).resolves.toEqual(
|
||||
{
|
||||
status: 400,
|
||||
body: { error: "cmd is required" },
|
||||
},
|
||||
);
|
||||
|
||||
const response = await fetch(`${bridge.url}/invoke`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(["legacy_command"]),
|
||||
});
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
await expect(response.json()).resolves.toEqual({
|
||||
error: "request body must be a JSON object",
|
||||
});
|
||||
expect(invoke).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("/invoke 把 Electron Host 错误投影为 error envelope,不能伪装成功", async () => {
|
||||
const invoke = vi.fn().mockRejectedValue(new Error("unsupported command"));
|
||||
const { bridge } = await createStartedBridge(invoke);
|
||||
|
||||
await expect(
|
||||
postJson(bridge.url, { cmd: "legacy_command", args: { a: 1 } }),
|
||||
).resolves.toEqual({
|
||||
status: 200,
|
||||
body: { error: "unsupported command" },
|
||||
});
|
||||
});
|
||||
|
||||
it("OPTIONS 和未知路径保持固定 HTTP 边界", async () => {
|
||||
const { bridge } = await createStartedBridge();
|
||||
|
||||
const options = await fetch(`${bridge.url}/invoke`, { method: "OPTIONS" });
|
||||
expect(options.status).toBe(204);
|
||||
expect(options.headers.get("access-control-allow-methods")).toBe(
|
||||
"GET,POST,OPTIONS",
|
||||
);
|
||||
|
||||
const notFound = await fetch(`${bridge.url}/unknown`);
|
||||
expect(notFound.status).toBe(404);
|
||||
await expect(notFound.json()).resolves.toEqual({ error: "not found" });
|
||||
});
|
||||
|
||||
it("events 端点只向订阅的事件推送 SSE message", async () => {
|
||||
const { bridge } = await createStartedBridge();
|
||||
const response = await fetch(`${bridge.url}/events?event=session:update`);
|
||||
const reader = response.body?.getReader();
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.headers.get("content-type")).toContain("text/event-stream");
|
||||
expect(reader).toBeDefined();
|
||||
|
||||
bridge.broadcast("ignored:event", { ignored: true });
|
||||
bridge.broadcast("session:update", { id: "session-1" });
|
||||
|
||||
const text = await readUntil(reader!, "session:update");
|
||||
expect(text).toContain(": connected");
|
||||
expect(text).toContain("event: message");
|
||||
expect(text).toContain(
|
||||
'data: {"event":"session:update","payload":{"id":"session-1"}}',
|
||||
);
|
||||
expect(text).not.toContain("ignored:event");
|
||||
|
||||
await reader?.cancel();
|
||||
});
|
||||
});
|
||||
|
||||
async function readUntil(
|
||||
reader: ReadableStreamDefaultReader<Uint8Array>,
|
||||
needle: string,
|
||||
): Promise<string> {
|
||||
const decoder = new TextDecoder();
|
||||
let text = "";
|
||||
const deadline = Date.now() + 2_000;
|
||||
while (!text.includes(needle) && Date.now() < deadline) {
|
||||
const result = await reader.read();
|
||||
if (result.done) {
|
||||
break;
|
||||
}
|
||||
text += decoder.decode(result.value, { stream: true });
|
||||
}
|
||||
if (!text.includes(needle)) {
|
||||
throw new Error(`SSE stream did not include ${needle}`);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { createRequire } from "node:module";
|
||||
import type * as Electron from "electron";
|
||||
|
||||
const requireElectron = createRequire(import.meta.url);
|
||||
const electron = requireElectron("electron") as typeof Electron;
|
||||
|
||||
export const {
|
||||
app,
|
||||
autoUpdater,
|
||||
BrowserWindow,
|
||||
dialog,
|
||||
globalShortcut,
|
||||
ipcMain,
|
||||
Menu,
|
||||
nativeImage,
|
||||
screen,
|
||||
session,
|
||||
shell,
|
||||
Tray,
|
||||
} = electron;
|
||||
|
||||
export type BrowserWindow = Electron.BrowserWindow;
|
||||
export type Menu = Electron.Menu;
|
||||
export type Tray = Electron.Tray;
|
||||
|
||||
export type {
|
||||
IpcMainInvokeEvent,
|
||||
MenuItemConstructorOptions,
|
||||
OpenDialogOptions,
|
||||
SaveDialogOptions,
|
||||
} from "electron";
|
||||
@@ -7,23 +7,38 @@ import { AppServerRequestError } from "app-server-client";
|
||||
import { ElectronHostCommands } from "./hostCommands";
|
||||
import type { ElectronAppServerHost } from "./appServerHost";
|
||||
|
||||
const { openExternalMock } = vi.hoisted(() => ({
|
||||
openExternalMock: vi.fn(),
|
||||
}));
|
||||
const {
|
||||
getFileIconMock,
|
||||
getPathMock,
|
||||
openExternalMock,
|
||||
openPathMock,
|
||||
showItemInFolderMock,
|
||||
} = vi.hoisted(() => {
|
||||
return {
|
||||
getFileIconMock: vi.fn(),
|
||||
getPathMock: vi.fn((_name: string) => os.tmpdir()),
|
||||
openExternalMock: vi.fn(),
|
||||
openPathMock: vi.fn(),
|
||||
showItemInFolderMock: vi.fn(),
|
||||
};
|
||||
});
|
||||
const tempDirs: string[] = [];
|
||||
type AppServerRequestMock = (
|
||||
method: string,
|
||||
params?: unknown,
|
||||
) => Promise<unknown>;
|
||||
|
||||
vi.mock("electron", () => ({
|
||||
vi.mock("./electronRuntime", () => ({
|
||||
app: {
|
||||
getFileIcon: getFileIconMock,
|
||||
getName: () => "Lime",
|
||||
getPath: () => os.tmpdir(),
|
||||
getPath: getPathMock,
|
||||
getVersion: () => "0.0.0-test",
|
||||
},
|
||||
shell: {
|
||||
openExternal: openExternalMock,
|
||||
openPath: openPathMock,
|
||||
showItemInFolder: showItemInFolderMock,
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -63,6 +78,7 @@ function sessionAlreadyExistsError(sessionId: string) {
|
||||
|
||||
afterEach(async () => {
|
||||
vi.clearAllMocks();
|
||||
getPathMock.mockImplementation(() => os.tmpdir());
|
||||
while (tempDirs.length > 0) {
|
||||
const dir = tempDirs.pop();
|
||||
if (dir) {
|
||||
@@ -85,6 +101,194 @@ describe("ElectronHostCommands retired file browser facade", () => {
|
||||
);
|
||||
});
|
||||
|
||||
describe("ElectronHostCommands retired automation facade", () => {
|
||||
it.each([
|
||||
"get_automation_scheduler_config",
|
||||
"get_automation_status",
|
||||
"get_automation_health",
|
||||
"get_automation_jobs",
|
||||
])("%s 不再作为 Electron Host compat facade 暴露", async (command) => {
|
||||
const userDataDir = await createTempUserDataDir();
|
||||
const host = createHost(userDataDir);
|
||||
|
||||
await expect(host.invoke(command, {})).rejects.toThrow(
|
||||
`Electron host command is not implemented: ${command}`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("ElectronHostCommands retired API Key Provider facade", () => {
|
||||
it.each([
|
||||
"get_api_key_providers",
|
||||
"get_system_provider_catalog",
|
||||
"get_provider_ui_state",
|
||||
"set_provider_ui_state",
|
||||
"fetch_provider_models_auto",
|
||||
])("%s 不再作为 Electron Host provider facade 暴露", async (command) => {
|
||||
const userDataDir = await createTempUserDataDir();
|
||||
const host = createHost(userDataDir);
|
||||
|
||||
await expect(host.invoke(command, {})).rejects.toThrow(
|
||||
`Electron host command is not implemented: ${command}`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("ElectronHostCommands local file shell facade", () => {
|
||||
it("reveal_in_finder 通过 Electron shell 定位本地路径", async () => {
|
||||
const userDataDir = await createTempUserDataDir();
|
||||
const host = createHost(userDataDir);
|
||||
|
||||
await expect(
|
||||
host.invoke("reveal_in_finder", { path: "/tmp/demo.txt" }),
|
||||
).resolves.toEqual({});
|
||||
|
||||
expect(showItemInFolderMock).toHaveBeenCalledWith("/tmp/demo.txt");
|
||||
});
|
||||
|
||||
it("open_with_default_app 通过 Electron shell 打开本地路径", async () => {
|
||||
openPathMock.mockResolvedValueOnce("");
|
||||
const userDataDir = await createTempUserDataDir();
|
||||
const host = createHost(userDataDir);
|
||||
|
||||
await expect(
|
||||
host.invoke("open_with_default_app", { path: "/tmp/demo.txt" }),
|
||||
).resolves.toEqual({});
|
||||
|
||||
expect(openPathMock).toHaveBeenCalledWith("/tmp/demo.txt");
|
||||
});
|
||||
|
||||
it("open_with_default_app 应暴露 Electron openPath 失败", async () => {
|
||||
openPathMock.mockResolvedValueOnce("Cannot open file");
|
||||
const userDataDir = await createTempUserDataDir();
|
||||
const host = createHost(userDataDir);
|
||||
|
||||
await expect(
|
||||
host.invoke("open_with_default_app", { path: "/tmp/missing.txt" }),
|
||||
).rejects.toThrow("Cannot open file");
|
||||
});
|
||||
|
||||
it("get_file_icon_data_url 应通过 Electron 读取系统文件图标", async () => {
|
||||
getFileIconMock.mockResolvedValueOnce({
|
||||
isEmpty: () => false,
|
||||
toDataURL: () => "data:image/png;base64,abc",
|
||||
});
|
||||
const userDataDir = await createTempUserDataDir();
|
||||
const host = createHost(userDataDir);
|
||||
|
||||
await expect(
|
||||
host.invoke("get_file_icon_data_url", { path: "/Applications/Lime.app" }),
|
||||
).resolves.toBe("data:image/png;base64,abc");
|
||||
|
||||
expect(getFileIconMock).toHaveBeenCalledWith("/Applications/Lime.app", {
|
||||
size: "normal",
|
||||
});
|
||||
});
|
||||
|
||||
it("get_file_icon_data_url 在系统图标不可用时返回 null", async () => {
|
||||
getFileIconMock.mockResolvedValueOnce({
|
||||
isEmpty: () => true,
|
||||
toDataURL: () => "data:image/png;base64,unused",
|
||||
});
|
||||
const userDataDir = await createTempUserDataDir();
|
||||
const host = createHost(userDataDir);
|
||||
|
||||
await expect(
|
||||
host.invoke("get_file_icon_data_url", { path: "/tmp/missing.txt" }),
|
||||
).resolves.toBeNull();
|
||||
});
|
||||
|
||||
it("get_file_icon_data_url 应隔离 Electron 图标读取失败", async () => {
|
||||
getFileIconMock.mockRejectedValueOnce(new Error("icon unavailable"));
|
||||
const userDataDir = await createTempUserDataDir();
|
||||
const host = createHost(userDataDir);
|
||||
|
||||
await expect(
|
||||
host.invoke("get_file_icon_data_url", { path: "/tmp/missing.txt" }),
|
||||
).resolves.toBeNull();
|
||||
});
|
||||
|
||||
it("get_home_dir 应返回 Electron 系统主目录", async () => {
|
||||
const userDataDir = await createTempUserDataDir();
|
||||
const homeDir = path.join(userDataDir, "home");
|
||||
getPathMock.mockImplementation((name: string) => {
|
||||
return name === "home" ? homeDir : os.tmpdir();
|
||||
});
|
||||
const host = createHost(userDataDir);
|
||||
|
||||
await expect(host.invoke("get_home_dir")).resolves.toBe(homeDir);
|
||||
});
|
||||
|
||||
it("get_home_dir 在系统主目录不可用时应 fail closed", async () => {
|
||||
const userDataDir = await createTempUserDataDir();
|
||||
getPathMock.mockImplementation((name: string) => {
|
||||
return name === "home" ? "" : os.tmpdir();
|
||||
});
|
||||
const host = createHost(userDataDir);
|
||||
|
||||
await expect(host.invoke("get_home_dir")).rejects.toThrow("无法获取主目录");
|
||||
});
|
||||
|
||||
it("get_file_manager_locations 应返回存在的系统快捷入口并去重", async () => {
|
||||
const userDataDir = await createTempUserDataDir();
|
||||
const homeDir = path.join(userDataDir, "home");
|
||||
const missingDesktopDir = path.join(userDataDir, "missing-desktop");
|
||||
const documentsDir = path.join(userDataDir, "Documents");
|
||||
const downloadsDir = path.join(userDataDir, "Downloads");
|
||||
await mkdir(homeDir, { recursive: true });
|
||||
await mkdir(documentsDir, { recursive: true });
|
||||
await mkdir(downloadsDir, { recursive: true });
|
||||
getPathMock.mockImplementation((name: string) => {
|
||||
if (name === "home") {
|
||||
return homeDir;
|
||||
}
|
||||
if (name === "desktop") {
|
||||
return missingDesktopDir;
|
||||
}
|
||||
if (name === "documents") {
|
||||
return documentsDir;
|
||||
}
|
||||
if (name === "downloads") {
|
||||
return downloadsDir;
|
||||
}
|
||||
return os.tmpdir();
|
||||
});
|
||||
const host = createHost(userDataDir);
|
||||
|
||||
const locations = await host.invoke("get_file_manager_locations");
|
||||
|
||||
expect(locations).toEqual(
|
||||
expect.arrayContaining([
|
||||
{
|
||||
id: "home",
|
||||
label: "个人",
|
||||
path: homeDir,
|
||||
kind: "home",
|
||||
},
|
||||
{
|
||||
id: "documents",
|
||||
label: "文档",
|
||||
path: documentsDir,
|
||||
kind: "documents",
|
||||
},
|
||||
{
|
||||
id: "downloads",
|
||||
label: "下载",
|
||||
path: downloadsDir,
|
||||
kind: "downloads",
|
||||
},
|
||||
]),
|
||||
);
|
||||
const returnedPaths = (locations as Array<{ path: string }>).map(
|
||||
(location) => location.path,
|
||||
);
|
||||
expect(returnedPaths.filter((nextPath) => nextPath === homeDir)).toHaveLength(
|
||||
1,
|
||||
);
|
||||
expect(returnedPaths).not.toContain(missingDesktopDir);
|
||||
});
|
||||
});
|
||||
|
||||
describe("ElectronHostCommands experimental config", () => {
|
||||
it("默认读取关闭的 WebMCP 预留配置", async () => {
|
||||
const userDataDir = await createTempUserDataDir();
|
||||
@@ -129,6 +333,72 @@ describe("ElectronHostCommands experimental config", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("ElectronHostCommands MCP current source", () => {
|
||||
it.each([
|
||||
[
|
||||
"get_mcp_servers",
|
||||
"mcpServer/list",
|
||||
{ servers: [{ id: "server-1", name: "docs" }] },
|
||||
[{ id: "server-1", name: "docs" }],
|
||||
],
|
||||
[
|
||||
"mcp_list_servers_with_status",
|
||||
"mcpServerStatus/list",
|
||||
{ servers: [{ id: "server-1", name: "docs", is_running: false }] },
|
||||
[{ id: "server-1", name: "docs", is_running: false }],
|
||||
],
|
||||
[
|
||||
"mcp_list_tools",
|
||||
"mcpTool/list",
|
||||
{ tools: [{ name: "mcp__docs__search_docs" }] },
|
||||
[{ name: "mcp__docs__search_docs" }],
|
||||
],
|
||||
[
|
||||
"mcp_list_prompts",
|
||||
"mcpPrompt/list",
|
||||
{ prompts: [{ name: "summarize", server_name: "docs" }] },
|
||||
[{ name: "summarize", server_name: "docs" }],
|
||||
],
|
||||
[
|
||||
"mcp_list_resources",
|
||||
"mcpResource/list",
|
||||
{ resources: [{ uri: "docs://readme", server_name: "docs" }] },
|
||||
[{ uri: "docs://readme", server_name: "docs" }],
|
||||
],
|
||||
] as const)(
|
||||
"%s 应经 App Server current method 返回 MCP 列表",
|
||||
async (command, expectedMethod, response, expectedResult) => {
|
||||
const userDataDir = await createTempUserDataDir();
|
||||
const request = vi.fn(async (method: string, params?: unknown) => {
|
||||
expect(params).toEqual({});
|
||||
if (method === expectedMethod) {
|
||||
return response;
|
||||
}
|
||||
throw new Error(`unexpected App Server method: ${method}`);
|
||||
});
|
||||
const host = createHost(userDataDir, () => undefined, request);
|
||||
|
||||
await expect(host.invoke(command)).resolves.toEqual(expectedResult);
|
||||
expect(request).toHaveBeenCalledWith(expectedMethod, {});
|
||||
},
|
||||
);
|
||||
|
||||
it("MCP current 空态不应带 Electron diagnostic facade 元数据", async () => {
|
||||
const userDataDir = await createTempUserDataDir();
|
||||
const host = createHost(userDataDir, () => undefined, async (method) => {
|
||||
if (method === "mcpTool/list") {
|
||||
return { tools: [] };
|
||||
}
|
||||
throw new Error(`unexpected App Server method: ${method}`);
|
||||
});
|
||||
|
||||
const result = await host.invoke("mcp_list_tools");
|
||||
|
||||
expect(result).toEqual([]);
|
||||
expect((result as { __diagnostic?: unknown }).__diagnostic).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("ElectronHostCommands model provider current source", () => {
|
||||
it("get_default_provider 应忽略旧配置值并返回 App Server 当前已配置 Provider", async () => {
|
||||
const userDataDir = await createTempUserDataDir();
|
||||
|
||||
+232
-130
@@ -1,4 +1,4 @@
|
||||
import { app, shell } from "electron";
|
||||
import { app, shell } from "./electronRuntime";
|
||||
import {
|
||||
AppServerRequestError,
|
||||
ERROR_CODES,
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
METHOD_AGENT_SESSION_TURN_CANCEL,
|
||||
METHOD_AGENT_SESSION_TURN_START,
|
||||
METHOD_AGENT_SESSION_UPDATE,
|
||||
METHOD_AUTOMATION_JOB_LIST,
|
||||
METHOD_CAPABILITY_LIST,
|
||||
METHOD_EVIDENCE_EXPORT,
|
||||
METHOD_KNOWLEDGE_PACK_LIST,
|
||||
@@ -24,6 +23,11 @@ import {
|
||||
METHOD_MODEL_PROVIDER_CATALOG_LIST,
|
||||
METHOD_MODEL_PROVIDER_LIST,
|
||||
METHOD_MODEL_SYNC_STATE_READ,
|
||||
METHOD_MCP_PROMPT_LIST,
|
||||
METHOD_MCP_RESOURCE_LIST,
|
||||
METHOD_MCP_SERVER_LIST,
|
||||
METHOD_MCP_SERVER_STATUS_LIST,
|
||||
METHOD_MCP_TOOL_LIST,
|
||||
METHOD_PROJECT_MEMORY_READ,
|
||||
METHOD_SKILL_LIST,
|
||||
METHOD_SKILL_READ,
|
||||
@@ -51,7 +55,6 @@ import {
|
||||
type AgentAppUiRuntimeStatusResponse,
|
||||
type AgentAppUiRuntimeStopParams,
|
||||
type ArtifactSummary,
|
||||
type AutomationJobListResponse,
|
||||
type CapabilityDescriptor,
|
||||
type CapabilityListResponse,
|
||||
type EvidenceExportResponse,
|
||||
@@ -63,6 +66,11 @@ import {
|
||||
type ModelProviderCatalogListResponse,
|
||||
type ModelProviderListResponse,
|
||||
type ModelSyncStateReadResponse,
|
||||
type McpPromptListResponse,
|
||||
type McpResourceListResponse,
|
||||
type McpServerListResponse,
|
||||
type McpServerStatusListResponse,
|
||||
type McpToolListResponse,
|
||||
type ProjectMemoryReadResponse,
|
||||
type SkillListResponse,
|
||||
type SkillReadResponse,
|
||||
@@ -73,7 +81,7 @@ import {
|
||||
type WorkspaceReadResponse,
|
||||
type WorkspaceSkillBindingsListResponse,
|
||||
} from "app-server-client";
|
||||
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
||||
import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
||||
import { createServer, type Server } from "node:http";
|
||||
import path from "node:path";
|
||||
import type { ElectronAppServerHost } from "./appServerHost";
|
||||
@@ -81,6 +89,16 @@ import type { ElectronAppServerHost } from "./appServerHost";
|
||||
type HostArgs = Record<string, unknown> | null | undefined;
|
||||
type AppServerParams = Record<string, unknown>;
|
||||
type HostEventEmitter = (event: string, payload?: unknown) => void;
|
||||
type FileManagerLocation = {
|
||||
id: string;
|
||||
label: string;
|
||||
path: string;
|
||||
kind: string;
|
||||
};
|
||||
type FileManagerLocationCandidate = Omit<FileManagerLocation, "path"> & {
|
||||
path: string | null;
|
||||
};
|
||||
type ElectronKnownPathName = "home" | "desktop" | "documents" | "downloads";
|
||||
|
||||
const CONFIG_FILE = "config.json";
|
||||
const OEM_CLOUD_OAUTH_CALLBACK_BRIDGE_EVENT = "oem-cloud-oauth-callback";
|
||||
@@ -139,16 +157,22 @@ export class ElectronHostCommands {
|
||||
return await this.#saveExperimentalConfig(args);
|
||||
case "open_external_url":
|
||||
return await this.#openExternalUrl(args);
|
||||
case "reveal_in_finder":
|
||||
return this.#revealInFinder(args);
|
||||
case "open_with_default_app":
|
||||
return await this.#openWithDefaultApp(args);
|
||||
case "get_home_dir":
|
||||
return this.#getHomeDir();
|
||||
case "get_file_manager_locations":
|
||||
return await this.#getFileManagerLocations();
|
||||
case "get_file_icon_data_url":
|
||||
return await this.#getFileIconDataUrl(args);
|
||||
case "start_oem_cloud_oauth_callback_bridge":
|
||||
return await this.#startOemCloudOAuthCallbackBridge();
|
||||
case "aster_agent_init":
|
||||
return await this.#initAgentRuntime();
|
||||
case "get_default_provider":
|
||||
return await this.#getDefaultProvider();
|
||||
case "get_provider_ui_state":
|
||||
return await this.#getProviderUiState(args);
|
||||
case "set_provider_ui_state":
|
||||
return await this.#setProviderUiState(args);
|
||||
case "agent_runtime_list_sessions":
|
||||
return await this.#listAgentRuntimeSessions(args);
|
||||
case "agent_runtime_get_session":
|
||||
@@ -171,10 +195,6 @@ export class ElectronHostCommands {
|
||||
return await this.#getAgentRuntimeToolInventory(args);
|
||||
case "agent_runtime_list_workspace_skill_bindings":
|
||||
return await this.#listWorkspaceSkillBindings(args);
|
||||
case "get_api_key_providers":
|
||||
return await this.#listModelProviders();
|
||||
case "get_system_provider_catalog":
|
||||
return await this.#listModelProviderCatalog();
|
||||
case "get_model_registry":
|
||||
return await this.#listModels();
|
||||
case "get_model_preferences":
|
||||
@@ -187,8 +207,6 @@ export class ElectronHostCommands {
|
||||
return await this.#listModelsForProvider(args);
|
||||
case "get_models_by_tier":
|
||||
return await this.#listModelsByTier(args);
|
||||
case "fetch_provider_models_auto":
|
||||
return await this.#fetchProviderModelsAuto(args);
|
||||
case "get_provider_alias_config":
|
||||
return await this.#readProviderAliasConfig(args);
|
||||
case "get_all_alias_configs":
|
||||
@@ -221,14 +239,6 @@ export class ElectronHostCommands {
|
||||
return await this.#listLocalSkillsForApp(args);
|
||||
case "get_skill_detail":
|
||||
return await this.#readSkillDetail(args);
|
||||
case "get_automation_scheduler_config":
|
||||
return await this.#getAutomationSchedulerConfig();
|
||||
case "get_automation_status":
|
||||
return this.#getAutomationStatus();
|
||||
case "get_automation_health":
|
||||
return await this.#getAutomationHealth();
|
||||
case "get_automation_jobs":
|
||||
return await this.#listAutomationJobs();
|
||||
case "get_usage_stats":
|
||||
return this.#getUsageStats();
|
||||
case "get_model_usage_ranking":
|
||||
@@ -254,11 +264,15 @@ export class ElectronHostCommands {
|
||||
case "unified_memory_stats":
|
||||
return this.#getUnifiedMemoryStats();
|
||||
case "get_mcp_servers":
|
||||
return await this.#listMcpServers();
|
||||
case "mcp_list_servers_with_status":
|
||||
return await this.#listMcpServersWithStatus();
|
||||
case "mcp_list_tools":
|
||||
return await this.#listMcpTools();
|
||||
case "mcp_list_prompts":
|
||||
return await this.#listMcpPrompts();
|
||||
case "mcp_list_resources":
|
||||
return this.#emptyDiagnosticList(command);
|
||||
return await this.#listMcpResources();
|
||||
case "site_get_adapter_catalog_status":
|
||||
return this.#getSiteAdapterCatalogStatus();
|
||||
case "site_list_adapters":
|
||||
@@ -327,6 +341,136 @@ export class ElectronHostCommands {
|
||||
return {};
|
||||
}
|
||||
|
||||
#revealInFinder(args: HostArgs): Record<string, never> {
|
||||
const request = readRequest(args);
|
||||
const targetPath = readRequiredString(request, "path");
|
||||
shell.showItemInFolder(targetPath);
|
||||
return {};
|
||||
}
|
||||
|
||||
async #openWithDefaultApp(args: HostArgs): Promise<Record<string, never>> {
|
||||
const request = readRequest(args);
|
||||
const targetPath = readRequiredString(request, "path");
|
||||
const errorMessage = await shell.openPath(targetPath);
|
||||
if (errorMessage) {
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
async #getFileIconDataUrl(args: HostArgs): Promise<string | null> {
|
||||
const request = readRequest(args);
|
||||
const targetPath = readRequiredString(request, "path");
|
||||
try {
|
||||
const icon = await app.getFileIcon(targetPath, { size: "normal" });
|
||||
if (icon.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return icon.toDataURL() || null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#getHomeDir(): string {
|
||||
const homePath = readElectronPath("home");
|
||||
if (!homePath) {
|
||||
throw new Error("无法获取主目录");
|
||||
}
|
||||
return homePath;
|
||||
}
|
||||
|
||||
async #getFileManagerLocations(): Promise<FileManagerLocation[]> {
|
||||
const locations: FileManagerLocation[] = [];
|
||||
const seenPaths = new Set<string>();
|
||||
const homePath = readElectronPath("home");
|
||||
|
||||
await appendFileManagerLocation(locations, seenPaths, {
|
||||
id: "home",
|
||||
label: "个人",
|
||||
kind: "home",
|
||||
path: homePath,
|
||||
});
|
||||
await appendFileManagerLocation(locations, seenPaths, {
|
||||
id: "desktop",
|
||||
label: "桌面",
|
||||
kind: "desktop",
|
||||
path: readElectronPath("desktop"),
|
||||
});
|
||||
await appendFileManagerLocation(locations, seenPaths, {
|
||||
id: "documents",
|
||||
label: "文档",
|
||||
kind: "documents",
|
||||
path: readElectronPath("documents"),
|
||||
});
|
||||
await appendFileManagerLocation(locations, seenPaths, {
|
||||
id: "downloads",
|
||||
label: "下载",
|
||||
kind: "downloads",
|
||||
path: readElectronPath("downloads"),
|
||||
});
|
||||
|
||||
if (process.platform === "darwin") {
|
||||
await appendFileManagerLocation(locations, seenPaths, {
|
||||
id: "applications",
|
||||
label: "应用程序",
|
||||
kind: "applications",
|
||||
path: "/Applications",
|
||||
});
|
||||
await appendFileManagerLocation(locations, seenPaths, {
|
||||
id: "user-applications",
|
||||
label: "用户应用程序",
|
||||
kind: "applications",
|
||||
path: homePath ? path.join(homePath, "Applications") : null,
|
||||
});
|
||||
}
|
||||
|
||||
if (process.platform === "win32") {
|
||||
await appendFileManagerLocation(locations, seenPaths, {
|
||||
id: "start-menu-programs",
|
||||
label: "应用程序",
|
||||
kind: "applications",
|
||||
path: process.env.APPDATA
|
||||
? path.join(
|
||||
process.env.APPDATA,
|
||||
"Microsoft",
|
||||
"Windows",
|
||||
"Start Menu",
|
||||
"Programs",
|
||||
)
|
||||
: null,
|
||||
});
|
||||
await appendFileManagerLocation(locations, seenPaths, {
|
||||
id: "common-start-menu-programs",
|
||||
label: "公共应用程序",
|
||||
kind: "applications",
|
||||
path: process.env.PROGRAMDATA
|
||||
? path.join(
|
||||
process.env.PROGRAMDATA,
|
||||
"Microsoft",
|
||||
"Windows",
|
||||
"Start Menu",
|
||||
"Programs",
|
||||
)
|
||||
: null,
|
||||
});
|
||||
await appendFileManagerLocation(locations, seenPaths, {
|
||||
id: "program-files",
|
||||
label: "Program Files",
|
||||
kind: "applications",
|
||||
path: process.env.ProgramFiles || null,
|
||||
});
|
||||
await appendFileManagerLocation(locations, seenPaths, {
|
||||
id: "program-files-x86",
|
||||
label: "Program Files (x86)",
|
||||
kind: "applications",
|
||||
path: process.env["ProgramFiles(x86)"] || null,
|
||||
});
|
||||
}
|
||||
|
||||
return locations;
|
||||
}
|
||||
|
||||
async #startOemCloudOAuthCallbackBridge(): Promise<{
|
||||
callbackUrl: string;
|
||||
}> {
|
||||
@@ -779,36 +923,6 @@ export class ElectronHostCommands {
|
||||
});
|
||||
}
|
||||
|
||||
async #getProviderUiState(args: HostArgs): Promise<string | null> {
|
||||
const key = readString(args, "key") ?? readString(readRequest(args), "key");
|
||||
if (!key) {
|
||||
return null;
|
||||
}
|
||||
const state = toRecord((await this.#readConfig()).provider_ui_state);
|
||||
const value = state?.[key];
|
||||
return typeof value === "string" ? value : null;
|
||||
}
|
||||
|
||||
async #setProviderUiState(args: HostArgs): Promise<void> {
|
||||
const request = readRequest(args);
|
||||
const key = readString(request, "key");
|
||||
if (!key) {
|
||||
throw new Error("set_provider_ui_state requires key");
|
||||
}
|
||||
const value = readString(request, "value") ?? "";
|
||||
const config = await this.#readConfig();
|
||||
const state = {
|
||||
...(toRecord(config.provider_ui_state) ?? {}),
|
||||
[key]: value,
|
||||
};
|
||||
await this.#saveConfig({
|
||||
config: {
|
||||
...config,
|
||||
provider_ui_state: state,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async #listAgentRuntimeSessions(args: HostArgs): Promise<unknown[]> {
|
||||
const request = readRequest(args);
|
||||
const params: AppServerParams = {
|
||||
@@ -1137,6 +1251,41 @@ export class ElectronHostCommands {
|
||||
return response.syncState;
|
||||
}
|
||||
|
||||
async #listMcpServers(): Promise<unknown[]> {
|
||||
const response = await this.#appServerRequest<McpServerListResponse>(
|
||||
METHOD_MCP_SERVER_LIST,
|
||||
);
|
||||
return response.servers;
|
||||
}
|
||||
|
||||
async #listMcpServersWithStatus(): Promise<unknown[]> {
|
||||
const response = await this.#appServerRequest<McpServerStatusListResponse>(
|
||||
METHOD_MCP_SERVER_STATUS_LIST,
|
||||
);
|
||||
return response.servers;
|
||||
}
|
||||
|
||||
async #listMcpTools(): Promise<unknown[]> {
|
||||
const response = await this.#appServerRequest<McpToolListResponse>(
|
||||
METHOD_MCP_TOOL_LIST,
|
||||
);
|
||||
return response.tools;
|
||||
}
|
||||
|
||||
async #listMcpPrompts(): Promise<unknown[]> {
|
||||
const response = await this.#appServerRequest<McpPromptListResponse>(
|
||||
METHOD_MCP_PROMPT_LIST,
|
||||
);
|
||||
return response.prompts;
|
||||
}
|
||||
|
||||
async #listMcpResources(): Promise<unknown[]> {
|
||||
const response = await this.#appServerRequest<McpResourceListResponse>(
|
||||
METHOD_MCP_RESOURCE_LIST,
|
||||
);
|
||||
return response.resources;
|
||||
}
|
||||
|
||||
async #listModelRegistryProviderIds(): Promise<string[]> {
|
||||
await this.#appServerRequest<ModelProviderCatalogListResponse>(
|
||||
METHOD_MODEL_PROVIDER_CATALOG_LIST,
|
||||
@@ -1163,28 +1312,6 @@ export class ElectronHostCommands {
|
||||
return await this.#listModels({ tier });
|
||||
}
|
||||
|
||||
async #fetchProviderModelsAuto(
|
||||
args: HostArgs,
|
||||
): Promise<Record<string, unknown>> {
|
||||
const request = readRequest(args);
|
||||
const providerId =
|
||||
readString(request, "providerId") ?? readString(request, "provider_id");
|
||||
if (!providerId) {
|
||||
throw new Error("fetch_provider_models_auto requires providerId");
|
||||
}
|
||||
return {
|
||||
models: [],
|
||||
source: "Error",
|
||||
error: "实时 Provider 模型接口尚未迁入 App Server。",
|
||||
request_url: null,
|
||||
diagnostic_hint:
|
||||
"当前 Electron Desktop Host 不能把 App Server model/list 快照伪装成实时 /models 结果;请先手动添加模型 ID,后续应补 App Server Provider 实时探测方法。",
|
||||
error_kind: "other",
|
||||
should_prompt_error: true,
|
||||
from_cache: false,
|
||||
};
|
||||
}
|
||||
|
||||
async #readProviderAliasConfig(args: HostArgs): Promise<unknown | null> {
|
||||
const request = readRequest(args);
|
||||
const provider = readString(request, "provider");
|
||||
@@ -1429,61 +1556,6 @@ export class ElectronHostCommands {
|
||||
);
|
||||
}
|
||||
|
||||
async #listAutomationJobs(): Promise<unknown[]> {
|
||||
const response = await this.#appServerRequest<AutomationJobListResponse>(
|
||||
METHOD_AUTOMATION_JOB_LIST,
|
||||
);
|
||||
return response.jobs;
|
||||
}
|
||||
|
||||
async #getAutomationSchedulerConfig(): Promise<Record<string, unknown>> {
|
||||
const config = await this.#readConfig();
|
||||
const automationConfig = toRecord(config.automation);
|
||||
return {
|
||||
enabled: readBoolean(automationConfig, "enabled") ?? false,
|
||||
poll_interval_secs:
|
||||
readNumber(automationConfig, "poll_interval_secs") ?? 30,
|
||||
enable_history: readBoolean(automationConfig, "enable_history") ?? true,
|
||||
};
|
||||
}
|
||||
|
||||
#getAutomationStatus(): Record<string, unknown> {
|
||||
return {
|
||||
running: false,
|
||||
last_polled_at: null,
|
||||
next_poll_at: null,
|
||||
last_job_count: 0,
|
||||
total_executions: 0,
|
||||
active_job_id: null,
|
||||
active_job_name: null,
|
||||
};
|
||||
}
|
||||
|
||||
async #getAutomationHealth(): Promise<Record<string, unknown>> {
|
||||
const jobs = await this.#listAutomationJobs();
|
||||
return {
|
||||
total_jobs: jobs.length,
|
||||
enabled_jobs: jobs.filter((job) => readBoolean(job, "enabled") ?? false)
|
||||
.length,
|
||||
pending_jobs: 0,
|
||||
running_jobs: jobs.filter(
|
||||
(job) => readString(job, "last_status") === "running",
|
||||
).length,
|
||||
failed_jobs: jobs.filter((job) =>
|
||||
["error", "timeout"].includes(readString(job, "last_status") ?? ""),
|
||||
).length,
|
||||
cooldown_jobs: jobs.filter((job) =>
|
||||
Boolean(readString(job, "auto_disabled_until")),
|
||||
).length,
|
||||
stale_running_jobs: 0,
|
||||
failed_last_24h: 0,
|
||||
failure_trend_24h: [],
|
||||
alerts: [],
|
||||
risky_jobs: [],
|
||||
generated_at: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
#getUsageStats(): Record<string, unknown> {
|
||||
return {
|
||||
total_conversations: 0,
|
||||
@@ -1884,6 +1956,36 @@ function readRequiredString(value: unknown, key: string): string {
|
||||
return next;
|
||||
}
|
||||
|
||||
function readElectronPath(name: ElectronKnownPathName): string | null {
|
||||
try {
|
||||
const next = app.getPath(name);
|
||||
return next.trim() ? next : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function appendFileManagerLocation(
|
||||
locations: FileManagerLocation[],
|
||||
seenPaths: Set<string>,
|
||||
location: FileManagerLocationCandidate,
|
||||
): Promise<void> {
|
||||
const normalizedPath = location.path?.trim() ?? "";
|
||||
if (!normalizedPath || seenPaths.has(normalizedPath)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const metadata = await stat(normalizedPath);
|
||||
if (!metadata.isDirectory()) {
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
seenPaths.add(normalizedPath);
|
||||
locations.push({ ...location, path: normalizedPath });
|
||||
}
|
||||
|
||||
function readBoolean(value: unknown, key: string): boolean | null {
|
||||
const record = toRecord(value);
|
||||
if (!record || typeof record[key] !== "boolean") {
|
||||
|
||||
@@ -35,16 +35,18 @@ describe("electron/ipcChannels", () => {
|
||||
expect(
|
||||
isElectronHostCommand("agent_runtime_list_workspace_skill_bindings"),
|
||||
).toBe(true);
|
||||
expect(isElectronHostCommand("fetch_provider_models_auto")).toBe(true);
|
||||
expect(isElectronHostCommand("get_provider_ui_state")).toBe(true);
|
||||
expect(isElectronHostCommand("fetch_provider_models_auto")).toBe(false);
|
||||
expect(isElectronHostCommand("get_provider_ui_state")).toBe(false);
|
||||
expect(isElectronHostCommand("get_local_skills_for_app")).toBe(true);
|
||||
expect(isElectronHostCommand("get_automation_jobs")).toBe(true);
|
||||
expect(isElectronHostCommand("get_automation_scheduler_config")).toBe(true);
|
||||
expect(isElectronHostCommand("get_automation_status")).toBe(true);
|
||||
expect(isElectronHostCommand("get_automation_health")).toBe(true);
|
||||
expect(isElectronHostCommand("get_automation_jobs")).toBe(false);
|
||||
expect(isElectronHostCommand("get_automation_scheduler_config")).toBe(
|
||||
false,
|
||||
);
|
||||
expect(isElectronHostCommand("get_automation_status")).toBe(false);
|
||||
expect(isElectronHostCommand("get_automation_health")).toBe(false);
|
||||
expect(isElectronHostCommand("project_memory_get")).toBe(true);
|
||||
expect(isElectronHostCommand("get_model_registry")).toBe(true);
|
||||
expect(isElectronHostCommand("get_api_key_providers")).toBe(true);
|
||||
expect(isElectronHostCommand("get_api_key_providers")).toBe(false);
|
||||
expect(isElectronHostCommand("list_dir")).toBe(false);
|
||||
expect(isElectronHostCommand("read_file_preview_cmd")).toBe(false);
|
||||
expect(isElectronHostCommand("list_executable_skills")).toBe(true);
|
||||
@@ -53,6 +55,9 @@ describe("electron/ipcChannels", () => {
|
||||
expect(isElectronHostCommand("get_config")).toBe(true);
|
||||
expect(isElectronHostCommand("get_experimental_config")).toBe(true);
|
||||
expect(isElectronHostCommand("save_experimental_config")).toBe(true);
|
||||
expect(isElectronHostCommand("get_file_icon_data_url")).toBe(true);
|
||||
expect(isElectronHostCommand("get_file_manager_locations")).toBe(true);
|
||||
expect(isElectronHostCommand("get_home_dir")).toBe(true);
|
||||
expect(isElectronHostCommand("agent_app_list_installed")).toBe(true);
|
||||
expect(isElectronHostCommand("agent_app_runtime_start_task")).toBe(true);
|
||||
expect(isElectronHostCommand("agent_app_runtime_cancel_task")).toBe(true);
|
||||
@@ -84,6 +89,8 @@ describe("electron/ipcChannels", () => {
|
||||
expect(isElectronHostCommand("mcp_list_servers_with_status")).toBe(true);
|
||||
expect(isElectronHostCommand("site_get_adapter_catalog_status")).toBe(true);
|
||||
expect(isElectronHostCommand("open_external_url")).toBe(true);
|
||||
expect(isElectronHostCommand("open_with_default_app")).toBe(true);
|
||||
expect(isElectronHostCommand("reveal_in_finder")).toBe(true);
|
||||
expect(isElectronHostCommand("start_oem_cloud_oauth_callback_bridge")).toBe(
|
||||
true,
|
||||
);
|
||||
@@ -140,15 +147,10 @@ describe("electron/ipcChannels", () => {
|
||||
"agent_runtime_submit_turn",
|
||||
"agent_runtime_update_session",
|
||||
"aster_agent_init",
|
||||
"fetch_provider_models_auto",
|
||||
"get_all_alias_configs",
|
||||
"get_api_key_providers",
|
||||
"get_automation_health",
|
||||
"get_automation_jobs",
|
||||
"get_automation_scheduler_config",
|
||||
"get_automation_status",
|
||||
"get_default_provider",
|
||||
"get_local_skills_for_app",
|
||||
"get_mcp_servers",
|
||||
"get_model_preferences",
|
||||
"get_model_registry",
|
||||
"get_model_registry_provider_ids",
|
||||
@@ -157,9 +159,12 @@ describe("electron/ipcChannels", () => {
|
||||
"get_models_for_provider",
|
||||
"get_provider_alias_config",
|
||||
"get_skill_detail",
|
||||
"get_system_provider_catalog",
|
||||
"knowledge_list_packs",
|
||||
"list_executable_skills",
|
||||
"mcp_list_prompts",
|
||||
"mcp_list_resources",
|
||||
"mcp_list_servers_with_status",
|
||||
"mcp_list_tools",
|
||||
"project_memory_get",
|
||||
"workspace_ensure_default_ready",
|
||||
"workspace_ensure_ready",
|
||||
@@ -185,6 +190,21 @@ describe("electron/ipcChannels", () => {
|
||||
"start_oem_cloud_oauth_callback_bridge" as never,
|
||||
),
|
||||
).toBe(false);
|
||||
expect(
|
||||
ELECTRON_APP_SERVER_TRUTH_BRIDGE_COMMANDS.includes(
|
||||
"get_file_icon_data_url" as never,
|
||||
),
|
||||
).toBe(false);
|
||||
expect(
|
||||
ELECTRON_APP_SERVER_TRUTH_BRIDGE_COMMANDS.includes(
|
||||
"get_file_manager_locations" as never,
|
||||
),
|
||||
).toBe(false);
|
||||
expect(
|
||||
ELECTRON_APP_SERVER_TRUTH_BRIDGE_COMMANDS.includes(
|
||||
"get_home_dir" as never,
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("IPC channel 命名保持 app 前缀,避免裸 ipcRenderer 通道扩散", () => {
|
||||
|
||||
+10
-16
@@ -34,12 +34,6 @@ export const ELECTRON_HOST_COMMANDS = [
|
||||
"agent_app_runtime_cancel_task",
|
||||
"agent_app_runtime_get_task",
|
||||
"agent_app_runtime_submit_host_response",
|
||||
"fetch_provider_models_auto",
|
||||
"get_api_key_providers",
|
||||
"get_automation_health",
|
||||
"get_automation_jobs",
|
||||
"get_automation_scheduler_config",
|
||||
"get_automation_status",
|
||||
"get_browser_backend_policy",
|
||||
"get_browser_backends_status",
|
||||
"get_browser_connector_install_status_cmd",
|
||||
@@ -52,6 +46,9 @@ export const ELECTRON_HOST_COMMANDS = [
|
||||
"get_default_provider",
|
||||
"get_environment_preview",
|
||||
"get_experimental_config",
|
||||
"get_file_icon_data_url",
|
||||
"get_file_manager_locations",
|
||||
"get_home_dir",
|
||||
"get_all_alias_configs",
|
||||
"get_asr_credentials",
|
||||
"get_local_skills_for_app",
|
||||
@@ -65,9 +62,7 @@ export const ELECTRON_HOST_COMMANDS = [
|
||||
"get_models_for_provider",
|
||||
"get_or_create_default_project",
|
||||
"get_provider_alias_config",
|
||||
"get_provider_ui_state",
|
||||
"get_skill_package_file_association_status",
|
||||
"get_system_provider_catalog",
|
||||
"get_usage_stats",
|
||||
"get_voice_input_config",
|
||||
"get_voice_instructions",
|
||||
@@ -81,13 +76,14 @@ export const ELECTRON_HOST_COMMANDS = [
|
||||
"mcp_list_servers_with_status",
|
||||
"mcp_list_tools",
|
||||
"open_external_url",
|
||||
"open_with_default_app",
|
||||
"project_memory_get",
|
||||
"reveal_in_finder",
|
||||
"report_frontend_crash",
|
||||
"report_frontend_debug_log",
|
||||
"save_config",
|
||||
"save_experimental_config",
|
||||
"set_skill_package_file_association_default",
|
||||
"set_provider_ui_state",
|
||||
"site_get_adapter_catalog_status",
|
||||
"site_list_adapters",
|
||||
"start_oem_cloud_oauth_callback_bridge",
|
||||
@@ -150,15 +146,10 @@ export const ELECTRON_APP_SERVER_TRUTH_BRIDGE_COMMANDS = [
|
||||
"agent_runtime_list_sessions",
|
||||
"agent_runtime_get_session",
|
||||
"agent_runtime_list_workspace_skill_bindings",
|
||||
"fetch_provider_models_auto",
|
||||
"get_api_key_providers",
|
||||
"get_all_alias_configs",
|
||||
"get_automation_health",
|
||||
"get_automation_jobs",
|
||||
"get_automation_scheduler_config",
|
||||
"get_automation_status",
|
||||
"get_default_provider",
|
||||
"get_local_skills_for_app",
|
||||
"get_mcp_servers",
|
||||
"get_model_preferences",
|
||||
"get_model_registry",
|
||||
"get_model_registry_provider_ids",
|
||||
@@ -166,10 +157,13 @@ export const ELECTRON_APP_SERVER_TRUTH_BRIDGE_COMMANDS = [
|
||||
"get_models_by_tier",
|
||||
"get_models_for_provider",
|
||||
"get_provider_alias_config",
|
||||
"get_system_provider_catalog",
|
||||
"get_skill_detail",
|
||||
"knowledge_list_packs",
|
||||
"list_executable_skills",
|
||||
"mcp_list_prompts",
|
||||
"mcp_list_resources",
|
||||
"mcp_list_servers_with_status",
|
||||
"mcp_list_tools",
|
||||
"project_memory_get",
|
||||
"workspace_ensure_default_ready",
|
||||
"workspace_ensure_ready",
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ import {
|
||||
type SaveDialogOptions,
|
||||
Tray,
|
||||
type IpcMainInvokeEvent,
|
||||
} from "electron";
|
||||
} from "./electronRuntime";
|
||||
import { spawn } from "node:child_process";
|
||||
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
@@ -0,0 +1,282 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import {
|
||||
ELECTRON_HOST_COMMANDS,
|
||||
IPC_DEEP_LINK_GET_CURRENT_CHANNEL,
|
||||
IPC_DEEP_LINK_GET_URLS_CHANNEL,
|
||||
IPC_DIALOG_OPEN_CHANNEL,
|
||||
IPC_DIALOG_SAVE_CHANNEL,
|
||||
IPC_EMIT_CHANNEL,
|
||||
IPC_GLOBAL_SHORTCUT_COMMAND_CHANNEL,
|
||||
IPC_INVOKE_CHANNEL,
|
||||
IPC_SHELL_OPEN_CHANNEL,
|
||||
IPC_WINDOW_COMMAND_CHANNEL,
|
||||
} from "./ipcChannels";
|
||||
|
||||
const exposedGlobals = new Map<string, unknown>();
|
||||
const ipcInvoke = vi.hoisted(() => vi.fn());
|
||||
const ipcSend = vi.hoisted(() => vi.fn());
|
||||
const ipcOn = vi.hoisted(() => vi.fn());
|
||||
const ipcRemoveListener = vi.hoisted(() => vi.fn());
|
||||
const exposeInMainWorld = vi.hoisted(() =>
|
||||
vi.fn((key: string, value: unknown) => {
|
||||
exposedGlobals.set(key, value);
|
||||
}),
|
||||
);
|
||||
|
||||
vi.mock("electron", () => ({
|
||||
contextBridge: {
|
||||
exposeInMainWorld,
|
||||
},
|
||||
ipcRenderer: {
|
||||
invoke: ipcInvoke,
|
||||
send: ipcSend,
|
||||
on: ipcOn,
|
||||
removeListener: ipcRemoveListener,
|
||||
},
|
||||
}));
|
||||
|
||||
type PreloadApi = {
|
||||
devBridgeFallback: false;
|
||||
supportsCommand(command: string): boolean;
|
||||
invoke<T = unknown>(
|
||||
command: string,
|
||||
args?: Record<string, unknown>,
|
||||
): Promise<T>;
|
||||
listen(event: string, handler: (event: unknown) => void): () => void;
|
||||
on(event: string, handler: (event: unknown) => void): () => void;
|
||||
emit(event: string, payload?: unknown): Promise<void>;
|
||||
send(event: string, payload?: unknown): void;
|
||||
convertFileSrc(filePath: string, protocol?: string): string;
|
||||
dialog: {
|
||||
open(options?: unknown): Promise<unknown>;
|
||||
save(options?: unknown): Promise<unknown>;
|
||||
};
|
||||
shell: {
|
||||
open(target: string, openWith?: string): Promise<unknown>;
|
||||
};
|
||||
window: {
|
||||
show(): Promise<unknown>;
|
||||
setTitle(title: string): Promise<unknown>;
|
||||
setSize(width: number, height: number): Promise<unknown>;
|
||||
isVisible(): Promise<boolean>;
|
||||
};
|
||||
globalShortcut: {
|
||||
register(shortcut: string): Promise<unknown>;
|
||||
unregister(shortcut: string): Promise<unknown>;
|
||||
unregisterAll(): Promise<unknown>;
|
||||
isRegistered(shortcut: string): Promise<unknown>;
|
||||
};
|
||||
deepLink: {
|
||||
onOpenUrl(handler: (urls: string[]) => void): () => void;
|
||||
getUrls(): Promise<unknown>;
|
||||
getCurrent(): Promise<unknown>;
|
||||
};
|
||||
};
|
||||
|
||||
async function loadPreloadApi(): Promise<PreloadApi> {
|
||||
vi.resetModules();
|
||||
exposedGlobals.clear();
|
||||
await import("./preload");
|
||||
const api = exposedGlobals.get("electronAPI");
|
||||
if (!api || typeof api !== "object") {
|
||||
throw new Error("preload did not expose electronAPI");
|
||||
}
|
||||
return api as PreloadApi;
|
||||
}
|
||||
|
||||
describe("electron/preload", () => {
|
||||
beforeEach(() => {
|
||||
ipcInvoke.mockReset();
|
||||
ipcSend.mockReset();
|
||||
ipcOn.mockReset();
|
||||
ipcRemoveListener.mockReset();
|
||||
exposeInMainWorld.mockClear();
|
||||
exposedGlobals.clear();
|
||||
});
|
||||
|
||||
it("暴露真实 Electron bridge 标记并用 host command 白名单判断支持命令", async () => {
|
||||
const api = await loadPreloadApi();
|
||||
|
||||
expect(exposeInMainWorld).toHaveBeenCalledWith("electronAPI", api);
|
||||
expect(exposeInMainWorld).toHaveBeenCalledWith("__LIME_ELECTRON__", true);
|
||||
expect(api.devBridgeFallback).toBe(false);
|
||||
expect(api.supportsCommand(ELECTRON_HOST_COMMANDS[0])).toBe(true);
|
||||
expect(api.supportsCommand("capability_draft_list_registered_skills")).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
it("通过统一 app:invoke 通道转发命令并解包成功响应", async () => {
|
||||
const api = await loadPreloadApi();
|
||||
ipcInvoke.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
result: { lines: ["{}"] },
|
||||
});
|
||||
|
||||
await expect(
|
||||
api.invoke("app_server_handle_json_lines", { lines: ["{}"] }),
|
||||
).resolves.toEqual({ lines: ["{}"] });
|
||||
expect(ipcInvoke).toHaveBeenCalledWith(
|
||||
IPC_INVOKE_CHANNEL,
|
||||
"app_server_handle_json_lines",
|
||||
{ lines: ["{}"] },
|
||||
);
|
||||
});
|
||||
|
||||
it("Electron invoke 失败响应必须 fail closed 并保留错误元数据", async () => {
|
||||
const api = await loadPreloadApi();
|
||||
ipcInvoke.mockResolvedValueOnce({
|
||||
ok: false,
|
||||
error: {
|
||||
code: "unsupported_command",
|
||||
message: "Desktop Host 尚未支持命令",
|
||||
data: { command: "legacy_command" },
|
||||
},
|
||||
});
|
||||
|
||||
await expect(api.invoke("legacy_command")).rejects.toMatchObject({
|
||||
message: "Desktop Host 尚未支持命令",
|
||||
code: "unsupported_command",
|
||||
data: { command: "legacy_command" },
|
||||
});
|
||||
});
|
||||
|
||||
it("事件监听统一归一化 payload,并返回反注册函数", async () => {
|
||||
const api = await loadPreloadApi();
|
||||
const handler = vi.fn();
|
||||
|
||||
const dispose = api.listen("agent-runtime:event", handler);
|
||||
const listener = ipcOn.mock.calls[0]?.[1] as
|
||||
| ((_event: unknown, payload: unknown) => void)
|
||||
| undefined;
|
||||
|
||||
expect(ipcOn).toHaveBeenCalledWith("evt:agent-runtime:event", listener);
|
||||
expect(listener).toBeTypeOf("function");
|
||||
|
||||
listener?.({}, { step: "ready" });
|
||||
expect(handler).toHaveBeenCalledWith({
|
||||
event: "agent-runtime:event",
|
||||
payload: { step: "ready" },
|
||||
});
|
||||
|
||||
listener?.({}, { event: "agent-runtime:event", payload: { step: "done" } });
|
||||
expect(handler).toHaveBeenCalledWith({
|
||||
event: "agent-runtime:event",
|
||||
payload: { step: "done" },
|
||||
});
|
||||
|
||||
dispose();
|
||||
expect(ipcRemoveListener).toHaveBeenCalledWith(
|
||||
"evt:agent-runtime:event",
|
||||
listener,
|
||||
);
|
||||
});
|
||||
|
||||
it("桌面壳能力保持固定 IPC channel,不绕过 Electron Host", async () => {
|
||||
const api = await loadPreloadApi();
|
||||
ipcInvoke.mockResolvedValue(undefined);
|
||||
|
||||
await api.dialog.open({ properties: ["openFile"] });
|
||||
await api.dialog.save({ defaultPath: "draft.md" });
|
||||
await api.shell.open("/tmp/draft.md", "Preview");
|
||||
await api.window.show();
|
||||
await api.window.setTitle("Lime");
|
||||
await api.window.setSize(1024, 768);
|
||||
await api.globalShortcut.register("CommandOrControl+L");
|
||||
await api.globalShortcut.unregister("CommandOrControl+L");
|
||||
await api.globalShortcut.unregisterAll();
|
||||
await api.globalShortcut.isRegistered("CommandOrControl+L");
|
||||
await api.deepLink.getUrls();
|
||||
await api.deepLink.getCurrent();
|
||||
api.send("frontend:ready", { ok: true });
|
||||
await api.emit("frontend:loaded");
|
||||
|
||||
expect(ipcInvoke).toHaveBeenCalledWith(IPC_DIALOG_OPEN_CHANNEL, {
|
||||
properties: ["openFile"],
|
||||
});
|
||||
expect(ipcInvoke).toHaveBeenCalledWith(IPC_DIALOG_SAVE_CHANNEL, {
|
||||
defaultPath: "draft.md",
|
||||
});
|
||||
expect(ipcInvoke).toHaveBeenCalledWith(
|
||||
IPC_SHELL_OPEN_CHANNEL,
|
||||
"/tmp/draft.md",
|
||||
"Preview",
|
||||
);
|
||||
expect(ipcInvoke).toHaveBeenCalledWith(
|
||||
IPC_WINDOW_COMMAND_CHANNEL,
|
||||
"show",
|
||||
);
|
||||
expect(ipcInvoke).toHaveBeenCalledWith(
|
||||
IPC_WINDOW_COMMAND_CHANNEL,
|
||||
"setTitle",
|
||||
"Lime",
|
||||
);
|
||||
expect(ipcInvoke).toHaveBeenCalledWith(
|
||||
IPC_WINDOW_COMMAND_CHANNEL,
|
||||
"setSize",
|
||||
1024,
|
||||
768,
|
||||
);
|
||||
expect(ipcInvoke).toHaveBeenCalledWith(
|
||||
IPC_GLOBAL_SHORTCUT_COMMAND_CHANNEL,
|
||||
"register",
|
||||
"CommandOrControl+L",
|
||||
);
|
||||
expect(ipcInvoke).toHaveBeenCalledWith(
|
||||
IPC_GLOBAL_SHORTCUT_COMMAND_CHANNEL,
|
||||
"unregister",
|
||||
"CommandOrControl+L",
|
||||
);
|
||||
expect(ipcInvoke).toHaveBeenCalledWith(
|
||||
IPC_GLOBAL_SHORTCUT_COMMAND_CHANNEL,
|
||||
"unregisterAll",
|
||||
);
|
||||
expect(ipcInvoke).toHaveBeenCalledWith(
|
||||
IPC_GLOBAL_SHORTCUT_COMMAND_CHANNEL,
|
||||
"isRegistered",
|
||||
"CommandOrControl+L",
|
||||
);
|
||||
expect(ipcInvoke).toHaveBeenCalledWith(IPC_DEEP_LINK_GET_URLS_CHANNEL);
|
||||
expect(ipcInvoke).toHaveBeenCalledWith(IPC_DEEP_LINK_GET_CURRENT_CHANNEL);
|
||||
expect(ipcSend).toHaveBeenCalledWith("app:emit", "frontend:ready", {
|
||||
ok: true,
|
||||
});
|
||||
expect(ipcSend).toHaveBeenCalledWith(IPC_EMIT_CHANNEL, "frontend:loaded", undefined);
|
||||
});
|
||||
|
||||
it("deep link 事件只向 renderer 传递数组 URL 列表", async () => {
|
||||
const api = await loadPreloadApi();
|
||||
const handler = vi.fn();
|
||||
|
||||
const dispose = api.deepLink.onOpenUrl(handler);
|
||||
const listener = ipcOn.mock.calls[0]?.[1] as
|
||||
| ((_event: unknown, payload: unknown) => void)
|
||||
| undefined;
|
||||
|
||||
expect(ipcOn).toHaveBeenCalledWith("evt:deep-link-open-url", listener);
|
||||
listener?.({}, { payload: ["lime://connect?id=1"] });
|
||||
listener?.({}, { payload: "lime://connect?id=2" });
|
||||
|
||||
expect(handler).toHaveBeenNthCalledWith(1, ["lime://connect?id=1"]);
|
||||
expect(handler).toHaveBeenNthCalledWith(2, []);
|
||||
|
||||
dispose();
|
||||
expect(ipcRemoveListener).toHaveBeenCalledWith(
|
||||
"evt:deep-link-open-url",
|
||||
listener,
|
||||
);
|
||||
});
|
||||
|
||||
it("convertFileSrc 只处理本地路径,不改写已有 URL", async () => {
|
||||
const api = await loadPreloadApi();
|
||||
|
||||
expect(api.convertFileSrc("https://example.com/a.png")).toBe(
|
||||
"https://example.com/a.png",
|
||||
);
|
||||
expect(api.convertFileSrc("/tmp/a.png")).toBe("file:///tmp/a.png");
|
||||
expect(api.convertFileSrc("/tmp/a b.png", "asset")).toBe(
|
||||
"asset://%2Ftmp%2Fa%20b.png",
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -10,7 +10,7 @@ const { appState, checkForUpdatesMock, quitAndInstallMock, setFeedURLMock } =
|
||||
setFeedURLMock: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("electron", () => ({
|
||||
vi.mock("./electronRuntime", () => ({
|
||||
app: {
|
||||
getVersion: () => "1.60.0",
|
||||
get isPackaged() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { app, autoUpdater } from "electron";
|
||||
import { app, autoUpdater } from "./electronRuntime";
|
||||
|
||||
type UpdateInstallStage =
|
||||
| "idle"
|
||||
|
||||
@@ -85,13 +85,37 @@ Connect deep link 已切到 Electron Desktop Host URL bridge 与 App Server JSON
|
||||
|
||||
旧 `poster_material_*` 命名只允许停留在 schema 迁移与治理守卫中,不应重新出现在前端网关、Rust 命令模块或运行时代码里。
|
||||
|
||||
模型 Provider 真相集同样遵循单一事实源。当前前端入口为 `src/lib/api/modelRegistry.ts` 中的:
|
||||
模型 Provider 真相集同样遵循单一事实源。当前前端入口为 `src/lib/api/modelRegistry.ts` 与 `src/lib/api/apiKeyProvider.ts` 中的 App Server JSON-RPC methods:
|
||||
|
||||
- `get_model_registry_provider_ids`
|
||||
- `model/list`
|
||||
- `modelPreferences/list`
|
||||
- `modelSyncState/read`
|
||||
- `modelProvider/list`
|
||||
- `modelProvider/catalog/list`
|
||||
- `modelProvider/read`
|
||||
- `modelProvider/create`
|
||||
- `modelProvider/update`
|
||||
- `modelProvider/delete`
|
||||
- `modelProvider/sortOrders/update`
|
||||
- `modelProvider/fetchModels`
|
||||
- `modelProvider/testConnection`
|
||||
- `modelProvider/testChat`
|
||||
- `modelProviderConfig/export`
|
||||
- `modelProviderConfig/import`
|
||||
- `modelProviderKey/create`
|
||||
- `modelProviderKey/update`
|
||||
- `modelProviderKey/delete`
|
||||
- `modelProviderKey/next`
|
||||
- `modelProviderKey/usage/record`
|
||||
- `modelProviderKey/error/record`
|
||||
- `modelProviderUiState/read`
|
||||
- `modelProviderUiState/write`
|
||||
- `modelProviderAlias/read`
|
||||
- `modelProviderAlias/list`
|
||||
|
||||
`lime-rs/resources/models` 本地模型 catalog 已下线;Provider 元信息以 `get_system_provider_catalog` 为事实源,模型列表以 Provider 实时 `/models` 接口和用户显式 `custom_models` 为事实源。Provider 实时 `/models` 的成功结果可以由 `ModelRegistryService` 持久化缓存 10 天,所有读取先查缓存再访问上游,但该缓存只保存真实接口结果,不得退化成本地 catalog 兜底。`get_model_registry_provider_ids` 仅作为兼容命令保留空结果,不应再读取本地资源、数据库或运行态缓存去“猜” provider 集合。
|
||||
`lime-rs/resources/models` 本地模型 catalog 已下线;Provider 元信息以 `modelProvider/catalog/list` 为事实源,Provider 配置 / API Key / UI state / 连接测试 / 模型实时拉取均以 `modelProvider*` / `modelProviderKey*` / `modelProviderUiState*` App Server method 为事实源。Provider 实时 `/models` 的成功结果可以由 `ModelRegistryService` 持久化缓存 10 天,所有读取先查缓存再访问上游,但该缓存只保存真实接口结果,不得退化成本地 catalog 兜底。`get_model_registry_provider_ids` 仅作为兼容命令保留空结果,不应再读取本地资源、数据库或运行态缓存去“猜” provider 集合。旧 Provider façade 命令族已判为 `dead`:`get_api_key_providers`、`get_system_provider_catalog`、`get_api_key_provider`、`read_api_key_provider_config`、`add_custom_api_key_provider`、`create_api_key_provider`、`update_api_key_provider`、`delete_custom_api_key_provider`、`delete_api_key_provider`、`update_provider_sort_orders`、`update_api_key_provider_sort_orders`、`export_api_key_providers`、`export_api_key_provider_config`、`import_api_key_providers`、`import_api_key_provider_config`、`test_api_key_provider_connection`、`test_api_key_provider_chat`、`fetch_provider_models_auto`、`add_api_key`、`create_api_key_provider_key`、`delete_api_key`、`delete_api_key_provider_key`、`toggle_api_key`、`update_api_key_alias`、`update_api_key_provider_key`、`get_next_api_key`、`next_api_key_provider_key`、`record_api_key_usage`、`record_api_key_provider_key_usage`、`record_api_key_error`、`record_api_key_provider_key_error`、`get_provider_ui_state`、`read_api_key_provider_ui_state`、`set_provider_ui_state`、`write_api_key_provider_ui_state`;不得重新接回前端网关、Electron Host、DevBridge truth、mock priority、runtime surface、Tauri runner 或 Rust dispatcher。
|
||||
同理,聊天运行时初始化的 `aster_agent_init` 在浏览器 DevBridge 模式下也不能再被放进 `mockPriorityCommands`。只要桥接在线,它就必须优先读取后端真实 `provider_name / model_name`,让聊天入口拿到当前运行时模型。
|
||||
进一步地,围绕运行时模型解析的真相命令:`aster_agent_init`、`get_default_provider`、`get_api_key_providers`、`get_model_registry`、`get_provider_alias_config`、`fetch_provider_models_auto`、`get_model_registry_provider_ids`,在浏览器 DevBridge 模式下如果桥接失败,必须直接抛错,不能再通过 `safeInvoke` 静默退回 mock;否则前端会把“后端未连上 / 命令失败”误显示成假的 Provider / 模型列表。旧 `get_provider_pool_overview` 属于凭证池命令面,已随凭证池退役,不得重新作为运行时模型解析事实源。
|
||||
进一步地,围绕运行时模型解析的真相入口:`aster_agent_init`、`get_default_provider`、`model/list`、`modelProvider/list`、`modelProvider/catalog/list`、`modelProvider/fetchModels`、`modelProviderAlias/read`、`modelProviderAlias/list`、`get_model_registry_provider_ids`,在浏览器 DevBridge 模式下如果桥接失败,必须直接抛错,不能再通过 `safeInvoke` 静默退回 mock;否则前端会把“后端未连上 / 命令失败”误显示成假的 Provider / 模型列表。旧 `get_provider_pool_overview` 属于凭证池命令面,已随凭证池退役,不得重新作为运行时模型解析事实源。
|
||||
同时要明确,`aster_agent_init` 只负责初始化 Agent,并不保证已经完成 Provider 配置;当它未返回 `provider_name / model_name` 时,前端不得把本地硬编码默认值当作真实模型,而应继续回退到 `get_default_provider` + 已配置 Provider/模型注册表解析链,拿到当前工作区真正可用的 `provider/model`。
|
||||
同一条约束也适用于 Prompt Cache 能力判断:运行时与前端都不得因为某个自定义 Provider “长得像 Anthropic 协议”就推断它支持官方 Anthropic Automatic Prompt Caching。当前事实源必须继续按 ProviderType 判断:`anthropic` 走自动缓存能力,`anthropic-compatible` 只保留显式 `cache_control` 语义;若上游没有实现 Automatic Prompt Cache,`cached_input_tokens` 为空不能直接归因到 Lime 没发字段。
|
||||
|
||||
@@ -186,7 +210,7 @@ Claw / Aster 原完整执行链是 Agent 对话 runtime 的 current 参考实现
|
||||
|
||||
前端从本地存储恢复出的 Agent session id 在发送前必须先被 App Server `agentSession/read` 确认存在且归属当前 workspace。明确 `session not found` 或 workspace mismatch 时只能丢弃本地恢复快照并创建新的 App Server session;普通 bridge / network error 必须 fail closed,不得静默创建会话或继续轮询 stale session。Electron `safeInvoke` 返回的 App Server JSON-RPC result envelope 也必须在 `src/lib/api/appServer.ts` 网关层统一解包,业务页面不得各自猜测 `{ result: { lines } }` / `{ lines }` 形状。
|
||||
|
||||
`agent_app_runtime_*` 是 Agent App 进入 App Server / AgentRuntime 主链的 Desktop facade:`agent_app_runtime_start_task` 必须经 App Server JSON-RPC `agentSession/turn/start` 进入 `RuntimeCore -> AsterBackend -> backend host`,不能再直接复制 `AsterChatRequest -> build_queued_turn_task` 提交流程。`startTask.turnConfig` 必须随 facade 透传,并写入 `RuntimeOptions.hostOptions.asterChatRequest`:扁平 `AsterChatRequest` 字段供 Desktop Aster host 恢复 Claw 原链,`turn_config` 镜像供外部 App Server backend 读取 provider_config / system_prompt / reasoning / sandbox 等配置。cancel / read / host response 在 App Server protocol 覆盖前可继续作为 Desktop compat 适配到既有 `agent_runtime_*` 读写命令,但不得复制 Claw `*_skill_launch.rs`、不得新增垂直 `content_factory_*` Agent 命令,也不得把 `LIME_GATEWAY_*` 直接模型调用宣称为完整 Agent 能力。
|
||||
`agent_app_runtime_*` 是 Agent App 进入 App Server / AgentRuntime 主链的 Desktop facade:`agent_app_runtime_start_task` 必须经 App Server JSON-RPC `agentSession/turn/start` 进入 `RuntimeCore -> AsterBackend -> backend host`,不能再直接复制 `AsterChatRequest -> build_queued_turn_task` 提交流程。`startTask.turnConfig` 必须随 facade 透传,并写入 `RuntimeOptions.hostOptions.asterChatRequest`:扁平 `AsterChatRequest` 字段供 Desktop Aster host 恢复 Claw 原链,`turn_config` 镜像供外部 App Server backend 读取 provider*config / system_prompt / reasoning / sandbox 等配置。cancel / read / host response 在 App Server protocol 覆盖前可继续作为 Desktop compat 适配到既有 `agent_runtime*_`读写命令,但不得复制 Claw`**skill_launch.rs`、不得新增垂直 `content_factory**`Agent 命令,也不得把`LIME*GATEWAY*_` 直接模型调用宣称为完整 Agent 能力。
|
||||
|
||||
P17.3 之前禁止真实删除 Agent App 本地数据:`agent_app_uninstall_rehearsal` 只生成 keep-data / delete-data 演练,`agent_app_uninstall` 只能返回同一演练摘要和未删除的 installed list,不得执行 `remove_file` / `remove_dir_all` 或移除 installed state。真实 delete-data 必须等后续路线图单独打开并补齐 evidence / residual audit / confirmation gate。
|
||||
|
||||
@@ -416,7 +440,25 @@ Skill 执行链路同样遵循单一命令边界。当前前端入口为 `src/li
|
||||
|
||||
这些命令如果仍处在 legacy desktop facade 兼容期,也必须继续保持 DevBridge dispatcher 已桥接;current 新能力优先同步 Electron Desktop Host bridge / App Server JSON-RPC,避免浏览器模式、Electron smoke 或 Playwright 续测时回退成 unknown command。
|
||||
|
||||
自动化设置链路同样遵循这条路径。当前主入口为 `src/lib/api/automation.ts`,统一承接:
|
||||
自动化设置链路同样遵循这条路径。当前主入口为 `src/lib/api/automation.ts`,统一经 `AppServerClient.request(...)` 承接:
|
||||
|
||||
- `automationScheduler/config/read`
|
||||
- `automationScheduler/config/update`
|
||||
- `automationScheduler/status`
|
||||
- `automationJob/list`
|
||||
- `automationJob/read`
|
||||
- `automationJob/create`
|
||||
- `automationJob/update`
|
||||
- `automationJob/delete`
|
||||
- `automationJob/runNow`
|
||||
- `automationJob/health`
|
||||
- `automationJob/runHistory`
|
||||
- `automationSchedule/preview`
|
||||
- `automationSchedule/validate`
|
||||
|
||||
这些 App Server JSON-RPC method 属于当前 `设置 -> 系统 -> 自动化` 主路径,缺少必需 result 时必须 fail closed,不得回退 DevBridge mock、renderer mock 或 legacy desktop facade。`automationJob/runNow` 已进入 current 协议,但 App Server 自动化执行器尚未迁完;当前实现必须 fail closed,不能回退旧 Tauri 执行器。
|
||||
|
||||
旧自动化命令统一判为 `dead`,不得重新接回前端网关、Electron Desktop Host 白名单、Rust `generate_handler!`、DevBridge dispatcher、`mockPriorityCommands` 或默认 mock:
|
||||
|
||||
- `get_automation_scheduler_config`
|
||||
- `update_automation_scheduler_config`
|
||||
@@ -432,8 +474,6 @@ Skill 执行链路同样遵循单一命令边界。当前前端入口为 `src/li
|
||||
- `preview_automation_schedule`
|
||||
- `validate_automation_schedule`
|
||||
|
||||
这些命令属于当前 `设置 -> 系统 -> 自动化` 主路径。浏览器模式下如已接通 DevBridge,应优先走真实后端;不要因为 dispatcher 漏接而长期依赖 mock 掩盖设置页报错。
|
||||
|
||||
Companion 桌宠链路同样遵循这条路径。当前主入口为 `src/lib/api/companion.ts`,统一承接:
|
||||
|
||||
- `companion_get_pet_status`
|
||||
@@ -632,7 +672,7 @@ npm run verify:local
|
||||
|
||||
## 自动化 `agent_turn` 负载补充约定
|
||||
|
||||
当 `create_automation_job` / `update_automation_job` 的 `payload.kind = "agent_turn"` 用于持续产出交付物时,允许并推荐透传以下字段:
|
||||
当 `automationJob/create` / `automationJob/update` 的 `payload.kind = "agent_turn"` 用于持续产出交付物时,允许并推荐透传以下字段:
|
||||
|
||||
- `content_id`:绑定长期内容主线,供自动化版本持续沉淀到同一交付链
|
||||
- `request_metadata`:与运行时 turn 保持同合同,至少可包含 `artifact` 与 `harness` 两层
|
||||
|
||||
@@ -435,7 +435,7 @@ npm run verify:gui-smoke -- --include-knowledge-product-e2e --reuse-running
|
||||
|
||||
1. 进入 `设置 -> 系统 -> 自动化`
|
||||
2. 确认调度状态、任务列表、健康面板能正常加载
|
||||
3. 打开控制台,确认浏览器模式接通 DevBridge 时不再出现 `get_automation_jobs`、`get_automation_health` 或 `get_automation_run_history` 的 unknown command 报错
|
||||
3. 打开控制台 / 网络证据,确认自动化读取经 `app_server_handle_json_lines` 观察到 `automationJob/list`、`automationJob/health` 或 `automationJob/runHistory` 等 App Server method,且没有旧 automation 命令族调用
|
||||
4. 如当前环境允许创建或编辑任务,再确认提交后列表能刷新,而不是只靠 web mock 静态回显
|
||||
|
||||
### 话题模型恢复验证
|
||||
|
||||
@@ -359,7 +359,7 @@ npm run verify:gui-smoke -- --include-knowledge-product-e2e --reuse-running
|
||||
|
||||
这类问题 **单靠** `lint`、`typecheck`、`vitest` 无法覆盖。
|
||||
|
||||
默认 `npm run verify:local`、`npm test`、`cargo test --manifest-path "lime-rs/Cargo.toml"` 与 `npm run verify:gui-smoke` 不允许消耗真实模型 / 图片 Provider 额度。会调用 `agent_runtime_submit_turn`、`test_api_key_provider_chat`、图片生成、embedding、ASR 或 live AgentRuntime transcript 的测试 / smoke,必须显式 opt-in:
|
||||
默认 `npm run verify:local`、`npm test`、`cargo test --manifest-path "lime-rs/Cargo.toml"` 与 `npm run verify:gui-smoke` 不允许消耗真实模型 / 图片 Provider 额度。会调用 `agent_runtime_submit_turn`、App Server `modelProvider/testChat`、图片生成、embedding、ASR 或 live AgentRuntime transcript 的测试 / smoke,必须显式 opt-in:
|
||||
|
||||
```bash
|
||||
npm run verify:gui-smoke -- --include-live-provider-smokes
|
||||
@@ -467,7 +467,7 @@ CI 里的 `.github/workflows/quality.yml` 结果摘要现在也会透出 `bridge
|
||||
- 修改首页 / 工作区进入 `Claw` 时的首条自动发送上下文,例如 `initialUserPrompt`、`initialAutoSendRequestMetadata`、`harness.service_skill_launch`
|
||||
- 修改 `site_*` 站点适配器命令族,例如 `site_recommend_adapters`、`site_get_adapter_launch_readiness`、`site_import_adapter_yaml_bundle`、`site_run_adapter`
|
||||
- 修改 `companion_get_pet_status`、`companion_launch_pet`、`companion_send_pet_command`,或调整 Lime 与独立桌宠之间的本地 companion 协议(例如 `pet.provider_overview`、`pet.open_provider_settings`、`pet.request_provider_overview_sync`、`pet.request_pet_cheer`、`pet.request_pet_next_step`、`pet.request_chat_reply`)
|
||||
- 修改自动化设置命令族,例如 `get_automation_jobs`、`create_automation_job`、`update_automation_job`、`get_automation_health` 或 `get_automation_run_history`,尤其是它们在浏览器模式 DevBridge 与 mock 间的分流
|
||||
- 修改自动化设置 App Server method 族,例如 `automationJob/list`、`automationJob/create`、`automationJob/update`、`automationJob/health` 或 `automationJob/runHistory`;旧 `get_automation_*` / `*_automation_job` 命令已退役,只能作为负向回归扫描对象
|
||||
- 修改浏览器资料 / 环境预设命令族,或调整它们在 `mockPriorityCommands` 里的优先级
|
||||
- 修改浏览器连接器命令族,例如安装目录、启用状态、系统连接器、浏览器动作配置、扩展安装状态、打开 Chrome 扩展 / 远程调试页,或主动断开扩展连接
|
||||
- 修改 `get_model_registry_provider_ids` 兼容空结果、Provider 实时模型读取或用户 `custom_models` 合并语义
|
||||
|
||||
@@ -1280,8 +1280,8 @@ git diff --check
|
||||
264. P3.136 文档守卫:`scripts/electron/current-docs-guard.test.mjs` 已把 `frontend-electron-migration.md` 纳入 high-weight current guidance 扫描,并新增 frontend host current contract 守卫,锁定 Electron 只负责 Desktop Host bridge、preload / IPC 白名单、窗口、托盘、Dock、菜单、updater、签名发布和 sidecar 生命周期,不是第二套后端或 Agent runtime adapter;同时锁定 Codex CLI / `codex-rs` 只提供 App Server 分层参考,Lime 不参考 Codex App UI 或桌面壳实现。`internal/roadmap/appserver/README.md` 索引也已从“切换到 Electron”改为 “Electron Desktop Host current 契约”。
|
||||
265. P3.136 增量验证:`npx vitest run "scripts/electron/current-docs-guard.test.mjs" --silent=passed-only --disableConsoleIntercept` 通过,10 个 docs guard 测试;`node --check "scripts/check-app-server-client-contract.mjs" && node "scripts/check-app-server-client-contract.mjs"` 通过,App Server client contract guard 121 项;`npx prettier --check` 覆盖本轮文档与守卫文件通过;`git diff --check` 覆盖本轮认领文件通过;`npm run test:contracts` 完整通过,覆盖 agent runtime clients、App Server client contract、command / harness / modality / cleanup / docs boundary。本轮只改 current 文档和守卫,未改 Electron 运行时代码,因此不重复跑 GUI smoke;下一刀应回到真实 GUI 发送后的 event/read model E2E 或 artifact/evidence UI 接入。
|
||||
266. P3.137 production mock / live-provider gate 收口:继续按“生产不能 mock,只有测试才 mock”规则补 current 主链守卫,本轮只认领 `scripts/check-command-contracts.mjs`、`scripts/check-app-server-client-contract.mjs`、`internal/roadmap/appserver/frontend-integration-matrix.md` 与本执行计划记录。`scripts/check-command-contracts.mjs` 的生产 mock-only 扫描已覆盖 `src / electron / packages`,锁住 Electron Desktop Host main、renderer runtime 与 `packages/app-server-client` 不能导入或调用 `explicitMockFallback`、`invokeMockOnly`、`mockCommand`、`clearMocks`、`invokeExplicitMock`、`listenExplicitMock`;前端命令统计仍只扫描 `src`,避免 Electron main 内部桥接被误判为前端命令调用。
|
||||
267. P3.137 Claw streaming live-provider gate:`scripts/check-app-server-client-contract.mjs` 已锁定 `scripts/claw-chat-ready-streaming-smoke.mjs` 必须在 `wait-health`、`prepare-runtime`、`aster_agent_init`、`get_api_key_providers` 和 `test_api_key_provider_chat` 之前调用 `assertLiveProviderSmokeAllowed(...)`。完整 Claw streaming GUI E2E 必须显式传入 `--allow-live-provider` 或 `LIME_ALLOW_LIVE_PROVIDER_SMOKE=1`;未授权时 smoke 必须 fail closed,不能用 mock、无模型探针或 Provider 自动探测替代真实业务闭环。
|
||||
268. P3.137 未授权 gate 实测:不传 `--allow-live-provider` 运行 `node scripts/claw-chat-ready-streaming-smoke.mjs --timeout-ms 30000 --evidence-dir ".lime/qc/gui-evidence/claw-chat-ready-streaming-gate-check" --prefix "gate-check"`,按预期失败在 live-provider 授权检查,未授权 gate 实测在 `wait-health`、`prepare-runtime` 和 Provider 探测前 fail closed,未进入 `aster_agent_init`、`get_api_key_providers` 或 `test_api_key_provider_chat`。本轮未运行带 `--allow-live-provider` 的真实模型调用,完整真实 GUI sending E2E 仍需用户明确允许真实 Provider 调用后再执行。
|
||||
267. P3.137 Claw streaming live-provider gate:`scripts/check-app-server-client-contract.mjs` 已锁定 `scripts/claw-chat-ready-streaming-smoke.mjs` 必须在 `wait-health`、`prepare-runtime`、`aster_agent_init`、App Server `modelProvider/list` 和 `modelProvider/testChat` 之前调用 `assertLiveProviderSmokeAllowed(...)`。完整 Claw streaming GUI E2E 必须显式传入 `--allow-live-provider` 或 `LIME_ALLOW_LIVE_PROVIDER_SMOKE=1`;未授权时 smoke 必须 fail closed,不能用 mock、无模型探针或 Provider 自动探测替代真实业务闭环。
|
||||
268. P3.137 未授权 gate 实测:不传 `--allow-live-provider` 运行 `node scripts/claw-chat-ready-streaming-smoke.mjs --timeout-ms 30000 --evidence-dir ".lime/qc/gui-evidence/claw-chat-ready-streaming-gate-check" --prefix "gate-check"`,按预期失败在 live-provider 授权检查,未授权 gate 实测在 `wait-health`、`prepare-runtime` 和 Provider 探测前 fail closed,未进入 `aster_agent_init`、App Server `modelProvider/list` 或 `modelProvider/testChat`。本轮未运行带 `--allow-live-provider` 的真实模型调用,完整真实 GUI sending E2E 仍需用户明确允许真实 Provider 调用后再执行。
|
||||
269. P3.137 增量验证:`node --check "scripts/check-app-server-client-contract.mjs"`、`node --check "scripts/check-command-contracts.mjs"`、`node "scripts/check-app-server-client-contract.mjs"`、`node "scripts/check-command-contracts.mjs"`、`npx prettier --check "scripts/check-command-contracts.mjs" "scripts/check-app-server-client-contract.mjs" "internal/roadmap/appserver/frontend-integration-matrix.md" "internal/exec-plans/app-server-implementation-plan.md"`、`git diff --check -- "scripts/check-command-contracts.mjs" "scripts/check-app-server-client-contract.mjs" "internal/roadmap/appserver/frontend-integration-matrix.md" "internal/exec-plans/app-server-implementation-plan.md"` 均通过;`npm run test:contracts` 完整通过,App Server client contract 123 项、command contracts、harness、modality、cleanup 与 docs boundary 均通过。未授权 Claw streaming smoke 的退出码为 1 属预期 gate 结果,不是产品路径通过证据;完整 GUI sending E2E 仍需显式 live-provider 授权后执行。
|
||||
270. P3.138 evidence pack UI gateway current 化:继续沿 `Frontend -> Electron Desktop Host IPC -> App Server JSON-RPC -> RuntimeCore / backend` 主链推进,本轮只认领 `src/lib/api/agentRuntime/exportClient.ts`、`exportClient.d.ts`、`clientFactory.ts`、`clientFactory.d.ts`、`clientFactory.test.ts`、新增 `appServerEvidenceExportProjection.ts` / `.test.ts`、新增 `exportClient.test.ts`、`scripts/check-app-server-client-contract.mjs`、`internal/roadmap/appserver/frontend-integration-matrix.md` 与本执行计划记录。`exportAgentRuntimeEvidencePack(sessionId)` 现在直接调用 `AppServerClient.exportEvidence({ sessionId, includeEvents: true, includeArtifacts: true, includeEvidencePack: true })`,再通过 `projectAppServerEvidenceExportToRuntimeEvidencePack(...)` 投影成旧 UI 消费的 `AgentRuntimeEvidencePack` snake_case 形状;空 `sessionId` 与缺少 `evidencePack` 都 fail closed,不再回退 `agent_runtime_export_evidence_pack` 或 renderer mock。
|
||||
271. P3.138 范围边界:本轮没有新增 App Server method,也没有改 Electron / Rust / package 大写集;只消费既有 `evidence/export` renderer-safe App Server gateway。handoff bundle、analysis handoff、replay case、review decision template 与 save review decision 仍暂走 `AGENT_RUNTIME_COMMANDS.exportHandoffBundle/exportAnalysisHandoff/exportReplayCase/exportReviewDecisionTemplate/saveReviewDecision` compat export command,因为 App Server 目前没有等价 method;这些路径不能作为 evidence/export 完成证据,后续需要先补 App Server 协议 / RuntimeCore 事实源再迁。
|
||||
@@ -1326,7 +1326,7 @@ git diff --check
|
||||
310. P3.149 contract guard / 测试:`scripts/check-app-server-client-contract.mjs` 新增 renderer project workspace 守卫,锁定 `project.ts` 必须导入 `AppServerClient`、复用 protocol `METHOD_WORKSPACE_*` 常量、经 `requestProjectAppServer(...)` 解 App Server JSON-RPC `result` envelope,并禁止读 / ensure current gateway 回流旧 `workspace_get*`、`workspace_list`、`workspace_ensure_*` 与 `get_or_create_default_project`;同时锁定 `project.test.ts` 必须用 App Server fake client 验证 `workspace/list/read/byPath/read/default/read/default/ensure/projectsRoot/read/projectPath/resolve/ensureReady`、缓存 / inflight 去重、缺 result fail-closed 和不回退 legacy。
|
||||
311. P3.149 增量验证:`npx vitest run "src/lib/api/project.test.ts" --silent=passed-only --disableConsoleIntercept` 通过,1 个文件 65 个测试;`node --check "scripts/check-app-server-client-contract.mjs" && node "scripts/check-app-server-client-contract.mjs"` 通过,153 项 App Server client contract guard;`npx prettier --check "src/lib/api/project.ts" "src/lib/api/project.test.ts" "scripts/check-app-server-client-contract.mjs" "internal/roadmap/appserver/frontend-integration-matrix.md" "internal/exec-plans/app-server-implementation-plan.md"` 通过;本轮认领文件 `git diff --check` 通过;`npm run test:contracts` 完整通过,App Server client contract 153 项、command contracts 保持 `mock priority commands: 0`,并覆盖 harness、modality、cleanup 与 docs boundary。本轮未改 Electron runtime / GUI 壳,未重复跑 GUI smoke;P3.143 已有 Electron Desktop Host + App Server initialize smoke 证据,workspace/project 真实 GUI / E2E 证据仍待补。
|
||||
312. P3.150 model registry read gateway current 化:继续沿 `Frontend -> Electron Desktop Host bridge -> App Server JSON-RPC -> RuntimeCore / backend` 主链推进,本轮只认领 `src/lib/api/modelRegistry.ts`、`modelRegistry.test.ts`、`scripts/check-app-server-client-contract.mjs`、`internal/roadmap/appserver/frontend-integration-matrix.md` 与本执行计划记录。`getModelRegistry()`、`getModelPreferences()`、`getModelSyncState()`、`getModelsForProvider()`、`getModelsByTier()`、`getProviderAliasConfig()` 与 `getAllAliasConfigs()` 现在通过 `AppServerClient.request(...)` 消费 `model/list`、`modelPreferences/list`、`modelSyncState/read`、`modelProviderAlias/read`、`modelProviderAlias/list` App Server current methods,并直接复用 `packages/app-server-client/src/protocol.ts` 的 `METHOD_MODEL_*` 常量;缺少 `models / preferences / syncState / configs` 时 fail closed,不回退 `get_model_registry`、`get_model_preferences`、`get_model_sync_state`、`get_models_for_provider`、`get_models_by_tier`、`get_provider_alias_config`、`get_all_alias_configs`、renderer mock 或 legacy facade。
|
||||
313. P3.150 范围边界:本轮只迁 App Server 已有等价 method 的模型读链,不迁 `refresh_model_registry`、`search_models`、`toggle_model_favorite`、`hide_model`、`record_model_usage`、`fetch_provider_models_auto` 或 `get_model_registry_provider_ids`,因为 App Server 当前没有完整等价 refresh / write / live provider fetch method;这些命令保持 `compat`,不能作为 current App Server 完成证据。`frontend-integration-matrix.md` 已同步把 model registry 读链改为 `partial`,避免把缺真实 GUI / E2E 证据的 API 迁移误判成完整产品闭环。
|
||||
313. P3.150 范围边界:本轮当时只迁 App Server 已有等价 method 的模型读链,不迁 `refresh_model_registry`、`search_models`、`toggle_model_favorite`、`hide_model`、`record_model_usage` 或 `get_model_registry_provider_ids`。后续 P7 已把旧 `fetch_provider_models_auto` 清退为 `dead` 并迁到 App Server `modelProvider/fetchModels`,不得再按本历史记录保留 compat。`frontend-integration-matrix.md` 已同步把 model registry 读链改为 `partial`,避免把缺真实 GUI / E2E 证据的 API 迁移误判成完整产品闭环。
|
||||
314. P3.150 contract guard / 增量验证:`scripts/check-app-server-client-contract.mjs` 新增 renderer model registry 守卫,锁定 `modelRegistry.ts` 必须导入 `AppServerClient`、复用 protocol `METHOD_MODEL_*` 常量、经 `requestModelRegistryAppServer(...)` 解 App Server JSON-RPC `result` envelope,并禁止读链 current gateway 回流旧 `get_model_*` 与 alias legacy 命令;同时锁定 `modelRegistry.test.ts` 必须用 App Server fake client 验证 `model/list`、`modelPreferences/list`、`modelSyncState/read`、`modelProviderAlias/read`、`modelProviderAlias/list`、缓存 / inflight 复用、缺 result fail-closed 和不回退 legacy。`npx vitest run "src/lib/api/modelRegistry.test.ts" --silent=passed-only --disableConsoleIntercept` 通过,1 个文件 7 个 tests;`node --check "scripts/check-app-server-client-contract.mjs" && node "scripts/check-app-server-client-contract.mjs"` 通过,155 项 App Server client contract guard;`npx prettier --check` 覆盖本轮认领文件通过;本轮认领文件 `git diff --check` 通过。本轮未改 Electron runtime / GUI 壳,未重复跑 GUI smoke;model/provider 真实 GUI / E2E 证据仍待补。
|
||||
315. P3.151 knowledge pack list gateway current 化:继续沿 `Frontend -> Electron Desktop Host bridge -> App Server JSON-RPC -> RuntimeCore / backend` 主链推进,本轮避让 Electron / Rust / protocol 大写集,只认领 `src/lib/api/knowledge.ts`、`knowledge.test.ts`、`scripts/check-app-server-client-contract.mjs`、`internal/roadmap/appserver/frontend-integration-matrix.md` 与本执行计划记录。`listKnowledgePacks(...)` 现在通过 `AppServerClient.request("knowledgePack/list", ...)` 消费 App Server current method,并复用 `packages/app-server-client/src/protocol.ts` 的 `METHOD_KNOWLEDGE_PACK_LIST`;空 `workingDir`、缺少 `workingDir/rootPath` 或 `packs` 非数组时 fail closed,不回退 `knowledge_list_packs`、renderer mock 或 Electron legacy facade。
|
||||
316. P3.151 范围边界 / 守卫:本轮只迁 App Server 已有等价 method 的知识包列表读链,不迁 `knowledge_get_pack`、`knowledge_import_source`、`knowledge_compile_pack`、`knowledge_set_default_pack`、`knowledge_update_pack_status`、`knowledge_resolve_context` 或 `knowledge_validate_context_run`,因为 App Server 当前没有完整等价详情 / 写入 / 编译 / resolve / validate method;这些命令保持 `compat`,不能作为 current App Server 完成证据。`scripts/check-app-server-client-contract.mjs` 新增 renderer knowledge 守卫,锁定 `knowledge.ts` 必须导入 `AppServerClient`、复用 `METHOD_KNOWLEDGE_PACK_LIST`、经 `requestKnowledgeAppServer(...)` 解 App Server JSON-RPC `result` envelope,并禁止列表读 current gateway 回流 `knowledge_list_packs` 或 `invokeMockOnly`;同时锁定 `knowledge.test.ts` 必须用 App Server fake client 验证 `knowledgePack/list`、`includeArchived`、缺 `workingDir` fail-closed、缺 `packs` 不回退 legacy。
|
||||
@@ -1346,8 +1346,8 @@ git diff --check
|
||||
330. P3.156 App Server list profile 收口:`commandPolicy.ts` 现在能识别 `app_server_handle_json_lines` 内的 JSON-RPC `agentSession/list`,并归到 `agent-session-list` 8 秒超时 profile;同批请求若包含 `agentSession/turn/start`,仍优先保留长请求 `app-server-turn-start` profile,避免真实发送被短超时误杀。该修复不新增命令、不新增 mock、不把 Electron main 扩成后端,只给 current Electron Desktop Host IPC -> App Server JSON-RPC 通道补明确 settle 语义。
|
||||
331. P3.156 增量验证:`npx vitest run "src/lib/dev-bridge/safeInvoke.test.ts" "src/lib/dev-bridge/commandPolicy.test.ts" --silent=passed-only --disableConsoleIntercept` 通过,2 个文件 20 个测试;`npx prettier --check "src/lib/dev-bridge/safeInvoke.ts" "src/lib/dev-bridge/safeInvoke.test.ts" "src/lib/dev-bridge/commandPolicy.ts" "src/lib/dev-bridge/commandPolicy.test.ts"` 通过;本轮认领文件 `git diff --check` 通过;`npm run test:contracts` 完整通过,App Server client contract 163 项、command contracts 保持 `mock priority commands: 0`,并覆盖 harness、modality、cleanup 与 docs boundary。`npm run verify:gui-smoke` 通过,覆盖 smoke-only renderer build、Electron host typecheck / build、App Server sidecar assets 和 Electron smoke `initialize -> initialized`;首次运行撞上并行 `electron/main.ts` 中间态 typecheck 失败,单跑 `npx tsc --noEmit --project tsconfig.electron.json --pretty false` 通过后重跑 GUI smoke 成功。
|
||||
332. P3.157 API key provider read gateway current 化:继续沿 `Frontend -> Electron Desktop Host bridge -> App Server JSON-RPC -> RuntimeCore / backend` 主链推进,本轮只认领 `src/lib/api/apiKeyProvider.ts`、`apiKeyProvider.test.ts`、`scripts/check-app-server-client-contract.mjs`、`internal/roadmap/appserver/frontend-integration-matrix.md` 与本执行计划记录。`apiKeyProviderApi.getProviders()` 与 `getSystemProviderCatalog()` 现在通过 `AppServerClient.request(...)` 消费 `modelProvider/list`、`modelProvider/catalog/list` App Server current methods,并复用 `packages/app-server-client/src/protocol.ts` 的 `METHOD_MODEL_PROVIDER_LIST` / `METHOD_MODEL_PROVIDER_CATALOG_LIST`;缺少 `providers` 时 fail closed,不回退 `get_api_key_providers`、`get_system_provider_catalog`、renderer mock 或 Electron legacy facade。
|
||||
333. P3.157 范围边界 / 守卫:本轮只迁 App Server 已有等价 method 的 Provider 列表 / catalog 读链,不迁 `get_api_key_provider`、自定义 Provider 写入、API Key 写入 / 启停 / 导入导出、UI state、连接测试或 chat test,因为 App Server 当前没有完整等价写 / secret / live provider test method;这些命令保持 Desktop compat,不能作为 current App Server 完成证据。`scripts/check-app-server-client-contract.mjs` 新增 renderer API key provider 守卫,锁定 `apiKeyProvider.ts` 必须导入 `AppServerClient`、复用 `METHOD_MODEL_PROVIDER_LIST` / `METHOD_MODEL_PROVIDER_CATALOG_LIST`、经 `requestApiKeyProviderAppServer(...)` 解 App Server JSON-RPC `result` envelope,并禁止读链 current gateway 回流 `safeInvoke("get_api_key_providers")`、`safeInvoke("get_system_provider_catalog")` 或 `invokeMockOnly`;同时锁定 `apiKeyProvider.test.ts` 必须用 App Server fake client 验证 `modelProvider/list`、`modelProvider/catalog/list`、缺 result fail-closed、不回退 legacy,并保留 `add_api_key` / 连接测试的 compat side-effect 边界。
|
||||
334. P3.157 增量验证:`npx vitest run "src/lib/api/apiKeyProvider.test.ts" --silent=passed-only --disableConsoleIntercept` 通过,1 个文件 9 个测试;`node --check "scripts/check-app-server-client-contract.mjs" && node "scripts/check-app-server-client-contract.mjs"` 通过,165 项 App Server client contract guard;`npx prettier --check "src/lib/api/apiKeyProvider.ts" "src/lib/api/apiKeyProvider.test.ts" "scripts/check-app-server-client-contract.mjs" "internal/roadmap/appserver/frontend-integration-matrix.md" "internal/exec-plans/app-server-implementation-plan.md"` 通过;本轮认领文件 `git diff --check` 通过。`src/components/api-key-provider/README.md` 已同步改成 App Server `modelProvider/list` / `modelProvider/catalog/list` 事实源口径,legacy `get_api_key_providers` / `get_system_provider_catalog` 只作为 Desktop compat facade。本轮未改 Electron runtime / GUI 壳,未重复跑 GUI smoke;Provider 设置页真实 GUI / E2E 证据仍待补。
|
||||
333. P3.157 范围边界 / 守卫:本轮当时只迁 App Server 已有等价 method 的 Provider 列表 / catalog 读链;后续 P7 已把 Provider read/write、API Key 写入 / 启停 / 导入导出、UI state、连接测试、chat test 与 live fetch 统一迁到 App Server `modelProvider*` / `modelProviderKey*` / `modelProviderUiState*` / `modelProviderConfig*` current method,旧 Provider command family 已判 `dead`。`scripts/check-app-server-client-contract.mjs` 新增 renderer API key provider 守卫,锁定 `apiKeyProvider.ts` 必须导入 `AppServerClient`、复用 `METHOD_MODEL_PROVIDER_*` 常量、经 `requestApiKeyProviderAppServer(...)` 解 App Server JSON-RPC `result` envelope,并禁止 current gateway 回流旧 Provider `safeInvoke(...)` 或 `invokeMockOnly`;同时锁定 `apiKeyProvider.test.ts` 必须用 App Server fake client 验证 `modelProvider*`、缺 result fail-closed、不回退 legacy。
|
||||
334. P3.157 增量验证:`npx vitest run "src/lib/api/apiKeyProvider.test.ts" --silent=passed-only --disableConsoleIntercept` 通过,1 个文件 9 个测试;`node --check "scripts/check-app-server-client-contract.mjs" && node "scripts/check-app-server-client-contract.mjs"` 通过,165 项 App Server client contract guard;`npx prettier --check "src/lib/api/apiKeyProvider.ts" "src/lib/api/apiKeyProvider.test.ts" "scripts/check-app-server-client-contract.mjs" "internal/roadmap/appserver/frontend-integration-matrix.md" "internal/exec-plans/app-server-implementation-plan.md"` 通过;本轮认领文件 `git diff --check` 通过。`src/components/api-key-provider/README.md` 已同步改成 App Server `modelProvider/list` / `modelProvider/catalog/list` 事实源口径;后续 P7 已把旧 Provider facade 全部标为 `dead`,不再作为 Desktop compat facade 正向证据。本轮未改 Electron runtime / GUI 壳,未重复跑 GUI smoke;Provider 设置页真实 GUI / E2E 证据仍待补。
|
||||
335. P3.158 current App Server gateway method 常量收口:继续沿 `packages/app-server-client/src/protocol.ts` 作为 renderer-safe 协议事实源推进,本轮只认领 `src/lib/api/agentRuntime/appServerSessionClient.ts`、`inventoryClient.ts`、`memory.ts`、`scripts/check-app-server-client-contract.mjs`、`internal/roadmap/appserver/frontend-integration-matrix.md` 与本执行计划记录。`agentSession/list`、`workspaceSkillBindings/list`、`projectMemory/read` 的前端 gateway 已从本地 `APP_SERVER_METHOD_*` 字符串常量改为直接复用 `METHOD_AGENT_SESSION_LIST`、`METHOD_WORKSPACE_SKILL_BINDINGS_LIST`、`METHOD_PROJECT_MEMORY_READ`;这不新增命令、不改变返回投影,只封住 current gateway 复制第二套 method 字符串的漂移面。
|
||||
336. P3.158 守卫 / 增量验证:`scripts/check-app-server-client-contract.mjs` 已补守卫,锁定 App Server session facade、workspace skill bindings、project memory 聚合读必须从 `packages/app-server-client/src/protocol.ts` 导入 method 常量,并禁止旧 `APP_SERVER_METHOD_*` 本地硬编码回流。`npx vitest run "src/lib/api/memory.test.ts" "src/lib/api/agentRuntime/inventoryClient.test.ts" "src/lib/api/agentRuntime/appServerSessionClient.test.ts" --silent=passed-only --disableConsoleIntercept` 通过,3 个文件 17 个测试;`node --check "scripts/check-app-server-client-contract.mjs" && node "scripts/check-app-server-client-contract.mjs"` 通过,165 项 App Server client contract guard;`npx prettier --check "src/lib/api/memory.ts" "src/lib/api/agentRuntime/inventoryClient.ts" "src/lib/api/agentRuntime/appServerSessionClient.ts" "scripts/check-app-server-client-contract.mjs"` 通过;`npm run test:contracts` 完整通过,App Server client contract 165 项、command contracts 保持 `mock priority commands: 0`,并覆盖 harness、modality、cleanup 与 docs boundary。本轮未改 Electron runtime / GUI 壳,未重复跑 GUI smoke;并行进程仍在运行 `npm run typecheck`,该全量类型检查结果不归入本轮证据。
|
||||
337. P3.158 Electron shell startup command cleanup:针对启动控制台报错 `sync_tray_model_shortcuts`、`take_pending_skill_package_open_requests`、`companion_get_pet_status`、`list_relay_providers`,本轮只收直接阻塞默认启动的 current / shell surface。`sync_tray_model_shortcuts` 与 `take_pending_skill_package_open_requests` 已补进 Electron Desktop Host 白名单和 main 进程实现:托盘模型菜单只保存 renderer 传入的菜单投影并通过 `tray-model-selected` 事件回写选择;`.skill` / `.skills` 打开请求由启动参数、macOS `open-file` 与 second-instance 参数进入 pending 队列,前端通过既有 `take_pending_skill_package_open_requests` 取出并清空。它们属于 Electron 桌面壳能力,不进入 App Server truth bridge,也不回退 mock。
|
||||
@@ -1356,7 +1356,7 @@ git diff --check
|
||||
340. P3.158 增量验证:`npx vitest run "src/hooks/useCompanionProviderBridge.test.tsx" "electron/ipcChannels.test.ts" --silent=passed-only --disableConsoleIntercept` 通过,2 个文件 11 个测试;`npm run typecheck:electron` 通过;`cargo check --manifest-path "lime-rs/Cargo.toml" -p app-server` 通过且不再输出 `ensure_capability_allowed` dead code warning;`npx prettier --check` 覆盖本轮前端 / Electron 文件通过;本轮认领文件 `git diff --check` 通过;`npm run test:contracts` 完整通过,App Server client contract 165 项、command contracts 保持 `mock priority commands: 0`,并覆盖 harness、modality、cleanup 与 docs boundary。`npm run typecheck` 全量前端检查超过 3 分钟无输出,已结束本轮启动的残留 `npm run typecheck` / `tsc --noEmit` 进程;本轮以定向 hook 测试、Electron typecheck、Rust app-server check 与完整 contracts 作为交付证据。
|
||||
341. P3.159 桌宠默认关闭补齐:继续收 `companion_get_pet_status` 默认触发面,本轮认领 `src/components/settings-v2/agent/providers/index.tsx`、`src/components/settings-v2/_layout/index.tsx` 与对应测试。Provider 设置页现在只有在 `navigation.enabled_items` 显式包含 `companion` 时才渲染 `CompanionProviderBridgeCard` / `CompanionCapabilityPreferencesCard` 与 companion `TabsContent`;设置首页也复用 `useCompanionEntryEnabled()`,默认不再展示“桌宠”快捷卡。默认关闭时只显示本地 Provider 主区,不挂载桌宠 bridge、不读取桌宠状态、不监听 companion 事件、不订阅 provider data 变化。这一步不新增命令、不新增 mock,也不把 companion 旧命令接回默认启动路径。
|
||||
342. P3.159 增量验证:`npx vitest run "src/components/settings-v2/_layout/index.test.tsx" "src/components/settings-v2/home/index.test.tsx" "src/components/settings-v2/agent/providers/index.test.tsx" "src/hooks/useCompanionProviderBridge.test.tsx" "src/lib/navigation/sidebarNav.test.ts" --silent=passed-only --disableConsoleIntercept` 通过,5 个文件 47 个测试;`npx prettier --check "src/components/settings-v2/_layout/index.tsx" "src/components/settings-v2/_layout/index.test.tsx" "src/components/settings-v2/agent/providers/index.tsx" "src/components/settings-v2/agent/providers/index.test.tsx" "src/hooks/useCompanionProviderBridge.ts" "src/hooks/useCompanionProviderBridge.test.tsx" "src/hooks/useCompanionEntryEnabled.ts" "src/lib/navigation/sidebarNav.ts" "src/lib/navigation/sidebarNav.test.ts" "internal/exec-plans/app-server-implementation-plan.md"` 通过;本轮认领文件 `git diff --check` 通过。GUI 只读点击路径也已补证据:通过系统 Chrome 从根页面打开账号菜单进入设置页,再切到设置首页,确认常用入口为 3 项且不含“桌宠 / Companion”,Provider 工作区也没有 `provider-workspace-tab-companion`,证据落到 `.lime/qc/gui-evidence/companion-default-off/home-card-summary.json` 与 `settings-home-card-check.png`。本轮未改 Electron runtime / App Server protocol / host 白名单,因此未重复跑 `npm run test:contracts`。
|
||||
343. P3.160 Provider 设置页 current GUI / bridge 证据:新增 `scripts/provider-settings-current-smoke.mjs`,通过真实 GUI 点击路径从根页面打开账号菜单进入 AI 服务商设置,再点击“添加模型”进入 catalog 面板;脚本监听 DevBridge `/invoke`,解析 `app_server_handle_json_lines` 的 JSON-RPC lines,要求同时观察到 `modelProvider/list` 与 `modelProvider/catalog/list`,并断言没有 `get_api_key_providers` / `get_system_provider_catalog`。该脚本只读取 Provider 列表与系统 catalog,不写入 API Key、不触发连接测试或真实模型调用。
|
||||
343. P3.160 Provider 设置页 current GUI / bridge 证据:新增 `scripts/provider-settings-current-smoke.mjs`,通过真实 GUI 点击路径从根页面打开账号菜单进入 AI 服务商设置,再点击“添加模型”进入 catalog 面板;脚本监听 DevBridge `/invoke`,解析 `app_server_handle_json_lines` 的 JSON-RPC lines,要求同时观察到 `modelProvider/list` 与 `modelProvider/catalog/list`,并断言没有旧 Provider command family。该脚本只读取 Provider 列表与系统 catalog,不写入 API Key、不触发连接测试或真实模型调用。
|
||||
344. P3.160 增量验证:`node --check "scripts/provider-settings-current-smoke.mjs"` 通过;`node "scripts/provider-settings-current-smoke.mjs" --timeout-ms 120000` 通过,并输出 `.lime/qc/gui-evidence/provider-settings-current/provider-settings-current-summary.json`、`provider-settings-current-network-invoke.json`、`provider-settings-current.png`,summary 显示 `transport=electron-host`、`providerPageMounted=true`、`modelAddCatalogMounted=true`、`appServerHandleJsonLinesSeen=true`、`appServerMethodsSeen` 包含 `modelProvider/list` 与 `modelProvider/catalog/list`、`legacyProviderCommandsSeen=[]`、`missingRequiredAppServerMethods=[]`。`npx prettier --check "scripts/provider-settings-current-smoke.mjs"` 通过;本轮认领脚本 `git diff --check` 通过;`internal/roadmap/appserver/frontend-integration-matrix.md` 已同步把 API key provider list/catalog 从“缺真实 GUI / E2E 证据”改为已有 provider settings current smoke 证据,仍保留单 Provider、secret 写入、UI state、导入导出和连接测试 compat 缺口。
|
||||
345. P3.161 提交前 `verify:local` 门禁修复:`verify:local` 在 Vitest smart 批次 8 暴露 `scripts/app-server/release-manifest.test.mjs` 收集阶段 `Expected ident`。根因是 `app-server-release-manifest.mjs` 带 shebang,Vite transform 对相对导入模块注入 import 后 shebang 落到文件中间成为非法语法;该脚本只通过 `node scripts/...` 调用且文件不是可执行权限,因此移除 shebang 不改变 CLI 入口。测试文件同步迁到 Vitest API,`app-server:manifest:test` 也从 `node scripts/*.test.mjs` 直跑改成 `npx vitest run`,避免保留坏入口。
|
||||
346. P3.162 侧栏会话 current GUI / bridge 证据:新增 `scripts/sidebar-sessions-current-smoke.mjs`,通过真实 GUI 从根页面打开主侧栏会话 Shelf,预置只读 `agent_last_project_id` 以触发最近对话列表读取,再展开“归档”触发归档列表懒加载;脚本监听 DevBridge `/invoke`,解析 `app_server_handle_json_lines` 的 JSON-RPC lines,要求观察到 `agentSession/list`,最近列表和归档列表都退出 loading,并断言没有 `agent_runtime_list_sessions`。该脚本只读会话列表,不创建会话、不点击会话菜单、不触发归档 / 重命名 / 删除,也不调用真实模型。
|
||||
@@ -1418,13 +1418,13 @@ git diff --check
|
||||
402. P3.189 增量验证:`node --check "scripts/check-app-server-client-contract.mjs"` 通过;`node "scripts/check-app-server-client-contract.mjs"` 通过,178 项 App Server client contract guard;`npx prettier --check "scripts/check-app-server-client-contract.mjs" "scripts/app-server/external-backend-smoke.mjs"` 通过;`git diff --check -- "scripts/check-app-server-client-contract.mjs" "scripts/app-server/external-backend-smoke.mjs" "internal/exec-plans/app-server-implementation-plan.md"` 通过;`npm run test:contracts` 完整通过,command contracts 保持 `mock priority commands: 0`、Electron host commands 103、DevBridge truth commands 148。真实 GUI 发送后 `event -> read model refresh -> timeline` 与 Codex-style `thread/list/read/archive/unarchive` 仍是后续主缺口。
|
||||
403. P3.190 packaged failure readback smoke 与防回流守卫:继续沿 `Frontend -> Electron Desktop Host IPC / DevBridge -> app_server_handle_json_lines -> App Server JSON-RPC -> RuntimeCore / backend` 的 App Server sidecar / read model 主链补失败路径证据,本轮只认领 `scripts/app-server/packaged-external-backend-failure-smoke.mjs`、`scripts/check-app-server-client-contract.mjs` 与本计划记录,不修改 `lime-rs/crates/app-server/src/local_data_source.rs`,也不触碰 App Server protocol / runtime / TS client 大写集。packaged external backend failure smoke 在收到失败的 `agentSession/turn/start` 后,不再只检查 streamed `message.delta` / `turn.failed` 和 `evidence/export`,还会调用 `agentSession/read`,断言同一 `sessionId/threadId` 可读回 1 个同 `turnId` 的 `failed` turn 且带 `completedAt`;summary 同步输出 `readTurns=1` 与 `readTurnStatus=failed`。App Server client contract guard 现在要求该 smoke 保留 `connection.readSession`、`readTurns.length === 1`、`readTurn.status === "failed"`、`completedAt` 与 `readTurnStatus=${readTurn.status}`,并禁止 `backendMode: "mock"` 回流,防止 packaged failure 证据退回只看通知 / evidence、不证明失败 read model 落地或靠 mock backend 假成功。
|
||||
404. P3.190 增量验证:`node --check "scripts/check-app-server-client-contract.mjs"` 通过;`node --check "scripts/app-server/packaged-external-backend-failure-smoke.mjs"` 通过;`npx prettier --check "scripts/check-app-server-client-contract.mjs" "scripts/app-server/packaged-external-backend-failure-smoke.mjs" "internal/exec-plans/app-server-implementation-plan.md"` 通过;`git diff --check -- "scripts/check-app-server-client-contract.mjs" "scripts/app-server/packaged-external-backend-failure-smoke.mjs" "internal/exec-plans/app-server-implementation-plan.md"` 通过;`node "scripts/app-server/packaged-external-backend-failure-smoke.mjs"` 通过,summary 显示 `clientEvents=message.delta,turn.failed`、`evidenceEvents=message.delta,turn.failed`、`readTurns=1`、`readTurnStatus=failed`;`node "scripts/check-app-server-client-contract.mjs"` 通过,178 项 App Server client contract guard;`npm run test:contracts` 完整通过,command contracts 保持 `mock priority commands: 0`、Electron host commands 103、DevBridge truth commands 148。本轮未改 GUI 壳 / 前端页面 / App Server protocol,因此未跑 `npm run verify:gui-smoke`;真实 GUI 发送后 `agentSession/event -> agentSession/read -> read model refresh -> timeline` 与 Codex-style `thread/list/read/archive/unarchive` 仍是后续主缺口。
|
||||
405. P3.191 Claw 工作台进入默认 Electron smoke:按用户确认的方向,默认 `npm run smoke:electron` / `npm run verify:gui-smoke` 不再只证明 renderer 与 App Server `initialize`,而是在 Electron main 中继续等待 Claw / Agent 工作台真实 DOM ready:`[data-testid="workspace-shell-scene"]`、`[data-testid="inputbar-core-container"]` 与 `textarea[name="agent-chat-message"]` 必须可见且 composer 可用;同时只过滤本次启动后的 `lime_invoke_error_buffer_v1` / `lime_invoke_trace_buffer_v1` 错误,避免旧 localStorage 残留误杀。`scripts/electron/current-entrypoints.test.mjs` 新增守卫,要求 smoke 保留 workbench shell / composer gate,并明确禁止默认 smoke 触发 `"agentSession/turn/start"` 或 `"test_api_key_provider_chat"`;完整 Claw streaming GUI E2E 仍必须走 `scripts/claw-chat-ready-streaming-smoke.mjs --allow-live-provider` 或 `LIME_ALLOW_LIVE_PROVIDER_SMOKE=1` 显式授权,不能把默认 smoke 扩成真实 Provider 消耗路径。
|
||||
405. P3.191 Claw 工作台进入默认 Electron smoke:按用户确认的方向,默认 `npm run smoke:electron` / `npm run verify:gui-smoke` 不再只证明 renderer 与 App Server `initialize`,而是在 Electron main 中继续等待 Claw / Agent 工作台真实 DOM ready:`[data-testid="workspace-shell-scene"]`、`[data-testid="inputbar-core-container"]` 与 `textarea[name="agent-chat-message"]` 必须可见且 composer 可用;同时只过滤本次启动后的 `lime_invoke_error_buffer_v1` / `lime_invoke_trace_buffer_v1` 错误,避免旧 localStorage 残留误杀。`scripts/electron/current-entrypoints.test.mjs` 新增守卫,要求 smoke 保留 workbench shell / composer gate,并明确禁止默认 smoke 触发 `"agentSession/turn/start"` 或 App Server `"modelProvider/testChat"`;完整 Claw streaming GUI E2E 仍必须走 `scripts/claw-chat-ready-streaming-smoke.mjs --allow-live-provider` 或 `LIME_ALLOW_LIVE_PROVIDER_SMOKE=1` 显式授权,不能把默认 smoke 扩成真实 Provider 消耗路径。
|
||||
406. P3.191 Electron packaged renderer 相对资源修复:首次把 Claw 工作台纳入 smoke 后,`npm run verify:gui-smoke` 真实失败在 `file:///.../dist/index.html?nativeStartup=1`,页面只剩 index.html 原生启动层文案,`workspace-shell-scene/inputbar/composer` 全部未挂载。根因是 Electron packaged renderer 在 `file://` 下仍使用绝对 `/assets/...` 资源路径,React bundle 加载不到。`vite.config.ts` 现在只在 `command === "build" && LIME_ELECTRON_RENDERER=1` 时设置 `base: "./"`,dev server 不变;入口守卫同步要求 Electron renderer build 脚本设置 `LIME_ELECTRON_RENDERER` 且 Vite config 保留相对 base。重跑 `npm run verify:gui-smoke` 通过,输出包含 `[electron-host] app-server ready protocol=appserver.v0 version=1.59.0`、`[electron-smoke] app-server initialized protocol=appserver.v0 version=1.59.0` 与 `[electron-smoke] claw workbench shell ready`;构建产物 `dist/index.html` 的主脚本 / CSS 已变为 `./assets/...`。
|
||||
407. P3.192 设置页 updater warning 收口:设置页 broad smoke 曾被开发态 About 页更新诊断 warning 误判失败,唯一噪音是缺少 dev `app-update.yml` 时 About 页把 updater 诊断错误打印到 `console.warn("Update check returned a diagnostic error:", ...)`。`src/components/settings-v2/system/about/index.tsx` 现在仍向用户展示本地化的“暂时无法检查更新”状态,但不把诊断错误写入 console warning;`about/index.test.tsx` 新增断言,更新检查返回 `error` 时 UI 不泄露技术细节,也不调用 `console.warn`。
|
||||
408. P3.192 增量验证:`node --check "scripts/electron-smoke.mjs" "scripts/electron/current-entrypoints.test.mjs" "scripts/check-app-server-client-contract.mjs"` 通过;`node "scripts/check-app-server-client-contract.mjs"` 通过,178 项守卫;`npx prettier --check "vite.config.ts" "electron/main.ts" "scripts/electron-smoke.mjs" "scripts/electron/current-entrypoints.test.mjs" "src/components/settings-v2/system/about/index.tsx" "src/components/settings-v2/system/about/index.test.tsx"` 通过;`npx vitest run "src/components/settings-v2/system/about/index.test.tsx" "scripts/electron/current-entrypoints.test.mjs" --silent=passed-only --disableConsoleIntercept` 通过,2 个文件 17 个测试;`npx tsc --noEmit --project "tsconfig.electron.json" --pretty false` 通过;`npm run verify:gui-smoke` 通过并输出 Claw workbench ready;`node "scripts/settings-all-pages-current-smoke.mjs" --timeout-ms 120000` 通过,summary 显示 `ok=true`、`tabs=15`、`pages=24`、`invokeCount=97`、`consoleErrors=0`、`unexpectedConsoleWarnings=0`、`invokeResponseErrors=0`;`npm run test:contracts` 完整通过,command contracts 保持 `mock priority commands: 0`。本轮只把默认 Electron smoke 提升到工作台入口 / composer 可用门槛,不宣称真实 Agent 发送、Provider streaming、event -> read model refresh 的完整 GUI 闭环已完成。
|
||||
409. P3.193 code runtime fixture smoke current runtime 迁移:继续沿 `Frontend -> Electron Desktop Host IPC / DevBridge -> app_server_handle_json_lines -> App Server JSON-RPC -> RuntimeCore / backend` 主链收正向 runtime 证据,本轮只认领 `scripts/code-runtime-fixture-smoke.mjs`、`scripts/check-app-server-client-contract.mjs`、本计划与前端矩阵记录,不修改 `lime-rs/crates/app-server/src/local_data_source.rs`,也不触碰 App Server protocol / runtime / TS client 大写集。`scripts/code-runtime-fixture-smoke.mjs` 不再调用 `agent_runtime_create_session`、`agent_runtime_update_session`、`agent_runtime_submit_turn`、`agent_runtime_get_session`、`agent_runtime_get_thread_read` 或 `agent_runtime_export_evidence_pack` 作为正向完成证据;会话创建、provider/model 更新、turn start、read model 轮询与 evidence export 均改为 `app_server_handle_json_lines -> agentSession/start`、`agentSession/update`、`agentSession/turn/start`、`agentSession/read`、`evidence/export`。submit 继续通过 `RuntimeOptions.hostOptions.asterChatRequest` 携带扁平 Claw/Aster 请求快照,包括 `provider_config`、approval/sandbox、workspace、execution strategy、metadata 与 turn id;read 侧对 `agentSession/read` 的 protocol turns 与 legacy `detail.thread_read` 两种形态做脚本内投影,避免再依赖旧 `agent_runtime_get_thread_read`。文件 checkpoint 仍通过 `agent_runtime_list_file_checkpoints` / `agent_runtime_diff_file_checkpoint` 验证,因为 App Server 目前没有等价 checkpoint method;该路径在 evidence 中明确标记为 `usesFileCheckpointCompat`,不能当成 runtime lifecycle current 证据。
|
||||
410. P3.193 防回流守卫 / 验证边界:`scripts/check-app-server-client-contract.mjs` 新增 code runtime fixture smoke 守卫,要求脚本保留 `app_server_handle_json_lines`、`agentSession/start`、`agentSession/update`、`agentSession/turn/start`、`agentSession/read`、`evidence/export`、`hostOptions.asterChatRequest`、`provider_config` 与 `usesAppServerJsonRpcSubmitTurn / usesAppServerSessionRead / usesAppServerEvidenceExport / usesFileCheckpointCompat` 覆盖字段,同时禁止 `agent_runtime_create_session`、`agent_runtime_update_session`、`agent_runtime_submit_turn`、`agent_runtime_get_session`、`agent_runtime_get_thread_read`、`agent_runtime_export_evidence_pack` 与旧 `usesCurrentRuntimeSubmitTurn` 回流;checkpoint 两个 compat 命令不被禁止,退出条件是补齐 App Server checkpoint/diff current method 后再迁。只读复核 Codex 四个 `app-server-*` crate 后确认,Codex 当前是一等 `thread/*` / `turn/*` v2 API,Lime 当前仍以 `agentSession/*` facade 为事实源;本轮不直接照搬 Codex `thread/*` 命名,后续要等 App Server protocol / runtime / TS client 写集稳定后单独实现。完整 Claw live GUI E2E 仍需显式 `--allow-live-provider` 授权,不能用本离线 fixture smoke 代替真实发送 / streaming / event refresh / `agentSession/read` 回流证据。
|
||||
411. P3.194 Claw streaming GUI E2E 证据收紧:继续沿第一竖切“真实发送、`agentSession/event`、`agentSession/read` 回流”缺口推进,本轮只认领 `scripts/claw-chat-ready-streaming-smoke.mjs`、`scripts/lib/live-provider-smoke-gate.test.mjs`、`scripts/check-app-server-client-contract.mjs`、本计划与前端矩阵记录,不触碰 Rust protocol / runtime / Electron main 大写集。`scripts/claw-chat-ready-streaming-smoke.mjs` 的 `wait-health` 现在必须确认 DevBridge health 返回 `transport=electron-host`,否则直接失败;summary 也新增 `electronHostBridge` pass 断言,防止完整 Claw streaming GUI E2E 误连旧 DevBridge 或非 Electron Host 后产出假 current 证据。该脚本仍必须显式 `--allow-live-provider` 或 `LIME_ALLOW_LIVE_PROVIDER_SMOKE=1 / LIME_REAL_API_TEST=1` 才会进入 `wait-health`、`prepare-runtime`、`aster_agent_init`、`get_api_key_providers` 与 `test_api_key_provider_chat`;默认运行继续在调用 DevBridge / Provider 前 fail closed。
|
||||
411. P3.194 Claw streaming GUI E2E 证据收紧:继续沿第一竖切“真实发送、`agentSession/event`、`agentSession/read` 回流”缺口推进,本轮只认领 `scripts/claw-chat-ready-streaming-smoke.mjs`、`scripts/lib/live-provider-smoke-gate.test.mjs`、`scripts/check-app-server-client-contract.mjs`、本计划与前端矩阵记录,不触碰 Rust protocol / runtime / Electron main 大写集。`scripts/claw-chat-ready-streaming-smoke.mjs` 的 `wait-health` 现在必须确认 DevBridge health 返回 `transport=electron-host`,否则直接失败;summary 也新增 `electronHostBridge` pass 断言,防止完整 Claw streaming GUI E2E 误连旧 DevBridge 或非 Electron Host 后产出假 current 证据。该脚本仍必须显式 `--allow-live-provider` 或 `LIME_ALLOW_LIVE_PROVIDER_SMOKE=1 / LIME_REAL_API_TEST=1` 才会进入 `wait-health`、`prepare-runtime`、`aster_agent_init`、App Server `modelProvider/list` 与 `modelProvider/testChat`;默认运行继续在调用 DevBridge / Provider 前 fail closed。
|
||||
412. P3.194 增量验证:`node --check "scripts/claw-chat-ready-streaming-smoke.mjs" "scripts/check-app-server-client-contract.mjs" "scripts/lib/live-provider-smoke-gate.test.mjs"` 通过;`npx vitest run "scripts/lib/live-provider-smoke-gate.test.mjs" --silent=passed-only --disableConsoleIntercept` 通过,13 个测试;`npm run smoke:claw-chat-ready-streaming -- --timeout-ms 30000` 按预期失败于 live provider gate,输出“默认禁止执行”,且未进入 `stage=wait-health`;`node "scripts/check-app-server-client-contract.mjs"` 通过,179 项 App Server client contract guard;`npx prettier --check "scripts/claw-chat-ready-streaming-smoke.mjs" "scripts/check-app-server-client-contract.mjs" "scripts/lib/live-provider-smoke-gate.test.mjs"` 通过;`git diff --check -- "scripts/claw-chat-ready-streaming-smoke.mjs" "scripts/check-app-server-client-contract.mjs" "scripts/lib/live-provider-smoke-gate.test.mjs"` 通过。本轮没有真实 Provider 授权,因此未运行完整 Claw live streaming GUI E2E,也不宣称真实发送 / streaming / event refresh / `agentSession/read` 回流已完成;下一刀仍是在明确授权后运行并修复 `scripts/claw-chat-ready-streaming-smoke.mjs --allow-live-provider` 的真实失败。
|
||||
413. P3.195 Connect deep link Electron current 化:按“Electron 全面接管前端宿主、Tauri 下线”目标,Connect deep link 生产主链已从旧 Tauri command/event 切到 `Electron protocol/open-url -> plugin-deep-link onOpenUrl/getCurrent -> useDeepLink -> src/lib/api/connect.ts -> App Server JSON-RPC -> RuntimeCore / LocalAppDataSource`。App Server protocol / TS client / processor / runtime / local data source 已提供 `connectDeepLink/resolve`、`connectOpenDeepLink/resolve`、`connectRelayApiKey/save`、`connectCallback/send`;前端 `useDeepLink` 不再监听 `deep-link://new-url`、`deep-link-connect`、`deep-link-error`,也不再调用 `handle_deep_link`、`handle_open_deep_link`、`save_relay_api_key`。`useConnectCallback` 改走 `sendConnectCallback` 网关,不再调用旧 `send_connect_callback`。renderer registry list 入口不再打 `list_relay_providers` / `refresh_relay_registry`,`src/lib/desktop-host/providerMocks.ts` 也移除 Relay registry mock,避免把旧 Tauri registry list 当成测试或生产 fallback。Connect registry 不在启动期加载,只有实际 `lime://connect` resolve/save/callback 时由 App Server 按需读取缓存 / 远程 registry。
|
||||
414. P3.195 守卫 / 增量验证:新增 `src/lib/api/connect.test.ts` 锁住四个 Connect current method 与 camelCase/snake_case 投影,并断言不经 `safeInvoke`;新增 `src/hooks/useConnectCallback.test.tsx` 锁住成功 / 失败 callback 只经 `sendConnectCallback` 且失败不阻塞主流程;`src/hooks/useDeepLink.test.tsx` 已覆盖运行中 Electron `onOpenUrl` 与启动期 `getCurrent` 的 `lime://connect` / `lime://open` 路径。`internal/aiprompts/commands.md` 已同步 Connect deep link current 主链和旧事件 / 旧命令禁止回流清单。定向验证已通过:`npx vitest run "src/lib/api/connect.test.ts" "src/hooks/useConnectCallback.test.tsx" "src/hooks/useDeepLink.test.tsx" --silent=passed-only --disableConsoleIntercept`、`npm --prefix "packages/app-server-client" run test`、`cargo test --manifest-path "lime-rs/Cargo.toml" -p app-server-protocol -p app-server --locked`、`npm run test:contracts`。本刀不新增 Electron 后端 adapter、不引入 mock fallback;剩余 GUI 级证据是后续用真实 Electron 窗口触发 `lime://connect` URL 并确认 Dialog / 保存 API Key / callback 全链路。
|
||||
@@ -1480,7 +1480,7 @@ git diff --check
|
||||
464. P3.218 真实 Electron SDK fixture 证据 / 验证边界:`scripts/agent-app/runtime-electron-sdk-fixture-smoke.mjs` 的 external backend fixture 在 `actionRespond` 后输出 `tool.started` 与 `tool.result`,iframe SDK 第二次 `getTask` 后 fail-closed 断言 `task:toolCall` replay event 与 `result.thread_read.tool_calls` 同时存在;summary 现在把 `toolEvidence` 提到顶层,便于后续自动审阅。真实 smoke 通过:`APP_SERVER_BIN="/Users/coso/Documents/dev/ai/aiclientproxy/lime/lime-rs/target/debug/app-server" node "scripts/agent-app/runtime-electron-sdk-fixture-smoke.mjs" --app-url "http://127.0.0.1:14321/" --timeout-ms 120000 --keep-temp`,summary 位于 `.lime/qc/gui-evidence/agent-app-runtime-electron-sdk-fixture/agent-app-runtime-electron-sdk-fixture-summary.json`,显示 `ok=true`、`turnId=agent-app-turn-1780811535060`、`statuses.first=running`、`statuses.second=running`、`statuses.cancel=cancelled`、`toolEvidence.toolEventType=task:toolCall`、`toolEvidence.toolName=WebFetch`、`toolEvidence.outputPreview=fetched https://example.com/agent-app-sdk-fixture`、`toolEvidence.readModelStatus=completed`、`toolEvidence.success=true`、`artifactEvidence.artifactEventType=artifact:created`、`consoleErrors=[]`;backend log 记录 `turnStart / actionRespond / turnCancel` 且三者共享同一动态 turn id。定向验证通过:`npx vitest run "src/features/agent-app/runtime/agentRuntimeCapabilityHost.test.ts" --silent=passed-only --disableConsoleIntercept`、`npx vitest run "scripts/agent-app/runtime-electron-sdk-fixture-smoke.test.mjs" --silent=passed-only --disableConsoleIntercept`、`node "scripts/check-app-server-client-contract.mjs"`、`npx prettier --check ...` 与本轮认领文件 `git diff --check`。该证据仍不等同 live provider 完整 Claw streaming,也不宣称真实模型已执行完整 WebFetch;它只证明 App Server current read model 的 tool facts 可经 Electron Host Bridge 回流到 Agent App SDK task record。下一刀应转向 live provider 授权后的完整 Claw streaming GUI E2E,或把真实 tool runtime 执行从 fixture evidence 扩到 live turn 证据。
|
||||
465. P3.219 Electron GUI smoke current 主路径回归:在 P3.218 后补跑产品级 GUI 门槛,确认当前大写集下 Electron Desktop Host、App Server sidecar 和默认 Claw 工作台 shell 仍能真实启动。本轮只记录验证证据,不接管并行 Electron / Rust 写集,也不修改 `lime-rs/crates/app-server/src/local_data_source.rs`。`npm run verify:gui-smoke` 通过,执行链包含 `generate:agent-runtime-clients`、`generate:extension-site-adapters`、`verify:app-version`、renderer smoke build、`packages/app-server-client` build、`typecheck:electron`、Electron main/preload build、desktop assets copy、App Server sidecar build 与 `scripts/electron-smoke.mjs`。运行输出确认 `app-server ready protocol=appserver.v0 version=1.59.0`、`renderer loaded`、`app-server initialized protocol=appserver.v0 version=1.59.0`、`claw workbench shell ready`。开发态 updater 缺少 `.lime/electron-dev-host/Lime-dev.app/Contents/Resources/app-update.yml` 仍只作为 smoke 期间已知诊断输出,不阻塞 GUI smoke。
|
||||
466. P3.219 边界:该刀证明默认 Electron GUI 主路径在 P3.218 后仍可交付启动,覆盖 Electron Host bridge、App Server sidecar initialize、renderer 加载与 Claw 工作台 shell/composer ready;它不是 live provider 完整 Claw streaming E2E,也不证明真实发送、模型 streaming、tool runtime live 执行、`agentSession/event -> agentSession/read` 刷新或最终 assistant 输出。完整业务闭环仍必须在显式 live provider 授权后运行 `scripts/claw-chat-ready-streaming-smoke.mjs --allow-live-provider`,或继续把真实 tool runtime 执行从 fixture evidence 扩到 live turn 证据。
|
||||
467. P3.220 Claw streaming live-provider gate 回归:继续沿完整目标的真实业务闭环缺口推进,但当前没有显式 live Provider 授权,因此本轮只验证 fail-closed gate,防止后续把 mock、无模型探针或自动 Provider 探测误当作完整 Claw streaming 证据。运行 `npm run smoke:claw-chat-ready-streaming -- --timeout-ms 30000` 按预期快速失败于 live provider gate,输出“默认禁止执行;如确需运行,请显式传入 --allow-live-provider,或设置 LIME_ALLOW_LIVE_PROVIDER_SMOKE=1 / LIME_REAL_API_TEST=1”,且没有进入 `wait-health`、DevBridge health、`aster_agent_init`、`get_api_key_providers` 或 `test_api_key_provider_chat`。该结果说明完整 Claw streaming GUI E2E 仍处于未授权状态,不允许用 default mock、renderer mock fallback、App Server mock backend 或 external backend fixture 替代。
|
||||
467. P3.220 Claw streaming live-provider gate 回归:继续沿完整目标的真实业务闭环缺口推进,但当前没有显式 live Provider 授权,因此本轮只验证 fail-closed gate,防止后续把 mock、无模型探针或自动 Provider 探测误当作完整 Claw streaming 证据。运行 `npm run smoke:claw-chat-ready-streaming -- --timeout-ms 30000` 按预期快速失败于 live provider gate,输出“默认禁止执行;如确需运行,请显式传入 --allow-live-provider,或设置 LIME_ALLOW_LIVE_PROVIDER_SMOKE=1 / LIME_REAL_API_TEST=1”,且没有进入 `wait-health`、DevBridge health、`aster_agent_init`、App Server `modelProvider/list` 或 `modelProvider/testChat`。该结果说明完整 Claw streaming GUI E2E 仍处于未授权状态,不允许用 default mock、renderer mock fallback、App Server mock backend 或 external backend fixture 替代。
|
||||
468. P3.221 Agent session history Electron current 证据:继续推进“对话 / 归档 / 历史详情无法获取”主缺口中不需要 live Provider 的 current 子链路,本轮只认领 `scripts/electron/session-history-fixture-smoke.mjs`、`scripts/electron/session-history-fixture-smoke.test.mjs`、`scripts/check-app-server-client-contract.mjs` 与本计划 / 前端矩阵记录,不修改 `lime-rs/crates/app-server/src/local_data_source.rs`。真实 Electron fixture 使用 `APP_SERVER_BACKEND_MODE=unavailable`,通过 preload `window.electronAPI.invoke("app_server_handle_json_lines")` 执行 `initialize -> initialized -> agentSession/start -> agentSession/read -> agentSession/update -> agentSession/read -> agentSession/list`,禁止 `agentSession/turn/start`,也不调用模型后端、external backend、legacy `agent_runtime_*`、default mock 或 renderer mock fallback。
|
||||
469. P3.221 增量验证 / 证据:`npx vitest run "scripts/electron/session-history-fixture-smoke.test.mjs" --silent=passed-only --disableConsoleIntercept` 通过,静态守卫锁住真实 Electron Desktop Host IPC、`APP_SERVER_BACKEND_MODE=unavailable`、current session history methods、hydrate detail 数组和禁 mock / legacy;`node --check "scripts/electron/session-history-fixture-smoke.mjs" "scripts/electron/session-history-fixture-smoke.test.mjs"` 通过;`node "scripts/electron/session-history-fixture-smoke.mjs" --timeout-ms 120000 --keep-temp` 通过,summary 位于 `.lime/qc/gui-evidence/agent-session-history-electron-fixture/agent-session-history-electron-fixture-summary.json`,显示 `ok=true`、`backendMode=unavailable`、`electronPreloadBridge=true`、`missingRequiredMethods=[]`、`forbiddenMethodsSeen=[]`、`listSessionFound=true`、`listedSession.title=Electron history fixture restored`、`consoleErrors=[]`;raw evidence 显示 request methods 为 `initialize, agentSession/start, agentSession/read, agentSession/update, agentSession/read, agentSession/list`,两次 read detail 都包含 `turns/items/queued_turns/thread_read`。`scripts/check-app-server-client-contract.mjs` 新增 guard 防止该 fixture 回流 `agent_runtime_*`、mock backend 或 live provider gate。该证据证明历史详情 hydrate 所需的 App Server current read/list/update 形状可经真实 Electron Host bridge 获取;它仍不等同真实 live turn streaming 或旧历史数据全量迁移完成。
|
||||
470. P3.221 收口验证 / Forge 事实源校准:本轮补跑 `npx prettier --check "scripts/check-app-server-client-contract.mjs" "internal/exec-plans/app-server-implementation-plan.md" "internal/roadmap/appserver/frontend-integration-matrix.md"`、`git diff --check -- "scripts/check-app-server-client-contract.mjs" "internal/exec-plans/app-server-implementation-plan.md" "internal/roadmap/appserver/frontend-integration-matrix.md"`、`node --check "scripts/electron/session-history-fixture-smoke.mjs" "scripts/electron/session-history-fixture-smoke.test.mjs"` 与 `npx vitest run "scripts/electron/session-history-fixture-smoke.test.mjs" --silent=passed-only --disableConsoleIntercept` 均通过。按最新 `AGENTS.md`,Electron packaging / installer / signing / notarization / updater metadata 的 current 事实源已切到 `forge.config.mjs`、`electron-forge package`、`electron-forge make` 与仓库内 Forge maker,`electron-builder.yml` / `electron-builder` CLI 按 `dead` 处理;`scripts/check-app-server-client-contract.mjs` 当前已改为锁定 `scripts/run-electron-package-dir.mjs` 使用 Forge package,`node "scripts/check-app-server-client-contract.mjs"` 通过 202 项。复跑 `npm run test:contracts` 完整通过,`mock priority commands: 0`,说明 P3.221 session history current 链路不再被旧 builder 守卫误挡;release/updater 文档、docs guard、entrypoint guard、release workflow、staging / verify 脚本与 package lock 的 Forge 成组收口继续归 packaging 写集处理。
|
||||
@@ -1563,7 +1563,7 @@ git diff --check
|
||||
547. P3.256 Claw 标题 hydrate 残留修复:接 P3.255 的非阻塞 UI 残留,本轮只认领 Agent Chat topic projection 与 Claw current fixture guard,不改 `lime-rs/crates/app-server/src/local_data_source.rs`,不触发 live Provider。`mapSessionToTopic(...)` 在缺失 / 非法 session timestamp 时不再把 epoch fallback 暴露为 `任务 1970/1/1`,而是显示 `新任务`;`upsertTopicFromSessionDetail(...)` 在 detail 刷新只带通用 fallback 标题(`新任务` 或 `任务 YYYY/M/D`)时保留已有真实标题,避免 App Server read detail 缺少标题时覆盖侧栏已经确认的 session title。`scripts/agent-runtime/claw-chat-current-fixture-smoke.mjs` 新增 `noEpochFallbackTitle` 断言,防止完成页再次出现 `任务 1970/1/1`。
|
||||
548. P3.256 验证 / 证据:定向验证通过:`npx prettier --check "src/components/agent/chat/hooks/agentChatShared.ts" "src/components/agent/chat/hooks/agentChatShared.test.ts" "src/components/agent/chat/hooks/agentSessionTopicViewModel.ts" "src/components/agent/chat/hooks/agentSessionTopicViewModel.unit.test.ts" "scripts/agent-runtime/claw-chat-current-fixture-smoke.mjs" "scripts/agent-runtime/claw-chat-current-fixture-smoke.test.mjs"`、`npx vitest run "src/components/agent/chat/hooks/agentChatShared.test.ts" "src/components/agent/chat/hooks/agentSessionTopicViewModel.unit.test.ts" "scripts/agent-runtime/claw-chat-current-fixture-smoke.test.mjs" --silent=passed-only --disableConsoleIntercept`、`git diff --check -- ...`。重建 renderer smoke bundle `npm run build:renderer:electron:smoke` 通过;聚合 `npm run smoke:agent-runtime-current-fixture` 通过,`liveProviderUsed=false`;真实 Electron `npm run smoke:claw-chat-current-fixture -- --timeout-ms 180000` 通过,summary session `claw-chat-current-1780848889813-2661` 显示 `ok=true`、`guiCompleted.hasEpochFallbackTitle=false`、`assertions.noEpochFallbackTitle=true`、`guiUserMessageVisible=true`、`guiAssistantOutputVisible=true`、`guiInputRemainsReady=true`、`guiNotStuckStreaming=true`、`noConsoleErrors=true`、`noInvokeErrors=true`,完成页片段已从 `任务 1970/1/1` 变为 `Claw 新闻输入 Electron fixture`。该证据仍是 non-live external fixture backend,不证明 live Provider streaming / 真实 WebSearch 联网;下一刀应继续推进 explicit live gate 或 Agent App/tool runtime live turn evidence。
|
||||
549. P3.256 默认 Electron GUI smoke 复验:在 2026-06-08 当前并行工作树仍包含 Rust App Server、Electron Host、Agent Chat、脚本目录治理和 updater 写集的状态下,本轮只认领本计划与前端矩阵记录,不修改 `lime-rs/crates/app-server/src/local_data_source.rs`、`runtime.rs`、`processor.rs`、`runtime_backend.rs`、`electron/appServerHost.ts` 或 Agent Chat streaming 文件。`npm run verify:gui-smoke` 通过,覆盖 `generate:agent-runtime-clients`、`generate:extension-site-adapters`、`verify:app-version`、Electron renderer smoke build、`packages/app-server-client` build、Electron host typecheck、main/preload build、desktop assets、App Server sidecar assets 与 `scripts/electron-smoke.mjs`;输出确认 `[electron-smoke] renderer loaded`、`[electron-host] app-server ready protocol=appserver.v0 version=1.59.0`、`[electron-smoke] app-server initialized protocol=appserver.v0 version=1.59.0` 与 `[electron-smoke] claw workbench shell ready`。该证据证明默认 `Frontend -> Electron Desktop Host IPC -> App Server JSON-RPC` 壳链和 Claw composer 准备态未被当前迁移打断;不发送 turn、不调用 Provider、不证明 `agentSession/event -> agentSession/read` 业务刷新。
|
||||
550. P3.256 live provider gate fail-closed 复验:`npm run smoke:claw-chat-ready-streaming -- --timeout-ms 30000` 按预期以退出码 1 停在 live Provider 授权检查,提示必须显式传入 `--allow-live-provider` 或设置 `LIME_ALLOW_LIVE_PROVIDER_SMOKE=1 / LIME_REAL_API_TEST=1`。该运行没有进入 DevBridge health、`wait-health`、`prepare-runtime`、`aster_agent_init`、`get_api_key_providers`、`test_api_key_provider_chat` 或真实模型调用,也没有使用 App Server mock backend、renderer mock fallback、`mockPriorityCommands`、`defaultMocks` 或 `invokeMockOnly` 绕过。完整业务闭环仍等待显式 live Provider 授权后运行 `scripts/claw-chat-ready-streaming-smoke.mjs --allow-live-provider`,验证真实发送、streaming、WebSearch/WebFetch 联网、tool runtime live 执行与 read model 回流。
|
||||
550. P3.256 live provider gate fail-closed 复验:`npm run smoke:claw-chat-ready-streaming -- --timeout-ms 30000` 按预期以退出码 1 停在 live Provider 授权检查,提示必须显式传入 `--allow-live-provider` 或设置 `LIME_ALLOW_LIVE_PROVIDER_SMOKE=1 / LIME_REAL_API_TEST=1`。该运行没有进入 DevBridge health、`wait-health`、`prepare-runtime`、`aster_agent_init`、App Server `modelProvider/list`、`modelProvider/testChat` 或真实模型调用,也没有使用 App Server mock backend、renderer mock fallback、`mockPriorityCommands`、`defaultMocks` 或 `invokeMockOnly` 绕过。完整业务闭环仍等待显式 live Provider 授权后运行 `scripts/claw-chat-ready-streaming-smoke.mjs --allow-live-provider`,验证真实发送、streaming、WebSearch/WebFetch 联网、tool runtime live 执行与 read model 回流。
|
||||
551. P3.257 默认 Electron GUI smoke 当前轮复验:在 P3.256 标题 hydrate 修复和当前 dist 重建后,本轮再次执行 `npm run verify:gui-smoke`,命令完整通过。覆盖链路包括 `generate:agent-runtime-clients`、`generate:extension-site-adapters`、`verify:app-version`、renderer smoke build、`packages/app-server-client` build、`typecheck:electron`、main/preload build、desktop assets、App Server sidecar assets 和真实 Electron smoke。关键输出确认 `版本一致性检查通过: 1.59.0`、`app-server ready protocol=appserver.v0 version=1.59.0`、`app-server initialized protocol=appserver.v0 version=1.59.0`、`renderer loaded` 与 `claw workbench shell ready`。该证据证明当前工作树下 Electron Desktop Host IPC、App Server sidecar initialize 和默认 Claw composer 准备态仍可启动;它不发送 Claw turn,也不调用正式 Provider。
|
||||
552. P3.257 live Provider 默认门禁当前轮复验:本轮再次执行 `npm run smoke:claw-chat-ready-streaming -- --timeout-ms 30000`,按预期以退出码 1 停在授权检查,输出要求显式传入 `--allow-live-provider` 或设置 `LIME_ALLOW_LIVE_PROVIDER_SMOKE=1 / LIME_REAL_API_TEST=1`。该结果证明当前默认质量入口仍 fail-closed,不会在未授权情况下调用真实模型、多模态 Provider 或 WebSearch/WebFetch live path,也没有用 App Server mock backend / renderer mock fallback 绕过。完整整体目标仍需要用户显式授权后再跑 live Provider streaming E2E;在授权前只能继续补 non-live current fixture 与 GUI 可见证据。
|
||||
553. P3.258 代码产物工作台真实 Electron 复验:继续补完整目标中的“有产物 / 代码工作台”GUI 证据,本轮复跑 `npx vitest run "scripts/electron/code-artifact-workbench-fixture-smoke.test.mjs" --silent=passed-only --disableConsoleIntercept`,4 项静态守卫通过,锁住真实 Electron Desktop Host IPC、App Server JSON-RPC、external fixture backend、artifact snapshot、tool timeline GUI evidence、禁止 live Provider / mock backend / renderer mock / legacy command。随后执行 `npm run smoke:code-artifact-workbench-electron-fixture -- --timeout-ms 180000`,真实 Electron fixture 通过,stage 覆盖 `launch-electron -> wait-renderer -> initialize-app-server -> ensure-default-workspace -> create-code-artifact-session -> bind-gui-workspace -> verify-session-list -> navigate-gui-workspace -> open-session-from-sidebar -> wait-session-hydrated -> open-workbench`。
|
||||
@@ -1595,7 +1595,7 @@ git diff --check
|
||||
579. P3.269 live WebSearch/WebFetch read envelope 证据补强:继续降低显式授权后才暴露的脚本误判风险,本轮只认领 `scripts/lib/claw-chat-live-web-tool-evidence.mjs`、`scripts/lib/claw-chat-live-web-tool-evidence.test.mjs`、`scripts/lib/live-provider-smoke-gate.test.mjs`、本计划与前端矩阵记录,不触碰 live smoke 主流程、Rust App Server、Electron Host 或 Agent Chat runtime 写集。`collectToolCallsFromValue(...)` 现在除 `thread_read/threadRead/runtime_summary/messages/items/turns` 外,还会递归 `result/detail/session/session_detail/sessionDetail` envelope;新增行为测试用 App Server JSON-RPC `agentSession/read` 的 `result.detail.thread_read.tool_calls` 形状证明 `WebSearch/WebFetch` turn-scoped evidence 不会因响应外壳漏提取。该刀仍不运行真实 Provider、不调用 live WebSearch/WebFetch;完整整体目标仍需显式授权后跑联网 E2E。
|
||||
580. P3.270 Electron 领域脚本根目录收缩:继续沿 Forge-only release/updater 与 `scripts/` 根目录治理目标推进,本轮只认领 Electron build / package / smoke / release helper 脚本、`package.json`、Forge config、Electron current guards、`scripts/README.md`、`scripts/script-root-governance-baseline.json`、release-updater 路线图与本计划记录,不碰 Rust/App Server/Agent Chat 并行写集。已用 `git mv` 将 14 个 Electron current 根脚本迁入 `scripts/electron/`:`brand-mac-helper-apps`、host/renderer build、desktop assets、App Server packaged assets、dev/preview/package-dir、smoke、release staging 和 package verifier;`package.json`、`forge.config.mjs`、release workflow guard、contract guard、quality planner、Electron current docs/entrypoint guards 与 release updater tests 均同步指向领域目录。`scripts/script-root-governance-baseline.json` 删除这些根目录例外,`npm run governance:scripts` 从 `rootFiles=119` 收缩为 `rootFiles=105 retired=0 untrackedNew=0`。
|
||||
581. P3.270 验证 / 边界:定向语法验证通过 `node --check` 覆盖迁入 `scripts/electron/` 的 build / assets / run / smoke / staging / verifier / branding 脚本;定向单测通过 `npx vitest run "scripts/electron/current-entrypoints.test.mjs" "scripts/electron/current-docs-guard.test.mjs" "scripts/electron/brand-mac-helper-apps.test.mjs" "scripts/electron/verify-package-resources.test.mjs" --silent=passed-only --disableConsoleIntercept`,4 文件 32 项;release/updater 组合通过 `npx vitest run "scripts/electron/release-updater-assets.test.mjs" "scripts/electron/release-workflow-guard.test.mjs" "scripts/electron/current-docs-guard.test.mjs" --silent=passed-only --disableConsoleIntercept`,3 文件 23 项;`npm run governance:electron-release-workflow` 与 `npm run governance:scripts` 均通过。该刀只治理 current 脚本事实源和守卫,不声称 macOS DMG/sign/notarize GitHub Actions、Windows Squirrel runner 或 live Provider WebSearch/WebFetch E2E 已完成。
|
||||
582. P3.271 live Provider gate 递归脚本守卫:只读子代理指出 `scripts/lib/live-provider-smoke-gate.test.mjs` 只扫描 `scripts/` 根层 `.mjs`,而当前 smoke 已迁入 `scripts/agent-app/`、`scripts/agent-runtime/`、`scripts/app-server/`、`scripts/electron/` 等子目录;这会让后续子目录新增真实 `agent_runtime_submit_turn` / `test_api_key_provider_chat` / `knowledge_compile_pack` 调用时绕过未授权 gate 静态守卫。本轮只认领 live provider gate 测试和本计划 / 矩阵记录,把 `listSmokeScripts()` 改成递归扫描 `scripts/**/*.mjs`,排除 `*.test.mjs`、`scripts/lib/**`、`check-*` 与测试夹具路径;现有子目录脚本在递归扫描下仍全部通过,证明当前没有未授权 live Provider 子目录回流。该刀不运行真实 Provider、不触碰生产 bridge;完整整体目标仍需显式授权后跑 live streaming / WebSearch-WebFetch 实际联网 E2E。
|
||||
582. P3.271 live Provider gate 递归脚本守卫:只读子代理指出 `scripts/lib/live-provider-smoke-gate.test.mjs` 只扫描 `scripts/` 根层 `.mjs`,而当前 smoke 已迁入 `scripts/agent-app/`、`scripts/agent-runtime/`、`scripts/app-server/`、`scripts/electron/` 等子目录;这会让后续子目录新增真实 `agent_runtime_submit_turn` / App Server `modelProvider/testChat` / `knowledge_compile_pack` 调用时绕过未授权 gate 静态守卫。本轮只认领 live provider gate 测试和本计划 / 矩阵记录,把 `listSmokeScripts()` 改成递归扫描 `scripts/**/*.mjs`,排除 `*.test.mjs`、`scripts/lib/**`、`check-*` 与测试夹具路径;现有子目录脚本在递归扫描下仍全部通过,证明当前没有未授权 live Provider 子目录回流。该刀不运行真实 Provider、不触碰生产 bridge;完整整体目标仍需显式授权后跑 live streaming / WebSearch-WebFetch 实际联网 E2E。
|
||||
583. P3.272 live WebSearch/WebFetch turn provider / routing 断言:只读子代理指出 live Web 工具 turn 虽记录 `summary.liveWebSubmitTurnConfig`,但 verdict 只校验 long/follow turn 的 provider/model 与 fast-response routing,导致授权后 WebSearch/WebFetch turn 即使走错 provider/model 或开启 fast routing 也可能误通过。本轮只认领 `scripts/claw-chat-ready-streaming-smoke.mjs`、`scripts/lib/live-provider-smoke-gate.test.mjs`、本计划与矩阵记录;live smoke 现在新增 `liveWebProviderPreferenceHonored`、`liveWebModelPreferenceHonored` 与 `liveWebFastResponseRoutingDisabled`,并纳入最终 verdict,要求 Web 工具 turn 本身同样保持 explicit provider/model 和禁用 fast-response routing。该刀不运行真实 Provider;完整整体目标仍需显式授权后跑 live streaming / WebSearch-WebFetch 实际联网 E2E。
|
||||
584. P3.273 App Server `detail.thread_read` current 证据保留:只读子代理指出 `readAppServerThreadRead()` 从 `agentSession/read` 的 turns 重建最小 thread read,会丢掉 App Server current `detail.thread_read.tool_calls/artifacts/model_routing` 等证据。本轮只认领 `scripts/claw-chat-ready-streaming-smoke.mjs` 与本计划 / 矩阵记录;`appServerThreadReadFromSessionRead(...)` 现在优先展开 `detail.thread_read` / `detail.threadRead`,再补充 active turn、queued turns、latest status 与 routing fallback,确保 smoke summary 的 `threadRead` 不再弱化 App Server current read model。该刀不运行真实 Provider;完整整体目标仍需显式授权后跑 live streaming / WebSearch-WebFetch 实际联网 E2E。
|
||||
585. P3.274 live WebSearch/WebFetch 输出证据门禁:只读子代理指出工具 `status=completed` 即算通过仍可能弱化“真实 WebFetch 打开页面并确认事实”的证据。本轮只认领 `scripts/lib/claw-chat-live-web-tool-evidence.mjs`、`scripts/lib/claw-chat-live-web-tool-evidence.test.mjs`、`scripts/claw-chat-ready-streaming-smoke.mjs`、`scripts/lib/live-provider-smoke-gate.test.mjs` 与本计划 / 矩阵记录;helper 现在为 required tools 计算 `outputPresent`、`allRequiredOutputPresent`、`allRequiredOutputPresentForTurn`,行为测试覆盖完成态但空输出的 `WebFetch` 不满足 output present,live smoke verdict 新增 `liveWebRequiredToolOutputsPresent`。该刀不运行真实 Provider;完整整体目标仍需显式授权后跑联网 E2E。
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
|
||||
2026-05-18 00:08 已新增 Connector outbox runtime smoke harness:`scripts/agent-app/connector-outbox-smoke.mjs`。本轮未启动新的 GUI flow,也未重新调用模型,只用 `--mode replay` 读取既有 live session `f08e135b-58a7-44eb-8f16-fc6809a5d045` / task `agent-app-connector-outbox-runtime-mp9xw68t`,生成 `.lime/qc/gui-evidence/agent-apps/p18-7-e-connector-outbox-runtime-smoke-replay-20260518-summary.json`;断言 `threadReadCompleted / connectorToolCallProjected / outputHadBoundedMetadata / toolCallEvidenceProjected / threadEvidenceProjected / taskEventEvidenceProjected` 全为 true。该脚本后续可在无并行 GUI smoke 时用 `--mode live --provider-preference ... --model-preference ...` 生成新 runtime outbox 证据,但仍不会完成真实 OAuth handshake、secret delivery 或外部 Cloud delivery。
|
||||
|
||||
2026-05-18 00:13 已增强 Connector outbox runtime smoke harness live 模式:`scripts/agent-app/connector-outbox-smoke.mjs` 的 `--mode live` 不再要求手工传 provider/model;若省略 `--provider-preference / --model-preference`,会只读 `get_api_key_providers` / `get_api_key_provider`,优先选择 enabled provider 中的 `deepseek/openai/anthropic/gemini/azure-openai`,并偏向 `flash/mini/lite` 模型。因隔壁正在跑内容工厂 GUI flow,本轮只执行 `node --check` 与 `--mode replay`,新 replay 证据为 `.lime/qc/gui-evidence/agent-apps/p18-7-e-connector-outbox-runtime-smoke-replay-auto-provider-20260518-summary.json`;未启动 GUI,也未重新调用模型。
|
||||
2026-05-18 00:13 已增强 Connector outbox runtime smoke harness live 模式:`scripts/agent-app/connector-outbox-smoke.mjs` 的 `--mode live` 不再要求手工传 provider/model;若省略 `--provider-preference / --model-preference`,现在必须只读 App Server current `modelProvider/list` / `modelProvider/read`,优先选择 enabled provider 中的 `deepseek/openai/anthropic/gemini/azure-openai`,并偏向 `flash/mini/lite` 模型。旧 `get_api_key_providers` / `get_api_key_provider` 已随 P7 Provider 迁移判为 `dead`,不得再作为 live harness 正向证据。因隔壁正在跑内容工厂 GUI flow,本轮只执行 `node --check` 与 `--mode replay`,新 replay 证据为 `.lime/qc/gui-evidence/agent-apps/p18-7-e-connector-outbox-runtime-smoke-replay-auto-provider-20260518-summary.json`;未启动 GUI,也未重新调用模型。
|
||||
|
||||
2026-05-18 00:22 已跑通 focused live runtime connector outbox smoke:`scripts/agent-app/connector-outbox-smoke.mjs --mode live` 自动选择 `deepseek/deepseek-v4-flash`,生成 `.lime/qc/gui-evidence/agent-apps/p18-7-e-connector-outbox-runtime-smoke-live-auto-provider-final-20260518-summary.json`,断言 `threadReadCompleted / connectorToolCallProjected / outputHadBoundedMetadata / toolCallEvidenceProjected / threadEvidenceProjected / taskEventEvidenceProjected` 全为 true;`threadRead.toolCallCount=1`,同一个 `outbox://connector/notion/createPage/...` evidence ref 同时出现在 tool call、thread evidence summary 和 Agent App task events。中途发现 harness 会过早在 evidence 出现时返回、且会选中早期失败 tool call,已修正为优先选带 expected evidence ref 的成功 tool call,并等待 `threadRead.status=completed`;修正后 replay 复核 `.lime/qc/gui-evidence/agent-apps/p18-7-e-connector-outbox-runtime-smoke-live-auto-provider-fixed2-recheck-20260518-summary.json` 也全绿。该 live smoke 仍只证明 non-fixture Cloud Overlay outbox/evidence runtime 链路,不证明真实 OAuth handshake、secret delivery 或外部 Cloud delivery。
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
| HTML artifact 工具栏切换 | `HtmlRenderer` 接入外部 `viewMode / previewSize` 受控状态;工作区已有外部工具栏时隐藏内部重复工具栏,源码切换会真实移除 iframe 并渲染源码 | 已实现 |
|
||||
| `.htm` 产物识别 | `buildArtifactFromWrite` 将 `.htm` 构建为 `html` artifact,并标记 `language: "html"` | 已实现 |
|
||||
| Tauri 权限与 CSP | `tauri.conf*.json` 启用 `assetProtocol`,CSP 覆盖 `asset:` / `http://asset.localhost` / `frame-src`;capability 覆盖 `html-preview-*` 与 `core:webview:allow-create-webview-window` | 已实现 |
|
||||
| Provider 设置页不再误导 | 文本模型按钮显示“试跑当前模型”,并通过 `test_api_key_provider_chat` 做真实聊天试跑 | 已实现 |
|
||||
| Provider 设置页不再误导 | 文本模型按钮显示“试跑当前模型”,并通过 App Server current `modelProvider/testChat` 做真实聊天试跑;旧 `test_api_key_provider_chat` 已判为 `dead`,不得作为正向证据恢复 | 已实现 |
|
||||
| 402 额度错误友好化 | `agentRuntimeErrorPresentation` 将 `402 / Payment Required / Insufficient Balance / 余额不足 / 额度不足` 归一为额度不足提示;聊天时间线、折叠摘要、流式失败 assistant patch 与会话概览均在 presentation 层显示友好提示,不再向普通用户暴露 raw Provider error | 已实现 |
|
||||
| 失败态不过度重复 | 失败详情只保留在时间线错误卡等主解释位置;assistant 正文若只是同一失败详情的重复文案会被隐藏,底部运行状态行只保留“失败 + 耗时”等短状态 | 已实现 |
|
||||
| 五语言文案 | `settings.json` 与 `agent.json` 已覆盖 `zh-CN / zh-TW / en-US / ja-JP / ko-KR` | 已实现 |
|
||||
|
||||
@@ -38,16 +38,16 @@ Electron main 是 Desktop Host bridge,不是后端适配层,也不是第二
|
||||
18. `src/lib/api/memory.ts` 的 `getProjectMemory(...)` 聚合读已通过 `AppServerClient.request("projectMemory/read", { projectId })` 进入 App Server current 主链;空 `projectId` 或 App Server 未返回 `memory` 时 fail closed,不再调用 `project_memory_get`、renderer mock 或 Electron legacy facade。`scripts/project-memory-current-smoke.mjs` 已通过真实 Agent 首页 / 工作区证明默认工作区解析后项目记忆读取经 `app_server_handle_json_lines` 观察到 `projectMemory/read`,且未观察到旧 `project_memory_get` 命令。角色 / 世界观 / 大纲 CRUD 仍暂走既有命令,因为 App Server 目前只覆盖项目记忆聚合读。
|
||||
19. Agent App mock SDK 只允许测试环境显式使用;`mockSdkEnabled` 在生产构建中硬关闭,`buildMockCapabilityProfile`、`MockCapabilityHost`、`createMockLimeCapabilityTransport` 都必须通过 test-only 断言,且 mock transport 只允许测试夹具显式导入。public SDK / production runtime 不再导出或导入 mock transport、mock host、mock capability profile 或 mock handler 类型;生产 Agent App runtime 缺真实 App Server / Electron current method 时必须 fail closed,不能用 mock capability profile、mock host 或 mock transport 作为产品闭环。
|
||||
20. `src/lib/api/project.ts` 的 workspace/project 读与目录就绪链路已通过 `AppServerClient.request(...)` 进入 App Server current 主链,并直接复用 `packages/app-server-client/src/protocol.ts` 的 `METHOD_WORKSPACE_*` 常量;`workspace/list`、`workspace/read`、`workspace/byPath/read`、`workspace/default/read`、`workspace/default/ensure`、`workspace/projectsRoot/read`、`workspace/projectPath/resolve`、`workspace/ensureReady` 缺少必需 result 时 fail closed,不再把 `workspace_get*`、`workspace_list`、`workspace_ensure_*` 或 `get_or_create_default_project` 当作前端事实源。`workspace_create/update/delete/set_default` 与 content 相关命令仍暂保 compat,因为 App Server 目前没有完整等价写 method。
|
||||
21. `src/lib/api/modelRegistry.ts` 的模型 registry / preference / sync 读链,以及 provider / tier / alias scoped 读链的前端 gateway,已通过 `AppServerClient.request(...)` 进入 App Server current 主链,并直接复用 `packages/app-server-client/src/protocol.ts` 的 `METHOD_MODEL_*` 常量;`model/list`、`modelPreferences/list`、`modelSyncState/read`、`modelProviderAlias/read`、`modelProviderAlias/list` 缺少必需 result 时 fail closed,不再把 `get_model_registry`、`get_model_preferences`、`get_model_sync_state`、`get_models_for_provider`、`get_models_by_tier`、`get_provider_alias_config` 或 `get_all_alias_configs` 当作前端事实源。`scripts/model-registry-current-smoke.mjs` 已通过真实设置页“添加模型”面板证明 `model/list`、`modelPreferences/list`、`modelSyncState/read` 经 `app_server_handle_json_lines` 到达 App Server,且未观察到旧 `get_model_*` 命令、模型写入 / refresh side-effect 或 `agentSession/turn/start`。本 GUI 证据不覆盖 `modelProviderAlias/read` / `modelProviderAlias/list` 的 alias GUI 触发,不覆盖 provider / tier scoped 查询或 provider ids 兼容查询。`refresh_model_registry`、`search_models`、`toggle_model_favorite`、`hide_model`、`record_model_usage`、`fetch_provider_models_auto` 与 provider ids 兼容查询仍暂保 compat,因为 App Server 目前没有完整等价写 / refresh / live fetch method。
|
||||
21. `src/lib/api/modelRegistry.ts` 的模型 registry / preference / sync 读链,以及 provider / tier / alias scoped 读链的前端 gateway,已通过 `AppServerClient.request(...)` 进入 App Server current 主链,并直接复用 `packages/app-server-client/src/protocol.ts` 的 `METHOD_MODEL_*` / `METHOD_MODEL_PROVIDER_*` 常量;`model/list`、`modelPreferences/list`、`modelSyncState/read`、`modelProvider/fetchModels`、`modelProviderAlias/read`、`modelProviderAlias/list` 缺少必需 result 时 fail closed,不再把 `get_model_registry`、`get_model_preferences`、`get_model_sync_state`、`get_models_for_provider`、`get_models_by_tier`、`get_provider_alias_config`、`get_all_alias_configs` 或 `fetch_provider_models_auto` 当作前端事实源。`scripts/model-registry-current-smoke.mjs` 已通过真实设置页“添加模型”面板证明 `model/list`、`modelPreferences/list`、`modelSyncState/read` 经 `app_server_handle_json_lines` 到达 App Server,且未观察到旧 `get_model_*` 命令、模型写入 / refresh side-effect 或 `agentSession/turn/start`。本 GUI 证据不覆盖 `modelProviderAlias/read` / `modelProviderAlias/list` 的 alias GUI 触发,不覆盖 provider / tier scoped 查询或 provider ids 兼容查询。`refresh_model_registry`、`search_models`、`toggle_model_favorite`、`hide_model`、`record_model_usage` 与 provider ids 兼容查询仍暂保 compat,因为 App Server 目前没有完整等价写 / refresh method。
|
||||
22. `src/lib/api/knowledge.ts` 的 `listKnowledgePacks(...)` 已通过 `AppServerClient.request("knowledgePack/list", ...)` 进入 App Server current 主链;空 `workingDir`、缺少 `workingDir/rootPath` 或 `packs` 非数组时 fail closed,不再把 `knowledge_list_packs`、renderer mock 或 Electron legacy facade 当作前端事实源。`scripts/knowledge-current-smoke.mjs` 已通过真实 GUI 点击路径证明项目资料页面列表读取经 `app_server_handle_json_lines` 观察到 `knowledgePack/list`,且未观察到旧 `knowledge_list_packs` 命令。`knowledge_get_pack`、`knowledge_import_source`、`knowledge_compile_pack`、`knowledge_set_default_pack`、`knowledge_update_pack_status`、`knowledge_resolve_context`、`knowledge_validate_context_run` 仍暂保 compat,因为 App Server 当前只覆盖知识包列表读链。
|
||||
23. `src/lib/api/skill-execution.ts` 的 `listExecutableSkills()` 与 `getSkillDetail(...)` 已通过 `AppServerClient.request(...)` 进入 App Server current 主链,并直接复用 `packages/app-server-client/src/protocol.ts` 的 `METHOD_SKILL_LIST` / `METHOD_SKILL_READ`;`skill/list` 或 `skill/read` 缺少必需 result 时 fail closed,不再把 `list_executable_skills`、`get_skill_detail`、renderer mock 或 Electron legacy facade 当作前端读事实源。`scripts/skills-current-smoke.mjs` 已通过真实 GUI slash skill preflight 证明 `skill/list` 经 `app_server_handle_json_lines` 返回 `result.skills` 数组,且未观察到旧 Skill 命令、`execute_skill` 或 `agentSession/turn/start`。`skill/read` 真实 GUI 触发与 `execute_skill` 仍暂保 compat side-effect,因为 App Server 当前没有完整等价执行 method。
|
||||
24. `src/lib/api/agentApps.ts` 的 `listInstalledAgentApps(...)` 已通过 `AppServerClient.request("agentAppInstalled/list", {})` 进入 App Server current 主链;缺少 `states` 或 `issues` 时 fail closed,不再把 `agent_app_list_installed`、renderer mock 或 Electron legacy facade 当作前端事实源。`scripts/agent-app/installed-current-smoke.mjs` 已通过真实 GUI 点击路径证明 Agent Apps 管理页已安装列表读取经 `app_server_handle_json_lines` 观察到 `agentAppInstalled/list`,且未观察到旧 `agent_app_list_installed` 命令。`agent_app_save_installed_state`、install / uninstall / shell 等写入或 desktop side-effect 命令仍暂保 compat,因为 App Server 当前只覆盖已安装列表读链;UI runtime start / status / stop 已拆到 `agentAppUiRuntime/*` current method。`scripts/agent-app/ui-runtime-current-smoke.mjs` 已通过真实前端 gateway 证明 UI runtime `status` / `stop` / `start` 均经 `app_server_handle_json_lines -> agentAppUiRuntime/*` 到达 App Server,未观察到旧 `agent_app_*_ui_runtime` 命令,并用一次性不存在的 appId 验证 `start` fail closed、不写 installed state、不启动子进程;`scripts/agent-app/ui-runtime-stdio-fixture-smoke.mjs` 已进一步证明 App Server stdio / RuntimeCore 可用 fixture installed state 启动真实 npm dev 子进程,完成 `/api/bootstrap` ready、`entryUrl` 可达、status running 与 stop stopped 生命周期;`scripts/agent-app/ui-runtime-electron-fixture-smoke.mjs` 已证明真实 Electron-host GUI 能从正式侧栏进入 fixture Agent App runtime 页面,发出 `agentAppUiRuntime/start`,并让 iframe surface 打开 App Server 返回的 `entryUrl`。该证据只覆盖 Agent App UI runtime 生命周期,不覆盖 Agent 对话 turn、tool runtime、evidence 或 Claw/Aster 完整执行链。
|
||||
25. `src/lib/api/automation.ts` 的 `getAutomationJobs()` 已通过 `AppServerClient.request("automationJob/list", {})` 进入 App Server current 主链;缺少 `jobs` 时 fail closed,不再把 `get_automation_jobs`、renderer mock 或 Electron legacy facade 当作前端事实源。`scripts/automation-current-smoke.mjs` 已通过真实 GUI 点击路径证明持续流程任务列表读取经 `app_server_handle_json_lines` 观察到 `automationJob/list`,且未观察到旧 `get_automation_jobs` 命令。scheduler / status / get single / create / update / delete / run / health / history / schedule preview / validate 等命令仍暂保 compat,因为 App Server 当前只覆盖自动化任务列表读链。
|
||||
26. `src/lib/api/apiKeyProvider.ts` 的 `getProviders()` 与 `getSystemProviderCatalog()` 已通过 `AppServerClient.request(...)` 进入 App Server current 主链,并直接复用 `METHOD_MODEL_PROVIDER_LIST` / `METHOD_MODEL_PROVIDER_CATALOG_LIST`;缺少 `providers` 时 fail closed,不再把 `get_api_key_providers`、`get_system_provider_catalog`、renderer mock 或 Electron legacy facade 当作前端读事实源。`scripts/provider-settings-current-smoke.mjs` 已通过真实 GUI 点击路径证明 AI 服务商设置页和“添加模型”catalog 读取经 `app_server_handle_json_lines` 观察到 `modelProvider/list`、`modelProvider/catalog/list`,且未观察到旧 Provider 命令。单 Provider 读取、自定义 Provider 写入、API Key 写入 / 启停 / 导入导出、UI state、连接测试和 chat test 仍暂保 compat,因为 App Server 当前没有完整等价 secret / live provider test method。
|
||||
25. `src/lib/api/automation.ts` 的 scheduler / job / schedule 网关已通过 `AppServerClient.request(...)` 进入 App Server current 主链,覆盖 `automationScheduler/config/read|update|status`、`automationJob/list|read|create|update|delete|runNow|health|runHistory` 与 `automationSchedule/preview|validate`;缺少必需 result 时 fail closed,不再把旧 `get_automation_*`、`*_automation_job`、renderer mock 或 Electron legacy facade 当作前端事实源。`scripts/automation-current-smoke.mjs` 已通过真实 GUI 点击路径证明持续流程任务列表读取经 `app_server_handle_json_lines` 观察到 `automationJob/list`,且未观察到旧 automation 命令族。旧 Tauri / Electron / DevBridge automation command surface 已判为 `dead` 并从 runner、dispatcher、Electron host 白名单和 mock priority 中移除;`automationJob/runNow` 已进入 current 协议,但 App Server 自动化执行器尚未迁完,当前实现 fail closed,不回退旧 Tauri 执行器。
|
||||
26. `src/lib/api/apiKeyProvider.ts` 的 Provider list/catalog/read/create/update/delete/sort、API Key create/update/delete/next/usage/error、config import/export、UI state、连接测试与 chat test 已通过 `AppServerClient.request(...)` 进入 App Server current 主链,并直接复用 `METHOD_MODEL_PROVIDER_*`、`METHOD_MODEL_PROVIDER_KEY_*`、`METHOD_MODEL_PROVIDER_UI_STATE_*` 与 `METHOD_MODEL_PROVIDER_CONFIG_*`;缺少必需 result 时 fail closed,不再把 `get_api_key_providers`、`get_system_provider_catalog`、`get_provider_ui_state`、`set_provider_ui_state`、renderer mock 或 Electron legacy facade 当作前端事实源。`scripts/provider-settings-current-smoke.mjs` 已通过真实 GUI 点击路径证明 AI 服务商设置页和“添加模型”catalog 读取经 `app_server_handle_json_lines` 观察到 `modelProvider/list`、`modelProvider/catalog/list`,且未观察到旧 Provider 命令。单 Provider、secret 写入、UI state、导入导出和连接测试的 GUI 逐项证据仍缺,但协议、client、processor 与前端 gateway 已走 App Server current。
|
||||
27. `src/lib/dev-bridge/safeInvoke.ts` 的 Electron IPC 分支已复用命令超时 profile;`app_server_handle_json_lines` 内的 `agentSession/list` 识别为 `agent-session-list` 8 秒 profile,超时后 fail closed 并记录 `electron-ipc` trace / error,不回退 legacy desktop host、DevBridge mock 或 renderer mock。侧栏最近对话 / 归档通过 `listAgentRuntimeSessions -> agentSession/list` 读取时,底层 App Server JSON-RPC 无返回也必须 settle,不能让 UI 永久 loading。`scripts/sidebar-sessions-current-smoke.mjs` 已通过真实侧栏 GUI 读取证明 `agentSession/list` 经 `app_server_handle_json_lines` 到达 Electron-host App Server,最近列表和归档列表都能退出 loading,且未观察到 `agent_runtime_list_sessions`;P3.197 进一步要求 seeded remembered workspace(默认 `workspace-1`)不得出现在任何 `agentSession/list.params.workspaceId`,避免本地记忆项目污染最近 / 归档列表;P3.198 已把 seeded workspace 禁污染、current App Server list path、legacy list 禁止和失败 summary options 固化为 `scripts/sidebar-sessions-current-smoke.test.mjs` 静态守卫。
|
||||
28. `src/lib/api/agentRuntime/appServerSessionClient.ts`、`src/lib/api/agentRuntime/inventoryClient.ts` 与 `src/lib/api/memory.ts` 已把 `agentSession/list`、`workspaceSkillBindings/list`、`projectMemory/read` 的前端 method 引用收敛到 `packages/app-server-client/src/protocol.ts` 的 `METHOD_*` 常量;这些 current gateway 不再各自硬编码 method 字符串,避免 Electron / App Server / content-studio 等后续消费者复制出第二套协议事实源。
|
||||
29. `lime-rs/crates/app-server/src/local_data_source.rs` 的 `agentSession/list` / `agentSession/read` 数据源已覆盖 current timeline 表与真实旧 `agent_messages` message-only 会话;列表去重合并后统一排序 / limit,详情对无 timeline entries 的既有会话调用 `lime_agent` 读取真实 runtime detail。该补齐发生在 App Server JSON-RPC 后端事实源内,不新增 Electron 后端逻辑、不回退旧 Tauri、不引入 mock。
|
||||
30. `scripts/settings-all-pages-current-smoke.mjs` 已补真实设置页 broad smoke:通过 Electron-host DevBridge 进入设置页,逐项点击 15 个设置入口并覆盖 24 个页面 / 子页快照,断言无 DevBridge 断连、无 unsupported command 文案、无 safeInvoke error buffer、无 console error / 未分类 warning、无 invoke JSON-RPC error response;最新证据位于 `.lime/qc/gui-evidence/settings-current/settings-current-summary.json`,显示 `ok=true`、`tabs=15`、`pages=24`、`invokeCount=97`、`consoleErrors=0`、`unexpectedConsoleWarnings=0`、`invokeResponseErrors=0`。About 页更新检查在开发态缺少 `app-update.yml` 时不再向 console 写 updater 诊断 warning,只保留用户可见的失败状态。该证据只证明设置页首屏点击不会再因桥接 unsupported / warning 断裂,并观察到 `app_server_handle_json_lines` 中的 `workspace/default/ensure`、`agentAppInstalled/list`、`modelProvider/list`、`model/list`、`modelPreferences/list`、`modelSyncState/read`、`knowledgePack/list`、`agentSession/list`、`automationJob/list`、`projectMemory/read`、`workspace/list` 等 current method。`get_experimental_config` / `save_experimental_config` 已接到 Electron `config.json` current 事实源;同一网络证据仍包含 `get_provider_ui_state`、`get_usage_stats`、`unified_memory_stats`、MCP / browser connector / updater / voice / automation scheduler 等 Desktop compat 或 degraded 桌面壳诊断命令,所以不能把 P3.177 / P3.192 误判为“设置页全部 current 化”。
|
||||
30. `scripts/settings-all-pages-current-smoke.mjs` 已补真实设置页 broad smoke:通过 Electron-host DevBridge 进入设置页,逐项点击 15 个设置入口并覆盖 24 个页面 / 子页快照,断言无 DevBridge 断连、无 unsupported command 文案、无 safeInvoke error buffer、无 console error / 未分类 warning、无 invoke JSON-RPC error response;最新证据位于 `.lime/qc/gui-evidence/settings-current/settings-current-summary.json`,显示 `ok=true`、`tabs=15`、`pages=24`、`invokeCount=97`、`consoleErrors=0`、`unexpectedConsoleWarnings=0`、`invokeResponseErrors=0`。About 页更新检查在开发态缺少 `app-update.yml` 时不再向 console 写 updater 诊断 warning,只保留用户可见的失败状态。该证据只证明设置页首屏点击不会再因桥接 unsupported / warning 断裂,并观察到 `app_server_handle_json_lines` 中的 `workspace/default/ensure`、`agentAppInstalled/list`、`modelProvider/list`、`model/list`、`modelPreferences/list`、`modelSyncState/read`、`knowledgePack/list`、`agentSession/list`、`automationJob/list`、`projectMemory/read`、`workspace/list` 等 current method。`get_experimental_config` / `save_experimental_config` 已接到 Electron `config.json` current 事实源;该历史网络证据曾包含旧 `get_provider_ui_state`,但 Provider UI state 现已迁入 `modelProviderUiState/read|write` current 主链,旧 Provider façade 不得继续作为 Desktop compat 证据;`get_usage_stats`、`unified_memory_stats`、MCP / browser connector / updater / voice / automation scheduler 等仍是 Desktop compat 或 degraded 桌面壳诊断命令,所以不能把 P3.177 / P3.192 误判为“设置页全部 current 化”。
|
||||
31. `scripts/code-runtime-fixture-smoke.mjs` 的离线 code runtime fixture smoke 已把正向 runtime 证据迁到 App Server JSON-RPC:会话、provider/model update、自然语言 turn start、read model 轮询与 evidence export 均经 `app_server_handle_json_lines -> agentSession/start/update/turn/start/read -> evidence/export`;脚本禁止再把 `agent_runtime_create_session`、`agent_runtime_update_session`、`agent_runtime_submit_turn`、`agent_runtime_get_session`、`agent_runtime_get_thread_read` 或 `agent_runtime_export_evidence_pack` 当成完成证据。file checkpoint / diff 仍保留 `agent_runtime_list_file_checkpoints` 与 `agent_runtime_diff_file_checkpoint` compat,因为 App Server 目前没有等价 checkpoint method;该 smoke 仍是 localhost fixture 离线证据,不等同于完整 Claw live GUI streaming E2E,也不等同于 Codex 一等 `thread/*` 已实现。
|
||||
32. `scripts/claw-chat-ready-streaming-smoke.mjs` 已作为 Chromium + DevBridge(electron-host transport) Claw live streaming E2E 当前入口收紧证据边界,不等同 Playwright `_electron.launch()` / 真实 Electron renderer IPC E2E:必须显式 `--allow-live-provider` 或 `LIME_ALLOW_LIVE_PROVIDER_SMOKE=1 / LIME_REAL_API_TEST=1` 才会进入 DevBridge / Provider 阶段;进入后必须先确认 DevBridge health 的 `transport=electron-host`,否则不能把结果当作 Electron current GUI 证据。该脚本已经通过网络捕获和 summary 断言覆盖 `agentSession/turn/start`、`agentSession/turn/cancel`、`agentSession/event`、`agentSession/read`、event 后 read model 回流、无 runtime mock fallback、provider/model preference 保持与恢复 turn 可见性;本轮只补了 electron-host 与未授权 gate 守卫,尚未获得 live provider 授权运行真实 streaming。
|
||||
33. `AppSidebar` 与 `AgentChatWorkspace` 的 workspace 过滤投影已收口:本地记忆项目只保留导航上下文,不能在未验证前成为 App Server `agentSession/list.workspaceId` 过滤条件;Agent 工作台仅在 `projectSelectionSource=remembered` 且项目尚未加载为同一 ID 时暂缓 runtime workspace,显式外部项目、初始会话、话题绑定项目、默认项目和手动选择继续按 current 主路径进入 `useAgentChatUnified`。
|
||||
@@ -89,21 +89,21 @@ Electron main 是 Desktop Host bridge,不是后端适配层,也不是第二
|
||||
|
||||
## 4. Workspace / Skill / Model 矩阵
|
||||
|
||||
| 用户能力 | App Server method | Electron Host bridge | 前端 caller | 当前状态 | 备注 |
|
||||
| -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| workspace list/read/default/ensure | `workspace/*` | 通用 JSONL 可达;legacy `workspace_*` facade 仅兼容投影 | `src/lib/api/project.ts -> AppServerClient.request` | `partial` | 读 / ensure gateway 已直连 App Server method,并有契约守卫;workspace 写命令与 content 仍是 compat,仍缺真实 GUI / E2E 证据。 |
|
||||
| skill list/read | `skill/list` / `skill/read` | 通用 JSONL 可达;legacy `list_executable_skills` / `get_skill_detail` facade 仅兼容投影 | `src/lib/api/skill-execution.ts -> AppServerClient.request` | `partial` | list/read 已直连 App Server;`scripts/skills-current-smoke.mjs` 已补 slash skill preflight 真实 GUI / bridge 证据,确认 `skill/list` 返回 `result.skills` 数组且无旧 Skill 命令、`execute_skill` 或 `agentSession/turn/start`;`skill/read` GUI 触发和 `execute_skill` current 化仍缺。 |
|
||||
| workspace registered skills / skill bindings | `workspaceRegisteredSkills/list` / `workspaceSkillBindings/list` | `app_server_handle_json_lines -> workspaceRegisteredSkills/list` / `workspaceSkillBindings/list` | `capabilityDraftsApi.listRegisteredSkills` / `createInventoryClient().listWorkspaceSkillBindings` | `partial` | 前端 registered skills discovery 与 runtime binding readiness gateway 均已直连 App Server method;`scripts/workspace-skill-bindings-current-smoke.mjs` 已补真实 Skills 用户安装页 / 已保存技能面板 GUI / bridge 证据,确认 `workspaceRegisteredSkills/list` 返回有效 `skills` 数组、`workspaceSkillBindings/list` 返回有效 `bindings` 数组,且无旧 `capability_draft_list_registered_skills` / `agent_runtime_list_workspace_skill_bindings`。Capability Draft create / verify / register 与完整 Skills 工作台执行闭环仍缺。 |
|
||||
| project memory aggregate read | `projectMemory/read` | `app_server_handle_json_lines -> projectMemory/read` | `getProjectMemory` | `partial` | 聚合读已直连 App Server method;`scripts/project-memory-current-smoke.mjs` 已补真实 GUI / bridge 证据,确认 Agent 首页默认工作区记忆读取 `projectMemory/read` 且无旧 `project_memory_get` 命令;角色 / 世界观 / 大纲 CRUD 仍是 compat。 |
|
||||
| model registry/preferences/sync read | `model/list` / `modelPreferences/list` / `modelSyncState/read` | 通用 JSONL 可达;legacy `get_model_*` facade 仅兼容投影 | `src/lib/api/modelRegistry.ts -> AppServerClient.request` | `partial` | `scripts/model-registry-current-smoke.mjs` 已补设置页“添加模型”真实 GUI / bridge 证据,确认 `model/list`、`modelPreferences/list`、`modelSyncState/read` 均返回有效 result 且无旧 model 命令、模型写入 / refresh side-effect 或 Agent turn;alias/provider/tier scoped gateway 已 current 化但 GUI 证据仍缺,provider ids、favorite / refresh / live fetch 等仍是 compat 或待补 App Server method。 |
|
||||
| API key provider list/catalog | `modelProvider/*` | 通用 JSONL 可达;legacy `get_api_key_providers` / `get_system_provider_catalog` facade 仅兼容投影 | `src/lib/api/apiKeyProvider.ts -> AppServerClient.request` | `partial` | Provider 列表 / catalog 读链已直连 App Server;`scripts/provider-settings-current-smoke.mjs` 已补真实 GUI / bridge 证据,确认设置页读取 `modelProvider/list`、添加模型 catalog 读取 `modelProvider/catalog/list` 且无旧 Provider 命令;单 Provider、secret 写入、UI state、导入导出和连接测试仍是 compat。 |
|
||||
| knowledge pack list | `knowledgePack/list` | 通用 JSONL 可达;legacy `knowledge_list_packs` facade 仅兼容投影 | `src/lib/api/knowledge.ts -> AppServerClient.request` | `partial` | 知识包列表读链已直连 App Server;`scripts/knowledge-current-smoke.mjs` 已补真实 GUI / bridge 证据,确认项目资料页面读取 `knowledgePack/list` 且无旧 `knowledge_list_packs` 命令;get/import/compile/default/status/resolve/validate 仍是 compat。 |
|
||||
| Agent App installed list | `agentAppInstalled/list` | 通用 JSONL 可达;legacy `agent_app_list_installed` facade 仅兼容投影 | `src/lib/api/agentApps.ts -> AppServerClient.request` | `partial` | 已安装列表读链已直连 App Server;`scripts/agent-app/installed-current-smoke.mjs` 已补真实 GUI / bridge 证据,确认 Agent Apps 页面读取 `agentAppInstalled/list` 且无旧 `agent_app_list_installed` 命令;保存、安装、卸载和 shell side-effect 仍是 compat。 |
|
||||
| automation job list | `automationJob/list` | 通用 JSONL 可达;legacy `get_automation_jobs` facade 仅兼容投影 | `src/lib/api/automation.ts -> AppServerClient.request` | `partial` | 自动化任务列表读链已直连 App Server;`scripts/automation-current-smoke.mjs` 已补真实 GUI / bridge 证据,确认持续流程任务列表读取 `automationJob/list` 且无旧 `get_automation_jobs` 命令;scheduler/status/write/run/history 等仍是 compat。 |
|
||||
| Connect deep link resolve/open | `connectDeepLink/resolve` / `connectOpenDeepLink/resolve` | Electron protocol/open-url / 首启参数 / `second-instance` -> preload `deepLink.getCurrent/onOpenUrl` -> `app_server_handle_json_lines` | `useDeepLink -> src/lib/api/connect.ts` | `wired` | `scripts/connect-deep-link-current-smoke.mjs` 已用真实 Electron 首启参数证明 `lime://connect` 经 preload bridge 到达 `connectDeepLink/resolve` 并出现确认弹窗;`scripts/connect-open-deep-link-current-smoke.mjs` 已用真实 Electron 首启参数证明 `lime://open?kind=prompt&slug=gemini-longform-master...` 经 `connectOpenDeepLink/resolve` 到达 Agent 官网入口 banner / prompt 状态。两条证据均未观察到旧 Tauri deep-link 事件或旧 Connect 命令。 |
|
||||
| Connect API Key save / callback | `connectRelayApiKey/save` / `connectCallback/send` | Electron preload deep link -> `app_server_handle_json_lines -> connectRelayApiKey/save / connectCallback/send`;旧 `save_relay_api_key` / `send_connect_callback` 不得回流 | `useDeepLink` / `useConnectCallback -> src/lib/api/connect.ts` | `wired` | `scripts/connect-deep-link-save-current-smoke.mjs` 已用真实 Electron 首启参数、隔离 app data 与本地 registry fixture 点击“确认添加”,证明确认后弹窗关闭,`connectDeepLink/resolve`、`connectRelayApiKey/save`、`connectCallback/send` 均经 `electron-ipc` 的 `app_server_handle_json_lines` 到达 App Server,且未观察到旧 Tauri deep-link 事件或旧 Connect 命令;本证据只覆盖 callback JSON-RPC method path,外部 HTTPS webhook 网络投递仍未验证。 |
|
||||
| Agent App UI runtime | `agentAppUiRuntime/start` / `agentAppUiRuntime/status` / `agentAppUiRuntime/stop` | 通用 JSONL 可达;legacy `agent_app_start_ui_runtime` / `agent_app_get_ui_runtime_status` / `agent_app_stop_ui_runtime` facade 仅兼容投影 | `src/lib/api/agentApps.ts -> AppServerClient.request` | `wired` | protocol / schema / TS client / Rust processor / RuntimeCore helper / 前端 gateway 已成组接入 App Server current method,生产 gateway 不再 safeInvoke 旧 `agent_app_*` 或 mock;`scripts/check-app-server-client-contract.mjs` 与 `src/lib/api/agentApps.test.ts` 已守住 no legacy fallback。`scripts/agent-app/ui-runtime-current-smoke.mjs` 已证明真实前端 gateway 的 status / stop / start 经 `app_server_handle_json_lines -> agentAppUiRuntime/*` 到达 App Server,且未观察到旧 `agent_app_*_ui_runtime`;`scripts/agent-app/ui-runtime-stdio-fixture-smoke.mjs` 已用临时 app data 和 fixture installed state 证明 App Server stdio current 链路能真实 start -> `/api/bootstrap` ready -> entryUrl -> status running -> stop;`scripts/agent-app/ui-runtime-electron-fixture-smoke.mjs` 已证明真实 Electron-host GUI 能从正式侧栏进入 runtime page,发出 `agentAppUiRuntime/start`,iframe 打开 `entryUrl` 并加载“内容工厂 / 工作台状态”。该 `wired` 只针对 UI 子进程生命周期,不代表 Agent App 对话 turn 完成;后续对话仍必须复用 `agentSession/* -> RuntimeCore -> AsterBackend`。 |
|
||||
| Agent App task facade | `agentSession/start` / `agentSession/turn/start` / `agentSession/read` / `agentSession/action/respond` / `agentSession/turn/cancel` | `agent_app_runtime_*` Electron Desktop Host facade -> App Server JSON-RPC -> RuntimeCore;legacy `agent_runtime_*` 只作为负向回归扫描对象,不得作为完成证据 | `src/lib/api/agentAppRuntime.ts -> safeInvoke -> electron/hostCommands.ts` | `wired` | `electron/hostCommands.ts` 已把 `agent_app_runtime_start_task/get_task/cancel_task/submit_host_response` 投影到 `agentSession/start`、`agentSession/turn/start`、`agentSession/read`、`agentSession/action/respond` 与 `agentSession/turn/cancel`,并保留 `RuntimeOptions.hostOptions.asterChatRequest` 与 `turn_config` 镜像。`scripts/agent-app/runtime-electron-fixture-smoke.mjs` 已用真实 Electron preload bridge 和显式 external backend fixture 证明四条 facade 可进入同一 App Server RuntimeCore session;`scripts/agent-app/runtime-electron-task-fixture-smoke.mjs` 已进一步证明真实 Electron renderer bridge 可完成 start -> read blocked -> host response -> read running -> cancel -> read cancelled,summary / backend log 记录 `turnStart/actionRespond/turnCancel`,且 `RuntimeOptions.hostOptions.asterChatRequest` 与 `turn_config` 镜像抵达 external backend。`P3.216` 已补 Agent App iframe SDK Host Bridge 真实 Electron fixture,证明 iframe 内 `createLimeHostBridgeCapabilityInvoker -> lime.agent.startTask/getTask/submitHostResponse/cancelTask` 经正式 runtime page / Host Bridge / Electron IPC 进入 App Server current 主链;`P3.217` 已把 RuntimeCore `artifact.snapshot` 通过 `agentSession/read.detail.thread_read.artifacts` / `detail.artifacts` 回流到 SDK task record,并用同一 SDK fixture 断言 `artifact:created`、`evidence:recorded` 与 `contentFactoryWorkspacePatch`;`P3.218` 已把 App Server read model 的 `thread_read.tool_calls` / `tool_calls` replay 成 SDK task record 的 `task:toolCall`,真实 Electron SDK fixture summary 显示 `toolEvidence.toolEventType=task:toolCall`、`toolName=WebFetch`、`readModelStatus=completed`。该证据不使用 mock backend、不回退旧 `agent_runtime_*`,但仍不等同于 live provider 完整 Claw streaming,或真实模型执行完整 WebFetch。 |
|
||||
| 用户能力 | App Server method | Electron Host bridge | 前端 caller | 当前状态 | 备注 |
|
||||
| -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| workspace list/read/default/ensure | `workspace/*` | 通用 JSONL 可达;legacy `workspace_*` facade 仅兼容投影 | `src/lib/api/project.ts -> AppServerClient.request` | `partial` | 读 / ensure gateway 已直连 App Server method,并有契约守卫;workspace 写命令与 content 仍是 compat,仍缺真实 GUI / E2E 证据。 |
|
||||
| skill list/read | `skill/list` / `skill/read` | 通用 JSONL 可达;legacy `list_executable_skills` / `get_skill_detail` facade 仅兼容投影 | `src/lib/api/skill-execution.ts -> AppServerClient.request` | `partial` | list/read 已直连 App Server;`scripts/skills-current-smoke.mjs` 已补 slash skill preflight 真实 GUI / bridge 证据,确认 `skill/list` 返回 `result.skills` 数组且无旧 Skill 命令、`execute_skill` 或 `agentSession/turn/start`;`skill/read` GUI 触发和 `execute_skill` current 化仍缺。 |
|
||||
| workspace registered skills / skill bindings | `workspaceRegisteredSkills/list` / `workspaceSkillBindings/list` | `app_server_handle_json_lines -> workspaceRegisteredSkills/list` / `workspaceSkillBindings/list` | `capabilityDraftsApi.listRegisteredSkills` / `createInventoryClient().listWorkspaceSkillBindings` | `partial` | 前端 registered skills discovery 与 runtime binding readiness gateway 均已直连 App Server method;`scripts/workspace-skill-bindings-current-smoke.mjs` 已补真实 Skills 用户安装页 / 已保存技能面板 GUI / bridge 证据,确认 `workspaceRegisteredSkills/list` 返回有效 `skills` 数组、`workspaceSkillBindings/list` 返回有效 `bindings` 数组,且无旧 `capability_draft_list_registered_skills` / `agent_runtime_list_workspace_skill_bindings`。Capability Draft create / verify / register 与完整 Skills 工作台执行闭环仍缺。 |
|
||||
| project memory aggregate read | `projectMemory/read` | `app_server_handle_json_lines -> projectMemory/read` | `getProjectMemory` | `partial` | 聚合读已直连 App Server method;`scripts/project-memory-current-smoke.mjs` 已补真实 GUI / bridge 证据,确认 Agent 首页默认工作区记忆读取 `projectMemory/read` 且无旧 `project_memory_get` 命令;角色 / 世界观 / 大纲 CRUD 仍是 compat。 |
|
||||
| model registry/preferences/sync read | `model/list` / `modelPreferences/list` / `modelSyncState/read` / `modelProvider/fetchModels` / `modelProviderAlias/*` | 通用 JSONL 可达;legacy `get_model_*` / `fetch_provider_models_auto` facade 仅允许停留在负向守卫和历史记录 | `src/lib/api/modelRegistry.ts -> AppServerClient.request` | `partial` | `scripts/model-registry-current-smoke.mjs` 已补设置页“添加模型”真实 GUI / bridge 证据,确认 `model/list`、`modelPreferences/list`、`modelSyncState/read` 均返回有效 result 且无旧 model 命令、模型写入 / refresh side-effect 或 Agent turn;alias/provider/tier scoped gateway 与 provider live fetch 已 current 化但 GUI 逐项证据仍缺,provider ids、favorite / refresh 等仍是 compat 或待补 App Server method。 |
|
||||
| API key provider list/catalog | `modelProvider/*` / `modelProviderKey/*` / `modelProviderUiState/*` / `modelProviderConfig/*` | 通用 JSONL 可达;legacy `get_api_key_providers` / `get_system_provider_catalog` / `get_provider_ui_state` / `set_provider_ui_state` facade 已判为 `dead`,不得回流 | `src/lib/api/apiKeyProvider.ts -> AppServerClient.request` | `partial` | Provider list/catalog/read/write、API Key 写入 / 启停、UI state、导入导出、连接测试与 chat test gateway 已直连 App Server;`scripts/provider-settings-current-smoke.mjs` 已补真实 GUI / bridge 证据,确认设置页读取 `modelProvider/list`、添加模型 catalog 读取 `modelProvider/catalog/list` 且无旧 Provider 命令;单 Provider、secret 写入、UI state、导入导出和连接测试的 GUI 逐项证据仍缺。 |
|
||||
| knowledge pack list | `knowledgePack/list` | 通用 JSONL 可达;legacy `knowledge_list_packs` facade 仅兼容投影 | `src/lib/api/knowledge.ts -> AppServerClient.request` | `partial` | 知识包列表读链已直连 App Server;`scripts/knowledge-current-smoke.mjs` 已补真实 GUI / bridge 证据,确认项目资料页面读取 `knowledgePack/list` 且无旧 `knowledge_list_packs` 命令;get/import/compile/default/status/resolve/validate 仍是 compat。 |
|
||||
| Agent App installed list | `agentAppInstalled/list` | 通用 JSONL 可达;legacy `agent_app_list_installed` facade 仅兼容投影 | `src/lib/api/agentApps.ts -> AppServerClient.request` | `partial` | 已安装列表读链已直连 App Server;`scripts/agent-app/installed-current-smoke.mjs` 已补真实 GUI / bridge 证据,确认 Agent Apps 页面读取 `agentAppInstalled/list` 且无旧 `agent_app_list_installed` 命令;保存、安装、卸载和 shell side-effect 仍是 compat。 |
|
||||
| automation scheduler/job/schedule | `automationScheduler/config/read` / `automationScheduler/config/update` / `automationScheduler/status` / `automationJob/list` / `automationJob/read` / `automationJob/create` / `automationJob/update` / `automationJob/delete` / `automationJob/runNow` / `automationJob/health` / `automationJob/runHistory` / `automationSchedule/preview` / `automationSchedule/validate` | `app_server_handle_json_lines -> automation*`;旧 `get_automation_*` / `*_automation_job` Tauri、Electron facade、DevBridge dispatcher 和 mock surface 已判为 `dead`,不得回流 | `src/lib/api/automation.ts -> AppServerClient.request` | `partial` | 自动化 scheduler/job/schedule 网关已直连 App Server method,并有前端 no legacy fallback 测试;`scripts/automation-current-smoke.mjs` 已补真实 GUI / bridge 证据,确认持续流程任务列表读取 `automationJob/list` 且无旧 automation 命令族。`automationJob/runNow` 仍是 current 缺口:协议、client、前端和 processor 已接入,但 App Server 自动化执行器尚未迁完,当前 fail closed,不回退旧 Tauri 执行器;`scripts/managed-objective-automation-smoke.mjs` 也改为只走 `automationJob/create/runNow/runHistory`,遇到该执行器缺口时失败而不是调用旧命令。 |
|
||||
| Connect deep link resolve/open | `connectDeepLink/resolve` / `connectOpenDeepLink/resolve` | Electron protocol/open-url / 首启参数 / `second-instance` -> preload `deepLink.getCurrent/onOpenUrl` -> `app_server_handle_json_lines` | `useDeepLink -> src/lib/api/connect.ts` | `wired` | `scripts/connect-deep-link-current-smoke.mjs` 已用真实 Electron 首启参数证明 `lime://connect` 经 preload bridge 到达 `connectDeepLink/resolve` 并出现确认弹窗;`scripts/connect-open-deep-link-current-smoke.mjs` 已用真实 Electron 首启参数证明 `lime://open?kind=prompt&slug=gemini-longform-master...` 经 `connectOpenDeepLink/resolve` 到达 Agent 官网入口 banner / prompt 状态。两条证据均未观察到旧 Tauri deep-link 事件或旧 Connect 命令。 |
|
||||
| Connect API Key save / callback | `connectRelayApiKey/save` / `connectCallback/send` | Electron preload deep link -> `app_server_handle_json_lines -> connectRelayApiKey/save / connectCallback/send`;旧 `save_relay_api_key` / `send_connect_callback` 不得回流 | `useDeepLink` / `useConnectCallback -> src/lib/api/connect.ts` | `wired` | `scripts/connect-deep-link-save-current-smoke.mjs` 已用真实 Electron 首启参数、隔离 app data 与本地 registry fixture 点击“确认添加”,证明确认后弹窗关闭,`connectDeepLink/resolve`、`connectRelayApiKey/save`、`connectCallback/send` 均经 `electron-ipc` 的 `app_server_handle_json_lines` 到达 App Server,且未观察到旧 Tauri deep-link 事件或旧 Connect 命令;本证据只覆盖 callback JSON-RPC method path,外部 HTTPS webhook 网络投递仍未验证。 |
|
||||
| Agent App UI runtime | `agentAppUiRuntime/start` / `agentAppUiRuntime/status` / `agentAppUiRuntime/stop` | 通用 JSONL 可达;legacy `agent_app_start_ui_runtime` / `agent_app_get_ui_runtime_status` / `agent_app_stop_ui_runtime` facade 仅兼容投影 | `src/lib/api/agentApps.ts -> AppServerClient.request` | `wired` | protocol / schema / TS client / Rust processor / RuntimeCore helper / 前端 gateway 已成组接入 App Server current method,生产 gateway 不再 safeInvoke 旧 `agent_app_*` 或 mock;`scripts/check-app-server-client-contract.mjs` 与 `src/lib/api/agentApps.test.ts` 已守住 no legacy fallback。`scripts/agent-app/ui-runtime-current-smoke.mjs` 已证明真实前端 gateway 的 status / stop / start 经 `app_server_handle_json_lines -> agentAppUiRuntime/*` 到达 App Server,且未观察到旧 `agent_app_*_ui_runtime`;`scripts/agent-app/ui-runtime-stdio-fixture-smoke.mjs` 已用临时 app data 和 fixture installed state 证明 App Server stdio current 链路能真实 start -> `/api/bootstrap` ready -> entryUrl -> status running -> stop;`scripts/agent-app/ui-runtime-electron-fixture-smoke.mjs` 已证明真实 Electron-host GUI 能从正式侧栏进入 runtime page,发出 `agentAppUiRuntime/start`,iframe 打开 `entryUrl` 并加载“内容工厂 / 工作台状态”。该 `wired` 只针对 UI 子进程生命周期,不代表 Agent App 对话 turn 完成;后续对话仍必须复用 `agentSession/* -> RuntimeCore -> AsterBackend`。 |
|
||||
| Agent App task facade | `agentSession/start` / `agentSession/turn/start` / `agentSession/read` / `agentSession/action/respond` / `agentSession/turn/cancel` | `agent_app_runtime_*` Electron Desktop Host facade -> App Server JSON-RPC -> RuntimeCore;legacy `agent_runtime_*` 只作为负向回归扫描对象,不得作为完成证据 | `src/lib/api/agentAppRuntime.ts -> safeInvoke -> electron/hostCommands.ts` | `wired` | `electron/hostCommands.ts` 已把 `agent_app_runtime_start_task/get_task/cancel_task/submit_host_response` 投影到 `agentSession/start`、`agentSession/turn/start`、`agentSession/read`、`agentSession/action/respond` 与 `agentSession/turn/cancel`,并保留 `RuntimeOptions.hostOptions.asterChatRequest` 与 `turn_config` 镜像。`scripts/agent-app/runtime-electron-fixture-smoke.mjs` 已用真实 Electron preload bridge 和显式 external backend fixture 证明四条 facade 可进入同一 App Server RuntimeCore session;`scripts/agent-app/runtime-electron-task-fixture-smoke.mjs` 已进一步证明真实 Electron renderer bridge 可完成 start -> read blocked -> host response -> read running -> cancel -> read cancelled,summary / backend log 记录 `turnStart/actionRespond/turnCancel`,且 `RuntimeOptions.hostOptions.asterChatRequest` 与 `turn_config` 镜像抵达 external backend。`P3.216` 已补 Agent App iframe SDK Host Bridge 真实 Electron fixture,证明 iframe 内 `createLimeHostBridgeCapabilityInvoker -> lime.agent.startTask/getTask/submitHostResponse/cancelTask` 经正式 runtime page / Host Bridge / Electron IPC 进入 App Server current 主链;`P3.217` 已把 RuntimeCore `artifact.snapshot` 通过 `agentSession/read.detail.thread_read.artifacts` / `detail.artifacts` 回流到 SDK task record,并用同一 SDK fixture 断言 `artifact:created`、`evidence:recorded` 与 `contentFactoryWorkspacePatch`;`P3.218` 已把 App Server read model 的 `thread_read.tool_calls` / `tool_calls` replay 成 SDK task record 的 `task:toolCall`,真实 Electron SDK fixture summary 显示 `toolEvidence.toolEventType=task:toolCall`、`toolName=WebFetch`、`readModelStatus=completed`。该证据不使用 mock backend、不回退旧 `agent_runtime_*`,但仍不等同于 live provider 完整 Claw streaming,或真实模型执行完整 WebFetch。 |
|
||||
|
||||
## 5. 第一竖切交付标准
|
||||
|
||||
@@ -123,7 +123,7 @@ Electron main 是 Desktop Host bridge,不是后端适配层,也不是第二
|
||||
8. GUI 证据以 Electron 为准:`npm run smoke:electron` 已证明 Electron renderer + Desktop Host bridge + App Server sidecar initialize + Claw 工作台 shell/composer ready 可用;Rust/TS 单测不能单独证明可交付。
|
||||
9. 第一竖切已有前端业务闭环测试覆盖 App Server turn notification 触发 read model refresh;尚需补真实 GUI 发送 / event refresh / thread read refresh 的业务 E2E,不能把 initialize smoke 或 hook 测试误判成完整聊天闭环。
|
||||
10. `agent_runtime_submit_turn` / `agent_runtime_interrupt_turn` / `agent_runtime_respond_action`、desktop-host mock、mock-only 测试或 legacy facade 投影只能作为负向回归扫描对象,不能作为 Agent turn lifecycle 的完成证据。
|
||||
11. 完整 Claw streaming GUI E2E 必须显式传入 `--allow-live-provider` 或 `LIME_ALLOW_LIVE_PROVIDER_SMOKE=1`;未授权时 smoke 必须在 `wait-health`、`prepare-runtime`、`aster_agent_init`、`get_api_key_providers`、`test_api_key_provider_chat` 之前 fail closed,不能用 mock、无模型探针或 Provider 自动探测替代真实业务闭环。
|
||||
11. 完整 Claw streaming GUI E2E 必须显式传入 `--allow-live-provider` 或 `LIME_ALLOW_LIVE_PROVIDER_SMOKE=1`;未授权时 smoke 必须在 `wait-health`、`prepare-runtime`、`aster_agent_init`、`modelProvider/list`、`modelProvider/testChat` 之前 fail closed,不能用 mock、无模型探针或 Provider 自动探测替代真实业务闭环。
|
||||
12. 离线 code runtime fixture smoke 可以作为 localhost fixture 下的 App Server JSON-RPC runtime 证据,但必须经 `agentSession/turn/start`、`agentSession/read` 与 `evidence/export`;file checkpoint compat 只能标记为剩余缺口,不能把旧 `agent_runtime_*` runtime facade 重新算作第一竖切完成证据。
|
||||
13. 用户可见发送态必须能由 App Server current `turn.completed` 终态收口;前端收到 `turn.completed` 后不能继续等待 `turn.final_done` 或长 timeout。若 `turn.completed` 携带 assistant 正文则直接完成;若正文为空但已有工具 / 产物等真实完成信号则软完成;若正文为空且没有真实完成信号则立即落失败态并释放输入框。`turn.final_done` 只能作为兼容 / 补充终态,不得成为 current GUI 退出发送态的唯一条件。
|
||||
14. 从本地存储恢复出的 session id 在发送前必须先被 App Server `agentSession/read` 确认存在且属于当前 workspace;确认失败时只能丢弃本地快照并创建新的 App Server session,不能继续轮询已不存在的 session 或向 stale session 提交 turn。
|
||||
@@ -164,7 +164,7 @@ Electron main 是 Desktop Host bridge,不是后端适配层,也不是第二
|
||||
30. `P3.217`:Agent App SDK artifact / evidence read model 回流已补真实 Electron fixture 证据。App Server `agentSession/read` 现在把 RuntimeCore artifact summaries 同步暴露到 `detail.thread_read.artifacts` 与 `detail.artifacts`,SDK task record 可从 current read model replay `artifact:created` 与 `evidence:recorded`;summary 位于 `.lime/qc/gui-evidence/agent-app-runtime-electron-sdk-fixture/agent-app-runtime-electron-sdk-fixture-summary.json`,显示 `artifactEvidence.artifactEventType=artifact:created`、`artifactEvidence.evidenceEventType=evidence:recorded`、`resultArtifactCount=1`。下一刀不再打磨 fixture lifecycle,应转向完整 tool runtime 执行证据或 live provider 授权后的 Claw streaming GUI E2E。
|
||||
31. `P3.218`:Agent App SDK tool call read model replay 已补真实 Electron fixture 证据。`AgentRuntimeCapabilityHost` 现在从 App Server `agentSession/read` detail 的 `thread_read.tool_calls` / `tool_calls` 读取工具事实,并 replay 为 SDK task record 的 `task:toolCall`;summary 位于 `.lime/qc/gui-evidence/agent-app-runtime-electron-sdk-fixture/agent-app-runtime-electron-sdk-fixture-summary.json`,显示 `toolEvidence.toolEventType=task:toolCall`、`toolEvidence.toolName=WebFetch`、`toolEvidence.outputPreview=fetched https://example.com/agent-app-sdk-fixture`、`toolEvidence.readModelStatus=completed`、`toolEvidence.success=true`。下一刀应转向 live provider 授权后的完整 Claw streaming GUI E2E,或把真实 tool runtime 执行从 fixture evidence 扩到 live turn 证据。
|
||||
32. `P3.219`:P3.218 后的默认 Electron GUI smoke 已通过,`npm run verify:gui-smoke` 覆盖 renderer smoke build、Electron main/preload build、`packages/app-server-client` build、Electron host typecheck、App Server sidecar build 与 `scripts/electron/smoke.mjs`,输出确认 `app-server initialized protocol=appserver.v0 version=1.59.0` 与 `claw workbench shell ready`。该证据只证明默认 Electron GUI 主路径仍可启动,不等同 live provider 完整 Claw streaming、真实发送、tool runtime live 执行或 `agentSession/event -> agentSession/read` 业务刷新。
|
||||
33. `P3.220`:未授权 Claw streaming smoke gate 已回归验证,`npm run smoke:claw-chat-ready-streaming -- --timeout-ms 30000` 按预期在 live provider gate 快速失败,并提示必须显式传入 `--allow-live-provider` 或设置 `LIME_ALLOW_LIVE_PROVIDER_SMOKE=1 / LIME_REAL_API_TEST=1`;该运行没有进入 `wait-health`、DevBridge health、`aster_agent_init`、`get_api_key_providers` 或 `test_api_key_provider_chat`。完整 Claw streaming GUI E2E 仍等待 live provider 授权,不能用 mock、无模型探针或 external backend fixture 代替。
|
||||
33. `P3.220`:未授权 Claw streaming smoke gate 已回归验证,`npm run smoke:claw-chat-ready-streaming -- --timeout-ms 30000` 按预期在 live provider gate 快速失败,并提示必须显式传入 `--allow-live-provider` 或设置 `LIME_ALLOW_LIVE_PROVIDER_SMOKE=1 / LIME_REAL_API_TEST=1`;该运行没有进入 `wait-health`、DevBridge health、`aster_agent_init`、`modelProvider/list` 或 `modelProvider/testChat`。完整 Claw streaming GUI E2E 仍等待 live provider 授权,不能用 mock、无模型探针或 external backend fixture 代替。
|
||||
34. `P3.221` / `P3.237`:Agent session history Electron fixture 已补真实 current 证据。`scripts/electron/session-history-fixture-smoke.mjs` 使用真实 Electron preload `app_server_handle_json_lines` 与 `APP_SERVER_BACKEND_MODE=unavailable`,执行 `initialize -> agentSession/start -> agentSession/read -> agentSession/update(title/provider/model) -> agentSession/update(archived=true) -> agentSession/read -> agentSession/list`,summary 位于 `.lime/qc/gui-evidence/agent-session-history-electron-fixture/agent-session-history-electron-fixture-summary.json`。P3.221 证明两次 `read.detail` 都包含 history hydrate 所需的 `turns/items/queued_turns/thread_read` 且 list 可读回更新标题;P3.237 进一步锁住内存 session 的 `archived=true` 必须 fail closed,错误文案包含 `agentSession/update archived is only supported for persisted current timeline sessions`,并确认 `listedSessionArchivedAt == null`。该证据不触发 `agentSession/turn/start`,不调用模型后端,不使用 legacy `agent_runtime_*`、mock backend 或 renderer mock fallback;persisted current timeline archive / unarchive / sidecar restart readback 已由 P3.246 补真实闭环。
|
||||
35. `P3.239`:侧栏归档 / 恢复入口已补 current boundary 静态守卫。`src/components/AppSidebar.current-boundary.test.ts` 锁住 `handleToggleSessionArchive` 只能经 `updateAgentRuntimeSession({ session_id, archived })`,session gateway update / delete 只能委托 `createAppServerSessionClient()`,App Server gateway 必须使用 `agentSession/list/read/update` 事实源;该守卫只防回流,不证明 persisted timeline archive / unarchive 成功。
|
||||
36. `P3.240`:session gateway 归档请求投影已补 current boundary 守卫。`src/lib/api/agentRuntime/sessionClient.current-boundary.test.ts` 锁住 `createSessionClient(...)` 对归档、恢复和兼容 delete 的请求必须投影为 `appServerClient.updateSession({ sessionId, archived })`,并确认注入的 legacy `invokeCommand` 不被调用;该守卫补齐侧栏 UI 与 App Server session gateway 之间的中间层证据,仍不证明 persisted timeline archive / unarchive 成功。
|
||||
@@ -185,7 +185,7 @@ Electron main 是 Desktop Host bridge,不是后端适配层,也不是第二
|
||||
51. `P3.265`:P3.262 的 live WebSearch/WebFetch 工具事实门禁已收紧为 turn-scoped。`scripts/claw-chat-ready-streaming-smoke.mjs` 现在从 App Server `agentSession/read` 投影中提取 `turn_id` / `turnId`,并只接受本次 `liveWebTurnId` 下完成态 `WebSearch` 与 `WebFetch`;`requiredForTurn` / `allRequiredCompletedForTurn` 是 verdict 条件,历史 session 中其他 turn 的工具调用不能让本次 live 工具 turn 误通过。`scripts/lib/live-provider-smoke-gate.test.mjs` 已补静态守卫。本证据仍不代表真实 Provider 已运行,下一刀仍是显式授权后跑 live streaming / WebSearch-WebFetch 实际联网 E2E。
|
||||
52. `P3.268`:P3.265 的 turn-scoped live WebSearch/WebFetch 判定已从 smoke 内联逻辑抽到 `scripts/lib/claw-chat-live-web-tool-evidence.mjs`,并补 `scripts/lib/claw-chat-live-web-tool-evidence.test.mjs` 行为单测。测试明确证明旧 turn 的完成态工具调用不能让本次 `liveWebTurnId` 通过,且支持 App Server read model 中 `turn_id` / `turnId` 与 inherited turn scope。真实 live Provider 仍未运行,下一刀仍需授权后跑完整联网 E2E。
|
||||
53. `P3.269`:live WebSearch/WebFetch helper 已补 App Server JSON-RPC / read model envelope 覆盖。`collectToolCallsFromValue(...)` 现在会递归 `result/detail/session/session_detail/sessionDetail`,行为测试锁住 `agentSession/read` 常见 `result.detail.thread_read.tool_calls` 形状,避免授权后因只传入 JSON-RPC response / read result 外壳而漏掉工具事实。真实 live Provider 仍未运行,下一刀仍需授权后跑完整联网 E2E。
|
||||
54. `P3.271`:live Provider gate 已从根层脚本扫描扩展为递归扫描 `scripts/**/*.mjs`。`scripts/lib/live-provider-smoke-gate.test.mjs` 现在覆盖子目录 smoke,排除测试文件、`scripts/lib/**`、`check-*` 与测试夹具路径;递归扫描下当前脚本仍无未授权 `agent_runtime_submit_turn` / `test_api_key_provider_chat` / `knowledge_compile_pack` 回流。真实 live Provider 仍未运行,下一刀仍需授权后跑完整联网 E2E。
|
||||
54. `P3.271`:live Provider gate 已从根层脚本扫描扩展为递归扫描 `scripts/**/*.mjs`。`scripts/lib/live-provider-smoke-gate.test.mjs` 现在覆盖子目录 smoke,排除测试文件、`scripts/lib/**`、`check-*` 与测试夹具路径;递归扫描下当前脚本仍无未授权 `agent_runtime_submit_turn` / App Server `modelProvider/testChat` / `knowledge_compile_pack` 回流。真实 live Provider 仍未运行,下一刀仍需授权后跑完整联网 E2E。
|
||||
55. `P3.272`:live WebSearch/WebFetch turn 已补自身 provider/model 与 fast routing verdict。`scripts/claw-chat-ready-streaming-smoke.mjs` 现在要求 Web 工具 turn 的 `turn_config` 同样命中 explicit provider/model,且 `metadata.harness.fast_response_routing` 未开启;这些断言已纳入最终 pass 条件。真实 live Provider 仍未运行,下一刀仍需授权后跑完整联网 E2E。
|
||||
56. `P3.273`:`claw-chat-ready-streaming` 的 final `threadRead` summary 已保留 App Server current `detail.thread_read`。`appServerThreadReadFromSessionRead(...)` 现在优先展开 `detail.thread_read/threadRead`,避免把工具事实、artifact 和 routing 证据降级成由 turns 重建的最小投影。真实 live Provider 仍未运行,下一刀仍需授权后跑完整联网 E2E。
|
||||
57. `P3.274`:live WebSearch/WebFetch verdict 已补工具输出非空证据。`scripts/lib/claw-chat-live-web-tool-evidence.mjs` 现在计算 `outputPresent` / `allRequiredOutputPresentForTurn`,`scripts/claw-chat-ready-streaming-smoke.mjs` 要求本次 live Web turn 下 WebSearch/WebFetch 均 completed 且有输出预览。真实 live Provider 仍未运行,下一刀仍需授权后跑完整联网 E2E。
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
- 2026-05-26:复跑 P6 非 live continuation 守卫与命令边界:`env -u LIME_ALLOW_LIVE_PROVIDER_SMOKE -u LIME_REAL_API_TEST npx vitest run "scripts/lib/managed-objective-continuation-smoke-core.test.mjs" "scripts/lib/managed-objective-guardrails-core.test.mjs"` 13 条通过,`cargo test --manifest-path "lime-rs/Cargo.toml" objective_continuation --lib` 14 条通过,`npm run test:contracts` 通过。该组证据覆盖自动续跑 allow / skip / budget_limited / needs_input / completed 停止、模型工具面禁用 objective 命令,以及 current 命令契约同步;仍不声称真实 Provider 多轮 smoke 已完成。
|
||||
- 2026-05-26:P6 continuation smoke 默认改为 localhost OpenAI-compatible fixture:`scripts/managed-objective-continuation-smoke.mjs` 未显式授权时不再使用 `LIME_AGENT_QC_PROVIDER / LIME_E2E_PROVIDER / LIME_DEFAULT_PROVIDER` 选择真实 Provider,显式传 `--provider-preference` / `--model-preference` 但缺少 `--allow-live-provider` 会立即失败;首轮 `agent_runtime_submit_turn` 带 Direct `providerConfig` 指向 fixture。自动续跑会继承当前 provider/model/base_url,避免第二轮退回真实 Provider 池,同时不把 `api_key` 写入持久化队列 payload。该证据把 P6 多轮 smoke 从“必须消耗 live Provider”收敛为默认离线可验证;是否额外要求 live Provider 证据仍由发布验收口径单独决定。
|
||||
- 2026-05-26:第 9 节补齐 automation `AgentTurn` owner-session evidence 回归:新增拆分后的 `automation_owner_session_evidence_tests.rs`,证明 `Skill/Intelligent` automation due job 的 finish metadata 会保留 `harness.managed_objective`、workspace skill runtime enable、`continuation_policy.dispatch=agent_runtime_submit_turn` 与 runtime `session_id`,并可通过 `AgentRunDao::list_runs_by_session` 查询到 automation owner run。验证已覆盖 `cargo test --manifest-path "lime-rs/Cargo.toml" automation_owner_session_evidence --lib`,不触发真实 Provider。
|
||||
- 2026-05-26:第 9 节补齐默认离线 automation owner smoke 入口:新增 `npm run smoke:managed-objective-automation`,脚本启动 localhost OpenAI-compatible fixture,先把 Aster 全局 Provider 临时切到 fixture,再创建 enabled automation job 和未来一次性 schedule,避免创建时把 managed objective 同步成 paused,再手动 `run_automation_job_now`;smoke 会轮询 runtime session 直到 fixture 收到 `/v1/chat/completions`,再导出 `agent_runtime_export_evidence_pack`,断言 automation run history、managed objective owner metadata 和 evidence pack owner audit 都可回看。该 smoke 默认不读取 Deepseek 等真实 Provider 环境偏好,也不伪造未验证 workspace skill runtime enable;验证已覆盖 `env -u LIME_ALLOW_LIVE_PROVIDER_SMOKE -u LIME_REAL_API_TEST npm run smoke:managed-objective-automation -- --timeout-ms 180000 --no-write`,fixture chat completion 请求数为 1,不触发真实 Provider。
|
||||
- 2026-05-26:第 9 节补齐默认离线 automation owner smoke 入口:新增 `npm run smoke:managed-objective-automation`,脚本启动 localhost OpenAI-compatible fixture,先把 Aster 全局 Provider 临时切到 fixture,再创建 enabled automation job 和未来一次性 schedule,避免创建时把 managed objective 同步成 paused。2026-06-08 自动化命令迁移后,该 smoke 已改为只通过 App Server `automationJob/create` / `automationJob/runNow` / `automationJob/runHistory` 验证;由于 App Server 自动化执行器尚未迁完,当前遇到 `automationJob/runNow` 执行器缺口时 fail closed,不能再回退旧 Tauri 自动化命令。后续完成 App Server 执行器迁移后,再恢复 owner run history、managed objective owner metadata 与 evidence pack owner audit 的离线 fixture 通过证据。
|
||||
|
||||
## 2. P0:文档与边界落盘
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
24. `agent_runtime_export_evidence_pack` 返回值已透出 `completionAuditSummary`,前端 normalizer 和 Harness 面板会展示 evidence-based decision、owner / ToolCall / artifact 计数与 blocking reasons,让 completion audit 不再只停留在落盘文件。
|
||||
25. Agent envelope presentation contract 已能消费 `completionAuditSummary`:只有 `completed` 且 automation owner / Workspace Skill ToolCall / artifact-or-timeline evidence 三项齐全时才进入 `evidence_ready`,`verifying` 或缺证据不会误报为可固化。
|
||||
26. Workspace 已注册能力面板已预留 `completionAuditSummariesByDirectory` 注入边界:当某个 skill 的 audit summary 为 evidence-based `completed` 时,“转成 Agent 草案”入口会复用现有 Managed Job 草案创建链;未 completed 或缺证据时仍禁用。
|
||||
27. Workspace 已注册能力面板已补“审计最近运行”入口:对匹配 Managed Job 复用 `get_automation_run_history` 找到最近 automation run 的 session,再调用 `agent_runtime_export_evidence_pack` 获取 `completionAuditSummary` 并回填对应 skill,不新增查询命令或平行 evidence。
|
||||
27. Workspace 已注册能力面板已补“审计最近运行”入口:对匹配 Managed Job 复用 `getAutomationRunHistory()` 当前网关,经 App Server `automationJob/runHistory` 找到最近 automation run 的 session,再调用 evidence export 当前网关获取 `completionAuditSummary` 并回填对应 skill,不新增查询命令或平行 evidence。
|
||||
28. Agent envelope 草案摘要已补齐 `Memory / Widget / Permission / Schedule / Evidence / Runbook` 六块组成:Memory 引用 verification report 与运行修正,Widget 展示 Managed Job 状态、产物、审计结论和下一步动作。
|
||||
29. Agent card / sharing 已采用派生形态:`workspace-local/<skill-directory>` 由已注册 Skill、Managed Job 和 completion audit 派生;共享范围先限制在当前 workspace / team,不进入 public Marketplace,也不新增 Agent card 存储表。
|
||||
30. Workspace/team sharing discovery 边界已显式展示:同 workspace 成员通过 registered skill discovery 发现 `.agents/skills/<skill-directory>`,复用同一 Managed Job / evidence 事实源,不新增分享命令或 Marketplace。
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
24. Evidence export 返回值、前端 API normalizer 与 Harness 面板已接入 `completionAuditSummary`:导出问题证据包后可直接在 UI 看到 evidence-based decision 与阻塞原因。
|
||||
25. Agent envelope presentation contract 已接入 completion audit gate:`completed` 且必要 evidence 齐全才进入 `evidence_ready`;非 completed audit 仍保持缺证据态,避免把 verifying 误报为可固化。
|
||||
26. Workspace 已注册能力面板已补 evidence-gated Agent envelope 入口边界:按 skill directory 注入 completion audit summary 后,只有 completed + 必要 evidence 齐全才启用“转成 Agent 草案”,并复用现有 Managed Job 草案创建链。
|
||||
27. Workspace 已注册能力面板已补最近运行审计入口:从匹配 Managed Job 调用既有 `get_automation_run_history` 与 `agent_runtime_export_evidence_pack`,把最近 automation run 的 evidence summary 回填到对应 skill 的 Agent envelope gate。
|
||||
27. Workspace 已注册能力面板已补最近运行审计入口:从匹配 Managed Job 调用 `getAutomationRunHistory()` 当前网关与 evidence export 当前网关,底层经 App Server `automationJob/runHistory` 和 `evidence/export` 把最近 automation run 的 evidence summary 回填到对应 skill 的 Agent envelope gate。
|
||||
28. Agent envelope 草案摘要已补完整组成:Runbook、Memory、Widget、Permission、Schedule、Evidence 均有 presentation 字段与 Workspace 展示,仍只作为产品组合面,不新增执行实体。
|
||||
29. Agent card 与 sharing 已收敛为派生展示:Agent card id 使用 `workspace-local/<skill-directory>`,事实源来自 registered skill、Managed Job 和 completion audit;sharing 先限定 workspace / team 范围,不做 public Marketplace。
|
||||
30. Workspace/team sharing discovery 已明确:团队成员通过同一 workspace root 的 registered skill discovery 发现 `.agents/skills/<skill-directory>`,并复用同一 Managed Job / evidence 事实源。
|
||||
|
||||
Generated
+28
-105
@@ -161,7 +161,7 @@ checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||
|
||||
[[package]]
|
||||
name = "app-server"
|
||||
version = "1.60.0"
|
||||
version = "1.61.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-server-protocol",
|
||||
@@ -169,6 +169,8 @@ dependencies = [
|
||||
"aster-core",
|
||||
"async-trait",
|
||||
"chrono",
|
||||
"chrono-tz",
|
||||
"cron",
|
||||
"lime-agent",
|
||||
"lime-core",
|
||||
"lime-knowledge",
|
||||
@@ -186,7 +188,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "app-server-agent-backend"
|
||||
version = "1.60.0"
|
||||
version = "1.61.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"aster-core",
|
||||
@@ -202,7 +204,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "app-server-client"
|
||||
version = "1.60.0"
|
||||
version = "1.61.0"
|
||||
dependencies = [
|
||||
"app-server-protocol",
|
||||
"app-server-transport",
|
||||
@@ -213,7 +215,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "app-server-daemon"
|
||||
version = "1.60.0"
|
||||
version = "1.61.0"
|
||||
dependencies = [
|
||||
"app-server-protocol",
|
||||
"app-server-transport",
|
||||
@@ -225,7 +227,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "app-server-protocol"
|
||||
version = "1.60.0"
|
||||
version = "1.61.0"
|
||||
dependencies = [
|
||||
"schemars",
|
||||
"serde",
|
||||
@@ -234,7 +236,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "app-server-test-client"
|
||||
version = "1.60.0"
|
||||
version = "1.61.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-server-client",
|
||||
@@ -245,7 +247,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "app-server-transport"
|
||||
version = "1.60.0"
|
||||
version = "1.61.0"
|
||||
dependencies = [
|
||||
"app-server-protocol",
|
||||
"serde_json",
|
||||
@@ -1115,12 +1117,6 @@ dependencies = [
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
@@ -1568,35 +1564,6 @@ dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cocoa"
|
||||
version = "0.26.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad36507aeb7e16159dfe68db81ccc27571c3ccd4b76fb2fb72fc59e7a4b1b64c"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"block",
|
||||
"cocoa-foundation",
|
||||
"core-foundation 0.10.1",
|
||||
"core-graphics 0.24.0",
|
||||
"foreign-types 0.5.0",
|
||||
"libc",
|
||||
"objc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cocoa-foundation"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81411967c50ee9a1fc11365f8c585f863a22a9697c89239c452292c40ba79b0d"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"block",
|
||||
"core-foundation 0.10.1",
|
||||
"core-graphics-types 0.2.0",
|
||||
"objc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.4"
|
||||
@@ -1763,20 +1730,7 @@ checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation 0.9.4",
|
||||
"core-graphics-types 0.1.3",
|
||||
"foreign-types 0.5.0",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-graphics"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"core-foundation 0.10.1",
|
||||
"core-graphics-types 0.2.0",
|
||||
"core-graphics-types",
|
||||
"foreign-types 0.5.0",
|
||||
"libc",
|
||||
]
|
||||
@@ -1792,17 +1746,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-graphics-types"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"core-foundation 0.10.1",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "coreaudio-rs"
|
||||
version = "0.11.3"
|
||||
@@ -2390,7 +2333,7 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0087a01fc8591217447d28005379fb5a183683cc83f0a4707af28cc6603f70fb"
|
||||
dependencies = [
|
||||
"core-graphics 0.23.2",
|
||||
"core-graphics",
|
||||
"foreign-types-shared 0.3.1",
|
||||
"icrate",
|
||||
"libc",
|
||||
@@ -3963,7 +3906,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lime-agent"
|
||||
version = "1.60.0"
|
||||
version = "1.61.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"aster-core",
|
||||
@@ -3992,7 +3935,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lime-browser-runtime"
|
||||
version = "1.60.0"
|
||||
version = "1.61.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"futures",
|
||||
@@ -4009,7 +3952,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lime-cli"
|
||||
version = "1.60.0"
|
||||
version = "1.61.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"lime-core",
|
||||
@@ -4021,7 +3964,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lime-config"
|
||||
version = "1.60.0"
|
||||
version = "1.61.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"lime-core",
|
||||
@@ -4037,7 +3980,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lime-core"
|
||||
version = "1.60.0"
|
||||
version = "1.61.0"
|
||||
dependencies = [
|
||||
"aster-models",
|
||||
"async-trait",
|
||||
@@ -4093,7 +4036,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lime-gateway"
|
||||
version = "1.60.0"
|
||||
version = "1.61.0"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"axum 0.7.9",
|
||||
@@ -4123,7 +4066,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lime-infra"
|
||||
version = "1.60.0"
|
||||
version = "1.61.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"dashmap 5.5.3",
|
||||
@@ -4143,7 +4086,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lime-knowledge"
|
||||
version = "1.60.0"
|
||||
version = "1.61.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"hex",
|
||||
@@ -4156,7 +4099,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lime-mcp"
|
||||
version = "1.60.0"
|
||||
version = "1.61.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"dirs 5.0.1",
|
||||
@@ -4172,7 +4115,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lime-media-runtime"
|
||||
version = "1.60.0"
|
||||
version = "1.61.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"base64 0.22.1",
|
||||
@@ -4204,7 +4147,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lime-processor"
|
||||
version = "1.60.0"
|
||||
version = "1.61.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"lime-core",
|
||||
@@ -4223,7 +4166,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lime-providers"
|
||||
version = "1.60.0"
|
||||
version = "1.61.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-stream",
|
||||
@@ -4278,7 +4221,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lime-server"
|
||||
version = "1.60.0"
|
||||
version = "1.61.0"
|
||||
dependencies = [
|
||||
"aster-core",
|
||||
"async-stream",
|
||||
@@ -4322,7 +4265,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lime-server-utils"
|
||||
version = "1.60.0"
|
||||
version = "1.61.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"futures",
|
||||
@@ -4337,14 +4280,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lime-services"
|
||||
version = "1.60.0"
|
||||
version = "1.61.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"aster-core",
|
||||
"async-trait",
|
||||
"base64 0.22.1",
|
||||
"chrono",
|
||||
"cocoa",
|
||||
"dashmap 5.5.3",
|
||||
"dirs 5.0.1",
|
||||
"futures",
|
||||
@@ -4353,7 +4295,6 @@ dependencies = [
|
||||
"lime-core",
|
||||
"lime-providers",
|
||||
"md5",
|
||||
"objc",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"proptest",
|
||||
@@ -4382,7 +4323,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lime-skills"
|
||||
version = "1.60.0"
|
||||
version = "1.61.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"dirs 5.0.1",
|
||||
@@ -4400,7 +4341,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lime-websocket"
|
||||
version = "1.60.0"
|
||||
version = "1.61.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -4503,15 +4444,6 @@ version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "670fdfda89751bc4a84ac13eaa63e205cf0fd22b4c9a5fbfa085b63c1f1d3a30"
|
||||
|
||||
[[package]]
|
||||
name = "malloc_buf"
|
||||
version = "0.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markup5ever"
|
||||
version = "0.12.1"
|
||||
@@ -5028,15 +4960,6 @@ dependencies = [
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
|
||||
dependencies = [
|
||||
"malloc_buf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc-sys"
|
||||
version = "0.3.5"
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ exclude = [
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
version = "1.60.0"
|
||||
version = "1.61.0"
|
||||
edition = "2021"
|
||||
authors = ["coso"]
|
||||
repository = "https://github.com/aiclientproxy/lime"
|
||||
|
||||
@@ -14,7 +14,25 @@ pub use app_server_protocol::AgentSessionTurnStartParams;
|
||||
pub use app_server_protocol::AppServerMethodKind;
|
||||
pub use app_server_protocol::AppServerMethodSpec;
|
||||
pub use app_server_protocol::ArtifactReadParams;
|
||||
pub use app_server_protocol::AutomationJobCreateParams;
|
||||
pub use app_server_protocol::AutomationJobDeleteResponse;
|
||||
pub use app_server_protocol::AutomationJobHealthParams;
|
||||
pub use app_server_protocol::AutomationJobHealthResponse;
|
||||
pub use app_server_protocol::AutomationJobIdParams;
|
||||
pub use app_server_protocol::AutomationJobListResponse;
|
||||
pub use app_server_protocol::AutomationJobReadResponse;
|
||||
pub use app_server_protocol::AutomationJobRunHistoryParams;
|
||||
pub use app_server_protocol::AutomationJobRunHistoryResponse;
|
||||
pub use app_server_protocol::AutomationJobRunNowResponse;
|
||||
pub use app_server_protocol::AutomationJobUpdateParams;
|
||||
pub use app_server_protocol::AutomationJobWriteResponse;
|
||||
pub use app_server_protocol::AutomationScheduleParams;
|
||||
pub use app_server_protocol::AutomationSchedulePreviewResponse;
|
||||
pub use app_server_protocol::AutomationScheduleValidateResponse;
|
||||
pub use app_server_protocol::AutomationSchedulerConfigReadResponse;
|
||||
pub use app_server_protocol::AutomationSchedulerConfigUpdateParams;
|
||||
pub use app_server_protocol::AutomationSchedulerConfigUpdateResponse;
|
||||
pub use app_server_protocol::AutomationSchedulerStatusResponse;
|
||||
pub use app_server_protocol::CapabilityListParams;
|
||||
pub use app_server_protocol::EvidenceExportParams;
|
||||
pub use app_server_protocol::FileSystemDirectoryListing;
|
||||
@@ -30,6 +48,11 @@ use app_server_protocol::JsonRpcRequest;
|
||||
use app_server_protocol::JsonRpcResponse;
|
||||
pub use app_server_protocol::KnowledgeListPacksParams;
|
||||
pub use app_server_protocol::KnowledgeListPacksResponse;
|
||||
pub use app_server_protocol::McpPromptListResponse;
|
||||
pub use app_server_protocol::McpResourceListResponse;
|
||||
pub use app_server_protocol::McpServerListResponse;
|
||||
pub use app_server_protocol::McpServerStatusListResponse;
|
||||
pub use app_server_protocol::McpToolListResponse;
|
||||
pub use app_server_protocol::ModelListParams;
|
||||
pub use app_server_protocol::ModelProviderAliasReadParams;
|
||||
pub use app_server_protocol::ProjectMemoryReadParams;
|
||||
@@ -62,7 +85,19 @@ pub use app_server_protocol::METHOD_AGENT_SESSION_START;
|
||||
pub use app_server_protocol::METHOD_AGENT_SESSION_TURN_CANCEL;
|
||||
pub use app_server_protocol::METHOD_AGENT_SESSION_TURN_START;
|
||||
pub use app_server_protocol::METHOD_ARTIFACT_READ;
|
||||
pub use app_server_protocol::METHOD_AUTOMATION_JOB_CREATE;
|
||||
pub use app_server_protocol::METHOD_AUTOMATION_JOB_DELETE;
|
||||
pub use app_server_protocol::METHOD_AUTOMATION_JOB_HEALTH;
|
||||
pub use app_server_protocol::METHOD_AUTOMATION_JOB_LIST;
|
||||
pub use app_server_protocol::METHOD_AUTOMATION_JOB_READ;
|
||||
pub use app_server_protocol::METHOD_AUTOMATION_JOB_RUN_HISTORY;
|
||||
pub use app_server_protocol::METHOD_AUTOMATION_JOB_RUN_NOW;
|
||||
pub use app_server_protocol::METHOD_AUTOMATION_JOB_UPDATE;
|
||||
pub use app_server_protocol::METHOD_AUTOMATION_SCHEDULER_CONFIG_READ;
|
||||
pub use app_server_protocol::METHOD_AUTOMATION_SCHEDULER_CONFIG_UPDATE;
|
||||
pub use app_server_protocol::METHOD_AUTOMATION_SCHEDULER_STATUS;
|
||||
pub use app_server_protocol::METHOD_AUTOMATION_SCHEDULE_PREVIEW;
|
||||
pub use app_server_protocol::METHOD_AUTOMATION_SCHEDULE_VALIDATE;
|
||||
pub use app_server_protocol::METHOD_CAPABILITY_LIST;
|
||||
pub use app_server_protocol::METHOD_EVIDENCE_EXPORT;
|
||||
pub use app_server_protocol::METHOD_FILE_SYSTEM_LIST_DIRECTORY;
|
||||
@@ -70,6 +105,11 @@ pub use app_server_protocol::METHOD_FILE_SYSTEM_READ_FILE_PREVIEW;
|
||||
pub use app_server_protocol::METHOD_INITIALIZE;
|
||||
pub use app_server_protocol::METHOD_INITIALIZED;
|
||||
pub use app_server_protocol::METHOD_KNOWLEDGE_PACK_LIST;
|
||||
pub use app_server_protocol::METHOD_MCP_PROMPT_LIST;
|
||||
pub use app_server_protocol::METHOD_MCP_RESOURCE_LIST;
|
||||
pub use app_server_protocol::METHOD_MCP_SERVER_LIST;
|
||||
pub use app_server_protocol::METHOD_MCP_SERVER_STATUS_LIST;
|
||||
pub use app_server_protocol::METHOD_MCP_TOOL_LIST;
|
||||
pub use app_server_protocol::METHOD_MODEL_LIST;
|
||||
pub use app_server_protocol::METHOD_MODEL_PREFERENCES_LIST;
|
||||
pub use app_server_protocol::METHOD_MODEL_PROVIDER_ALIAS_LIST;
|
||||
@@ -301,6 +341,104 @@ impl AppServerClient {
|
||||
self.typed_request(typed::list_automation_jobs())
|
||||
}
|
||||
|
||||
pub fn read_automation_scheduler_config(&mut self) -> Result<JsonRpcRequest, ClientError> {
|
||||
self.typed_request(typed::read_automation_scheduler_config())
|
||||
}
|
||||
|
||||
pub fn update_automation_scheduler_config(
|
||||
&mut self,
|
||||
params: AutomationSchedulerConfigUpdateParams,
|
||||
) -> Result<JsonRpcRequest, ClientError> {
|
||||
self.typed_request(typed::update_automation_scheduler_config(params))
|
||||
}
|
||||
|
||||
pub fn read_automation_scheduler_status(&mut self) -> Result<JsonRpcRequest, ClientError> {
|
||||
self.typed_request(typed::read_automation_scheduler_status())
|
||||
}
|
||||
|
||||
pub fn read_automation_job(
|
||||
&mut self,
|
||||
params: AutomationJobIdParams,
|
||||
) -> Result<JsonRpcRequest, ClientError> {
|
||||
self.typed_request(typed::read_automation_job(params))
|
||||
}
|
||||
|
||||
pub fn create_automation_job(
|
||||
&mut self,
|
||||
params: AutomationJobCreateParams,
|
||||
) -> Result<JsonRpcRequest, ClientError> {
|
||||
self.typed_request(typed::create_automation_job(params))
|
||||
}
|
||||
|
||||
pub fn update_automation_job(
|
||||
&mut self,
|
||||
params: AutomationJobUpdateParams,
|
||||
) -> Result<JsonRpcRequest, ClientError> {
|
||||
self.typed_request(typed::update_automation_job(params))
|
||||
}
|
||||
|
||||
pub fn delete_automation_job(
|
||||
&mut self,
|
||||
params: AutomationJobIdParams,
|
||||
) -> Result<JsonRpcRequest, ClientError> {
|
||||
self.typed_request(typed::delete_automation_job(params))
|
||||
}
|
||||
|
||||
pub fn run_automation_job_now(
|
||||
&mut self,
|
||||
params: AutomationJobIdParams,
|
||||
) -> Result<JsonRpcRequest, ClientError> {
|
||||
self.typed_request(typed::run_automation_job_now(params))
|
||||
}
|
||||
|
||||
pub fn read_automation_health(
|
||||
&mut self,
|
||||
params: AutomationJobHealthParams,
|
||||
) -> Result<JsonRpcRequest, ClientError> {
|
||||
self.typed_request(typed::read_automation_health(params))
|
||||
}
|
||||
|
||||
pub fn read_automation_run_history(
|
||||
&mut self,
|
||||
params: AutomationJobRunHistoryParams,
|
||||
) -> Result<JsonRpcRequest, ClientError> {
|
||||
self.typed_request(typed::read_automation_run_history(params))
|
||||
}
|
||||
|
||||
pub fn preview_automation_schedule(
|
||||
&mut self,
|
||||
params: AutomationScheduleParams,
|
||||
) -> Result<JsonRpcRequest, ClientError> {
|
||||
self.typed_request(typed::preview_automation_schedule(params))
|
||||
}
|
||||
|
||||
pub fn validate_automation_schedule(
|
||||
&mut self,
|
||||
params: AutomationScheduleParams,
|
||||
) -> Result<JsonRpcRequest, ClientError> {
|
||||
self.typed_request(typed::validate_automation_schedule(params))
|
||||
}
|
||||
|
||||
pub fn list_mcp_servers(&mut self) -> Result<JsonRpcRequest, ClientError> {
|
||||
self.typed_request(typed::list_mcp_servers())
|
||||
}
|
||||
|
||||
pub fn list_mcp_servers_with_status(&mut self) -> Result<JsonRpcRequest, ClientError> {
|
||||
self.typed_request(typed::list_mcp_servers_with_status())
|
||||
}
|
||||
|
||||
pub fn list_mcp_tools(&mut self) -> Result<JsonRpcRequest, ClientError> {
|
||||
self.typed_request(typed::list_mcp_tools())
|
||||
}
|
||||
|
||||
pub fn list_mcp_prompts(&mut self) -> Result<JsonRpcRequest, ClientError> {
|
||||
self.typed_request(typed::list_mcp_prompts())
|
||||
}
|
||||
|
||||
pub fn list_mcp_resources(&mut self) -> Result<JsonRpcRequest, ClientError> {
|
||||
self.typed_request(typed::list_mcp_resources())
|
||||
}
|
||||
|
||||
pub fn read_project_memory(
|
||||
&mut self,
|
||||
params: ProjectMemoryReadParams,
|
||||
@@ -535,6 +673,97 @@ pub mod typed {
|
||||
TypedRequest::new(METHOD_AUTOMATION_JOB_LIST, serde_json::json!({}))
|
||||
}
|
||||
|
||||
pub fn read_automation_scheduler_config() -> TypedRequest<serde_json::Value> {
|
||||
TypedRequest::new(
|
||||
METHOD_AUTOMATION_SCHEDULER_CONFIG_READ,
|
||||
serde_json::json!({}),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn update_automation_scheduler_config(
|
||||
params: AutomationSchedulerConfigUpdateParams,
|
||||
) -> TypedRequest<AutomationSchedulerConfigUpdateParams> {
|
||||
TypedRequest::new(METHOD_AUTOMATION_SCHEDULER_CONFIG_UPDATE, params)
|
||||
}
|
||||
|
||||
pub fn read_automation_scheduler_status() -> TypedRequest<serde_json::Value> {
|
||||
TypedRequest::new(METHOD_AUTOMATION_SCHEDULER_STATUS, serde_json::json!({}))
|
||||
}
|
||||
|
||||
pub fn read_automation_job(
|
||||
params: AutomationJobIdParams,
|
||||
) -> TypedRequest<AutomationJobIdParams> {
|
||||
TypedRequest::new(METHOD_AUTOMATION_JOB_READ, params)
|
||||
}
|
||||
|
||||
pub fn create_automation_job(
|
||||
params: AutomationJobCreateParams,
|
||||
) -> TypedRequest<AutomationJobCreateParams> {
|
||||
TypedRequest::new(METHOD_AUTOMATION_JOB_CREATE, params)
|
||||
}
|
||||
|
||||
pub fn update_automation_job(
|
||||
params: AutomationJobUpdateParams,
|
||||
) -> TypedRequest<AutomationJobUpdateParams> {
|
||||
TypedRequest::new(METHOD_AUTOMATION_JOB_UPDATE, params)
|
||||
}
|
||||
|
||||
pub fn delete_automation_job(
|
||||
params: AutomationJobIdParams,
|
||||
) -> TypedRequest<AutomationJobIdParams> {
|
||||
TypedRequest::new(METHOD_AUTOMATION_JOB_DELETE, params)
|
||||
}
|
||||
|
||||
pub fn run_automation_job_now(
|
||||
params: AutomationJobIdParams,
|
||||
) -> TypedRequest<AutomationJobIdParams> {
|
||||
TypedRequest::new(METHOD_AUTOMATION_JOB_RUN_NOW, params)
|
||||
}
|
||||
|
||||
pub fn read_automation_health(
|
||||
params: AutomationJobHealthParams,
|
||||
) -> TypedRequest<AutomationJobHealthParams> {
|
||||
TypedRequest::new(METHOD_AUTOMATION_JOB_HEALTH, params)
|
||||
}
|
||||
|
||||
pub fn read_automation_run_history(
|
||||
params: AutomationJobRunHistoryParams,
|
||||
) -> TypedRequest<AutomationJobRunHistoryParams> {
|
||||
TypedRequest::new(METHOD_AUTOMATION_JOB_RUN_HISTORY, params)
|
||||
}
|
||||
|
||||
pub fn preview_automation_schedule(
|
||||
params: AutomationScheduleParams,
|
||||
) -> TypedRequest<AutomationScheduleParams> {
|
||||
TypedRequest::new(METHOD_AUTOMATION_SCHEDULE_PREVIEW, params)
|
||||
}
|
||||
|
||||
pub fn validate_automation_schedule(
|
||||
params: AutomationScheduleParams,
|
||||
) -> TypedRequest<AutomationScheduleParams> {
|
||||
TypedRequest::new(METHOD_AUTOMATION_SCHEDULE_VALIDATE, params)
|
||||
}
|
||||
|
||||
pub fn list_mcp_servers() -> TypedRequest<serde_json::Value> {
|
||||
TypedRequest::new(METHOD_MCP_SERVER_LIST, serde_json::json!({}))
|
||||
}
|
||||
|
||||
pub fn list_mcp_servers_with_status() -> TypedRequest<serde_json::Value> {
|
||||
TypedRequest::new(METHOD_MCP_SERVER_STATUS_LIST, serde_json::json!({}))
|
||||
}
|
||||
|
||||
pub fn list_mcp_tools() -> TypedRequest<serde_json::Value> {
|
||||
TypedRequest::new(METHOD_MCP_TOOL_LIST, serde_json::json!({}))
|
||||
}
|
||||
|
||||
pub fn list_mcp_prompts() -> TypedRequest<serde_json::Value> {
|
||||
TypedRequest::new(METHOD_MCP_PROMPT_LIST, serde_json::json!({}))
|
||||
}
|
||||
|
||||
pub fn list_mcp_resources() -> TypedRequest<serde_json::Value> {
|
||||
TypedRequest::new(METHOD_MCP_RESOURCE_LIST, serde_json::json!({}))
|
||||
}
|
||||
|
||||
pub fn read_project_memory(
|
||||
params: ProjectMemoryReadParams,
|
||||
) -> TypedRequest<ProjectMemoryReadParams> {
|
||||
@@ -675,6 +904,30 @@ mod tests {
|
||||
assert_eq!(request.params.expect("params"), json!({}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mcp_list_helpers_use_current_methods_and_empty_params() {
|
||||
let mut client = AppServerClient::new();
|
||||
|
||||
let servers = client.list_mcp_servers().expect("servers");
|
||||
let status = client
|
||||
.list_mcp_servers_with_status()
|
||||
.expect("server status");
|
||||
let tools = client.list_mcp_tools().expect("tools");
|
||||
let prompts = client.list_mcp_prompts().expect("prompts");
|
||||
let resources = client.list_mcp_resources().expect("resources");
|
||||
|
||||
assert_eq!(servers.method, METHOD_MCP_SERVER_LIST);
|
||||
assert_eq!(servers.params.expect("params"), json!({}));
|
||||
assert_eq!(status.method, METHOD_MCP_SERVER_STATUS_LIST);
|
||||
assert_eq!(status.params.expect("params"), json!({}));
|
||||
assert_eq!(tools.method, METHOD_MCP_TOOL_LIST);
|
||||
assert_eq!(tools.params.expect("params"), json!({}));
|
||||
assert_eq!(prompts.method, METHOD_MCP_PROMPT_LIST);
|
||||
assert_eq!(prompts.params.expect("params"), json!({}));
|
||||
assert_eq!(resources.method, METHOD_MCP_RESOURCE_LIST);
|
||||
assert_eq!(resources.params.expect("params"), json!({}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_capabilities_preserves_optional_scope_params() {
|
||||
let mut client = AppServerClient::new();
|
||||
@@ -884,7 +1137,91 @@ mod tests {
|
||||
include_archived: true,
|
||||
})
|
||||
.expect("knowledge packs");
|
||||
let scheduler_config = client
|
||||
.read_automation_scheduler_config()
|
||||
.expect("automation scheduler config");
|
||||
let scheduler_config_update = client
|
||||
.update_automation_scheduler_config(AutomationSchedulerConfigUpdateParams {
|
||||
config: json!({
|
||||
"enabled": true,
|
||||
"poll_interval_secs": 60,
|
||||
"enable_history": true,
|
||||
}),
|
||||
})
|
||||
.expect("automation scheduler config update");
|
||||
let scheduler_status = client
|
||||
.read_automation_scheduler_status()
|
||||
.expect("automation scheduler status");
|
||||
let jobs = client.list_automation_jobs().expect("automation jobs");
|
||||
let job = client
|
||||
.read_automation_job(AutomationJobIdParams {
|
||||
id: "job-1".to_string(),
|
||||
})
|
||||
.expect("automation job");
|
||||
let created_job = client
|
||||
.create_automation_job(AutomationJobCreateParams {
|
||||
request: json!({
|
||||
"name": "每日简报",
|
||||
"workspace_id": "workspace-main",
|
||||
"schedule": {
|
||||
"kind": "every",
|
||||
"every_secs": 3600,
|
||||
},
|
||||
"payload": {
|
||||
"kind": "agent_turn",
|
||||
"prompt": "总结今天重点",
|
||||
"web_search": false,
|
||||
},
|
||||
}),
|
||||
})
|
||||
.expect("automation job create");
|
||||
let updated_job = client
|
||||
.update_automation_job(AutomationJobUpdateParams {
|
||||
id: "job-1".to_string(),
|
||||
request: json!({
|
||||
"enabled": false,
|
||||
}),
|
||||
})
|
||||
.expect("automation job update");
|
||||
let deleted_job = client
|
||||
.delete_automation_job(AutomationJobIdParams {
|
||||
id: "job-1".to_string(),
|
||||
})
|
||||
.expect("automation job delete");
|
||||
let run_now = client
|
||||
.run_automation_job_now(AutomationJobIdParams {
|
||||
id: "job-1".to_string(),
|
||||
})
|
||||
.expect("automation job run now");
|
||||
let health = client
|
||||
.read_automation_health(AutomationJobHealthParams {
|
||||
query: Some(json!({
|
||||
"top_limit": 3,
|
||||
})),
|
||||
})
|
||||
.expect("automation health");
|
||||
let history = client
|
||||
.read_automation_run_history(AutomationJobRunHistoryParams {
|
||||
id: "job-1".to_string(),
|
||||
limit: Some(10),
|
||||
})
|
||||
.expect("automation run history");
|
||||
let preview = client
|
||||
.preview_automation_schedule(AutomationScheduleParams {
|
||||
schedule: json!({
|
||||
"kind": "every",
|
||||
"every_secs": 3600,
|
||||
}),
|
||||
})
|
||||
.expect("automation schedule preview");
|
||||
let validate = client
|
||||
.validate_automation_schedule(AutomationScheduleParams {
|
||||
schedule: json!({
|
||||
"kind": "every",
|
||||
"every_secs": 3600,
|
||||
}),
|
||||
})
|
||||
.expect("automation schedule validate");
|
||||
let memory = client
|
||||
.read_project_memory(ProjectMemoryReadParams {
|
||||
project_id: "workspace-main".to_string(),
|
||||
@@ -901,8 +1238,88 @@ mod tests {
|
||||
"includeArchived": true,
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
scheduler_config.method,
|
||||
METHOD_AUTOMATION_SCHEDULER_CONFIG_READ
|
||||
);
|
||||
assert_eq!(
|
||||
scheduler_config_update.method,
|
||||
METHOD_AUTOMATION_SCHEDULER_CONFIG_UPDATE
|
||||
);
|
||||
assert_eq!(
|
||||
scheduler_config_update.params.expect("params"),
|
||||
json!({
|
||||
"config": {
|
||||
"enabled": true,
|
||||
"poll_interval_secs": 60,
|
||||
"enable_history": true,
|
||||
},
|
||||
})
|
||||
);
|
||||
assert_eq!(scheduler_status.method, METHOD_AUTOMATION_SCHEDULER_STATUS);
|
||||
assert_eq!(jobs.method, METHOD_AUTOMATION_JOB_LIST);
|
||||
assert_eq!(jobs.params.expect("params"), json!({}));
|
||||
assert_eq!(job.method, METHOD_AUTOMATION_JOB_READ);
|
||||
assert_eq!(job.params.expect("params"), json!({ "id": "job-1" }));
|
||||
assert_eq!(created_job.method, METHOD_AUTOMATION_JOB_CREATE);
|
||||
assert_eq!(
|
||||
created_job.params.expect("params"),
|
||||
json!({
|
||||
"request": {
|
||||
"name": "每日简报",
|
||||
"workspace_id": "workspace-main",
|
||||
"schedule": {
|
||||
"kind": "every",
|
||||
"every_secs": 3600,
|
||||
},
|
||||
"payload": {
|
||||
"kind": "agent_turn",
|
||||
"prompt": "总结今天重点",
|
||||
"web_search": false,
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
assert_eq!(updated_job.method, METHOD_AUTOMATION_JOB_UPDATE);
|
||||
assert_eq!(
|
||||
updated_job.params.expect("params"),
|
||||
json!({
|
||||
"id": "job-1",
|
||||
"request": {
|
||||
"enabled": false,
|
||||
},
|
||||
})
|
||||
);
|
||||
assert_eq!(deleted_job.method, METHOD_AUTOMATION_JOB_DELETE);
|
||||
assert_eq!(run_now.method, METHOD_AUTOMATION_JOB_RUN_NOW);
|
||||
assert_eq!(health.method, METHOD_AUTOMATION_JOB_HEALTH);
|
||||
assert_eq!(
|
||||
health.params.expect("params"),
|
||||
json!({
|
||||
"query": {
|
||||
"top_limit": 3,
|
||||
},
|
||||
})
|
||||
);
|
||||
assert_eq!(history.method, METHOD_AUTOMATION_JOB_RUN_HISTORY);
|
||||
assert_eq!(
|
||||
history.params.expect("params"),
|
||||
json!({
|
||||
"id": "job-1",
|
||||
"limit": 10,
|
||||
})
|
||||
);
|
||||
assert_eq!(preview.method, METHOD_AUTOMATION_SCHEDULE_PREVIEW);
|
||||
assert_eq!(
|
||||
preview.params.expect("params"),
|
||||
json!({
|
||||
"schedule": {
|
||||
"kind": "every",
|
||||
"every_secs": 3600,
|
||||
},
|
||||
})
|
||||
);
|
||||
assert_eq!(validate.method, METHOD_AUTOMATION_SCHEDULE_VALIDATE);
|
||||
assert_eq!(memory.method, METHOD_PROJECT_MEMORY_READ);
|
||||
assert_eq!(
|
||||
memory.params.expect("params"),
|
||||
@@ -1143,7 +1560,19 @@ mod tests {
|
||||
assert!(methods.contains(&METHOD_WORKSPACE_SKILL_BINDINGS_LIST));
|
||||
assert!(methods.contains(&METHOD_AGENT_APP_INSTALLED_LIST));
|
||||
assert!(methods.contains(&METHOD_KNOWLEDGE_PACK_LIST));
|
||||
assert!(methods.contains(&METHOD_AUTOMATION_SCHEDULER_CONFIG_READ));
|
||||
assert!(methods.contains(&METHOD_AUTOMATION_SCHEDULER_CONFIG_UPDATE));
|
||||
assert!(methods.contains(&METHOD_AUTOMATION_SCHEDULER_STATUS));
|
||||
assert!(methods.contains(&METHOD_AUTOMATION_JOB_LIST));
|
||||
assert!(methods.contains(&METHOD_AUTOMATION_JOB_READ));
|
||||
assert!(methods.contains(&METHOD_AUTOMATION_JOB_CREATE));
|
||||
assert!(methods.contains(&METHOD_AUTOMATION_JOB_UPDATE));
|
||||
assert!(methods.contains(&METHOD_AUTOMATION_JOB_DELETE));
|
||||
assert!(methods.contains(&METHOD_AUTOMATION_JOB_RUN_NOW));
|
||||
assert!(methods.contains(&METHOD_AUTOMATION_JOB_HEALTH));
|
||||
assert!(methods.contains(&METHOD_AUTOMATION_JOB_RUN_HISTORY));
|
||||
assert!(methods.contains(&METHOD_AUTOMATION_SCHEDULE_PREVIEW));
|
||||
assert!(methods.contains(&METHOD_AUTOMATION_SCHEDULE_VALIDATE));
|
||||
assert!(methods.contains(&METHOD_PROJECT_MEMORY_READ));
|
||||
assert!(methods.contains(&METHOD_AGENT_SESSION_EVENT));
|
||||
assert!(is_app_server_request_method(METHOD_CAPABILITY_LIST));
|
||||
@@ -1161,7 +1590,20 @@ mod tests {
|
||||
METHOD_AGENT_APP_INSTALLED_LIST
|
||||
));
|
||||
assert!(is_app_server_request_method(METHOD_KNOWLEDGE_PACK_LIST));
|
||||
assert!(is_app_server_request_method(
|
||||
METHOD_AUTOMATION_SCHEDULER_CONFIG_READ
|
||||
));
|
||||
assert!(is_app_server_request_method(
|
||||
METHOD_AUTOMATION_SCHEDULER_CONFIG_UPDATE
|
||||
));
|
||||
assert!(is_app_server_request_method(
|
||||
METHOD_AUTOMATION_SCHEDULER_STATUS
|
||||
));
|
||||
assert!(is_app_server_request_method(METHOD_AUTOMATION_JOB_LIST));
|
||||
assert!(is_app_server_request_method(METHOD_AUTOMATION_JOB_CREATE));
|
||||
assert!(is_app_server_request_method(
|
||||
METHOD_AUTOMATION_SCHEDULE_VALIDATE
|
||||
));
|
||||
assert!(is_app_server_request_method(METHOD_PROJECT_MEMORY_READ));
|
||||
assert!(is_app_server_request_method(
|
||||
METHOD_AGENT_SESSION_TURN_CANCEL
|
||||
|
||||
@@ -1704,6 +1704,62 @@
|
||||
"title": "ArtifactSummary",
|
||||
"type": "object"
|
||||
},
|
||||
"AutomationJobCreateParams": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"request": true
|
||||
},
|
||||
"required": [
|
||||
"request"
|
||||
],
|
||||
"title": "AutomationJobCreateParams",
|
||||
"type": "object"
|
||||
},
|
||||
"AutomationJobDeleteResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"deleted": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"deleted"
|
||||
],
|
||||
"title": "AutomationJobDeleteResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"AutomationJobHealthParams": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"query": true
|
||||
},
|
||||
"title": "AutomationJobHealthParams",
|
||||
"type": "object"
|
||||
},
|
||||
"AutomationJobHealthResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"health": true
|
||||
},
|
||||
"required": [
|
||||
"health"
|
||||
],
|
||||
"title": "AutomationJobHealthResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"AutomationJobIdParams": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id"
|
||||
],
|
||||
"title": "AutomationJobIdParams",
|
||||
"type": "object"
|
||||
},
|
||||
"AutomationJobListResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
@@ -1716,6 +1772,171 @@
|
||||
"title": "AutomationJobListResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"AutomationJobReadResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"job": true
|
||||
},
|
||||
"title": "AutomationJobReadResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"AutomationJobRunHistoryParams": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"limit": {
|
||||
"format": "uint",
|
||||
"minimum": 0,
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id"
|
||||
],
|
||||
"title": "AutomationJobRunHistoryParams",
|
||||
"type": "object"
|
||||
},
|
||||
"AutomationJobRunHistoryResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"runs": {
|
||||
"default": [],
|
||||
"items": true,
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"title": "AutomationJobRunHistoryResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"AutomationJobRunNowResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"result": true
|
||||
},
|
||||
"required": [
|
||||
"result"
|
||||
],
|
||||
"title": "AutomationJobRunNowResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"AutomationJobUpdateParams": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"request": true
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"request"
|
||||
],
|
||||
"title": "AutomationJobUpdateParams",
|
||||
"type": "object"
|
||||
},
|
||||
"AutomationJobWriteResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"job": true
|
||||
},
|
||||
"required": [
|
||||
"job"
|
||||
],
|
||||
"title": "AutomationJobWriteResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"AutomationScheduleParams": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"schedule": true
|
||||
},
|
||||
"required": [
|
||||
"schedule"
|
||||
],
|
||||
"title": "AutomationScheduleParams",
|
||||
"type": "object"
|
||||
},
|
||||
"AutomationSchedulePreviewResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"nextRunAt": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
}
|
||||
},
|
||||
"title": "AutomationSchedulePreviewResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"AutomationScheduleValidateResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"error": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"valid": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"valid"
|
||||
],
|
||||
"title": "AutomationScheduleValidateResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"AutomationSchedulerConfigReadResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"config": true
|
||||
},
|
||||
"required": [
|
||||
"config"
|
||||
],
|
||||
"title": "AutomationSchedulerConfigReadResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"AutomationSchedulerConfigUpdateParams": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"config": true
|
||||
},
|
||||
"required": [
|
||||
"config"
|
||||
],
|
||||
"title": "AutomationSchedulerConfigUpdateParams",
|
||||
"type": "object"
|
||||
},
|
||||
"AutomationSchedulerConfigUpdateResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"config": true
|
||||
},
|
||||
"required": [
|
||||
"config"
|
||||
],
|
||||
"title": "AutomationSchedulerConfigUpdateResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"AutomationSchedulerStatusResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"status": true
|
||||
},
|
||||
"required": [
|
||||
"status"
|
||||
],
|
||||
"title": "AutomationSchedulerStatusResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"BusinessObjectRef": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
@@ -3448,6 +3669,66 @@
|
||||
"title": "KnowledgeListPacksResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"McpPromptListResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"prompts": {
|
||||
"default": [],
|
||||
"items": true,
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"title": "McpPromptListResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"McpResourceListResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"resources": {
|
||||
"default": [],
|
||||
"items": true,
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"title": "McpResourceListResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"McpServerListResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"servers": {
|
||||
"default": [],
|
||||
"items": true,
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"title": "McpServerListResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"McpServerStatusListResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"servers": {
|
||||
"default": [],
|
||||
"items": true,
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"title": "McpServerStatusListResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"McpToolListResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"tools": {
|
||||
"default": [],
|
||||
"items": true,
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"title": "McpToolListResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelListParams": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
@@ -3536,6 +3817,321 @@
|
||||
"title": "ModelProviderCatalogListResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderConfigExportParams": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"includeKeys": {
|
||||
"default": null,
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"title": "ModelProviderConfigExportParams",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderConfigExportResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"configJson": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"configJson"
|
||||
],
|
||||
"title": "ModelProviderConfigExportResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderConfigImportParams": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"configJson": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"configJson"
|
||||
],
|
||||
"title": "ModelProviderConfigImportParams",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderConfigImportResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"errors": {
|
||||
"default": [],
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"importedApiKeys": {
|
||||
"format": "uint",
|
||||
"minimum": 0,
|
||||
"type": "integer"
|
||||
},
|
||||
"importedProviders": {
|
||||
"format": "uint",
|
||||
"minimum": 0,
|
||||
"type": "integer"
|
||||
},
|
||||
"skippedProviders": {
|
||||
"format": "uint",
|
||||
"minimum": 0,
|
||||
"type": "integer"
|
||||
},
|
||||
"success": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"importedApiKeys",
|
||||
"importedProviders",
|
||||
"skippedProviders",
|
||||
"success"
|
||||
],
|
||||
"title": "ModelProviderConfigImportResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderCreateParams": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"provider": true
|
||||
},
|
||||
"required": [
|
||||
"provider"
|
||||
],
|
||||
"title": "ModelProviderCreateParams",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderDeleteParams": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"providerId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"providerId"
|
||||
],
|
||||
"title": "ModelProviderDeleteParams",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderDeleteResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"deleted": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"deleted"
|
||||
],
|
||||
"title": "ModelProviderDeleteResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderFetchModelsParams": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"providerId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"providerId"
|
||||
],
|
||||
"title": "ModelProviderFetchModelsParams",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderFetchModelsResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"diagnosticHint": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"error": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"errorKind": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"fromCache": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"models": {
|
||||
"default": [],
|
||||
"items": true,
|
||||
"type": "array"
|
||||
},
|
||||
"requestUrl": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"shouldPromptError": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"source": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"fromCache",
|
||||
"shouldPromptError",
|
||||
"source"
|
||||
],
|
||||
"title": "ModelProviderFetchModelsResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderKeyCreateParams": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"alias": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"apiKey": {
|
||||
"type": "string"
|
||||
},
|
||||
"providerId": {
|
||||
"type": "string"
|
||||
},
|
||||
"replaceExisting": {
|
||||
"default": null,
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"apiKey",
|
||||
"providerId"
|
||||
],
|
||||
"title": "ModelProviderKeyCreateParams",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderKeyDeleteParams": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"keyId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"keyId"
|
||||
],
|
||||
"title": "ModelProviderKeyDeleteParams",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderKeyDeleteResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"deleted": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"deleted"
|
||||
],
|
||||
"title": "ModelProviderKeyDeleteResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderKeyEventParams": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"keyId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"keyId"
|
||||
],
|
||||
"title": "ModelProviderKeyEventParams",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderKeyNextParams": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"providerId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"providerId"
|
||||
],
|
||||
"title": "ModelProviderKeyNextParams",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderKeyNextResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"apiKey": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"keyId": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
}
|
||||
},
|
||||
"title": "ModelProviderKeyNextResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderKeyUpdateParams": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"alias": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"enabled": {
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"keyId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"keyId"
|
||||
],
|
||||
"title": "ModelProviderKeyUpdateParams",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderKeyWriteResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"key": true
|
||||
},
|
||||
"required": [
|
||||
"key"
|
||||
],
|
||||
"title": "ModelProviderKeyWriteResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderListResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
@@ -3548,6 +4144,268 @@
|
||||
"title": "ModelProviderListResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderMutationResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "ModelProviderMutationResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderReadParams": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"providerId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"providerId"
|
||||
],
|
||||
"title": "ModelProviderReadParams",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderReadResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"provider": true
|
||||
},
|
||||
"title": "ModelProviderReadResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderSortOrderItem": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"providerId": {
|
||||
"type": "string"
|
||||
},
|
||||
"sortOrder": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"providerId",
|
||||
"sortOrder"
|
||||
],
|
||||
"title": "ModelProviderSortOrderItem",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderSortOrdersUpdateParams": {
|
||||
"$defs": {
|
||||
"ModelProviderSortOrderItem": {
|
||||
"properties": {
|
||||
"providerId": {
|
||||
"type": "string"
|
||||
},
|
||||
"sortOrder": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"providerId",
|
||||
"sortOrder"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"sortOrders": {
|
||||
"default": [],
|
||||
"items": {
|
||||
"$ref": "#/$defs/ModelProviderSortOrderItem"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"title": "ModelProviderSortOrdersUpdateParams",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderTestChatParams": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"modelName": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"prompt": {
|
||||
"type": "string"
|
||||
},
|
||||
"providerId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"prompt",
|
||||
"providerId"
|
||||
],
|
||||
"title": "ModelProviderTestChatParams",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderTestChatResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"error": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"latencyMs": {
|
||||
"format": "uint64",
|
||||
"minimum": 0,
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"raw": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"success": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"success"
|
||||
],
|
||||
"title": "ModelProviderTestChatResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderTestConnectionParams": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"modelName": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"providerId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"providerId"
|
||||
],
|
||||
"title": "ModelProviderTestConnectionParams",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderTestConnectionResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"error": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"latencyMs": {
|
||||
"format": "uint64",
|
||||
"minimum": 0,
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"models": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"success": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"success"
|
||||
],
|
||||
"title": "ModelProviderTestConnectionResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderUiStateReadParams": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"key"
|
||||
],
|
||||
"title": "ModelProviderUiStateReadParams",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderUiStateReadResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"value": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
}
|
||||
},
|
||||
"title": "ModelProviderUiStateReadResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderUiStateWriteParams": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"key",
|
||||
"value"
|
||||
],
|
||||
"title": "ModelProviderUiStateWriteParams",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderUpdateParams": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"patch": true,
|
||||
"providerId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"patch",
|
||||
"providerId"
|
||||
],
|
||||
"title": "ModelProviderUpdateParams",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelProviderWriteResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"provider": true
|
||||
},
|
||||
"required": [
|
||||
"provider"
|
||||
],
|
||||
"title": "ModelProviderWriteResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"ModelSyncStateReadResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
@@ -4056,7 +4914,19 @@
|
||||
"agentSession/turn/start",
|
||||
"agentSession/update",
|
||||
"artifact/read",
|
||||
"automationJob/create",
|
||||
"automationJob/delete",
|
||||
"automationJob/health",
|
||||
"automationJob/list",
|
||||
"automationJob/read",
|
||||
"automationJob/runHistory",
|
||||
"automationJob/runNow",
|
||||
"automationJob/update",
|
||||
"automationSchedule/preview",
|
||||
"automationSchedule/validate",
|
||||
"automationScheduler/config/read",
|
||||
"automationScheduler/config/update",
|
||||
"automationScheduler/status",
|
||||
"capability/list",
|
||||
"connectCallback/send",
|
||||
"connectDeepLink/resolve",
|
||||
@@ -4067,12 +4937,35 @@
|
||||
"fileSystem/readFilePreview",
|
||||
"initialize",
|
||||
"knowledgePack/list",
|
||||
"mcpPrompt/list",
|
||||
"mcpResource/list",
|
||||
"mcpServer/list",
|
||||
"mcpServerStatus/list",
|
||||
"mcpTool/list",
|
||||
"model/list",
|
||||
"modelPreferences/list",
|
||||
"modelProvider/catalog/list",
|
||||
"modelProvider/create",
|
||||
"modelProvider/delete",
|
||||
"modelProvider/fetchModels",
|
||||
"modelProvider/list",
|
||||
"modelProvider/read",
|
||||
"modelProvider/sortOrders/update",
|
||||
"modelProvider/testChat",
|
||||
"modelProvider/testConnection",
|
||||
"modelProvider/update",
|
||||
"modelProviderAlias/list",
|
||||
"modelProviderAlias/read",
|
||||
"modelProviderConfig/export",
|
||||
"modelProviderConfig/import",
|
||||
"modelProviderKey/create",
|
||||
"modelProviderKey/delete",
|
||||
"modelProviderKey/error/record",
|
||||
"modelProviderKey/next",
|
||||
"modelProviderKey/update",
|
||||
"modelProviderKey/usage/record",
|
||||
"modelProviderUiState/read",
|
||||
"modelProviderUiState/write",
|
||||
"modelSyncState/read",
|
||||
"projectMemory/read",
|
||||
"skill/list",
|
||||
|
||||
@@ -62,10 +62,58 @@
|
||||
"kind": "request",
|
||||
"method": "artifact/read"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "automationJob/create"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "automationJob/delete"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "automationJob/health"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "automationJob/list"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "automationJob/read"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "automationJob/runHistory"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "automationJob/runNow"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "automationJob/update"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "automationSchedule/preview"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "automationSchedule/validate"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "automationScheduler/config/read"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "automationScheduler/config/update"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "automationScheduler/status"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "capability/list"
|
||||
@@ -110,6 +158,26 @@
|
||||
"kind": "request",
|
||||
"method": "knowledgePack/list"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "mcpPrompt/list"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "mcpResource/list"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "mcpServer/list"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "mcpServerStatus/list"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "mcpTool/list"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "model/list"
|
||||
@@ -122,10 +190,42 @@
|
||||
"kind": "request",
|
||||
"method": "modelProvider/catalog/list"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "modelProvider/create"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "modelProvider/delete"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "modelProvider/fetchModels"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "modelProvider/list"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "modelProvider/read"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "modelProvider/sortOrders/update"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "modelProvider/testChat"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "modelProvider/testConnection"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "modelProvider/update"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "modelProviderAlias/list"
|
||||
@@ -134,6 +234,46 @@
|
||||
"kind": "request",
|
||||
"method": "modelProviderAlias/read"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "modelProviderConfig/export"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "modelProviderConfig/import"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "modelProviderKey/create"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "modelProviderKey/delete"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "modelProviderKey/error/record"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "modelProviderKey/next"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "modelProviderKey/update"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "modelProviderKey/usage/record"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "modelProviderUiState/read"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "modelProviderUiState/write"
|
||||
},
|
||||
{
|
||||
"kind": "request",
|
||||
"method": "modelSyncState/read"
|
||||
@@ -237,7 +377,25 @@
|
||||
"ArtifactReadParams",
|
||||
"ArtifactReadResponse",
|
||||
"ArtifactSummary",
|
||||
"AutomationJobCreateParams",
|
||||
"AutomationJobDeleteResponse",
|
||||
"AutomationJobHealthParams",
|
||||
"AutomationJobHealthResponse",
|
||||
"AutomationJobIdParams",
|
||||
"AutomationJobListResponse",
|
||||
"AutomationJobReadResponse",
|
||||
"AutomationJobRunHistoryParams",
|
||||
"AutomationJobRunHistoryResponse",
|
||||
"AutomationJobRunNowResponse",
|
||||
"AutomationJobUpdateParams",
|
||||
"AutomationJobWriteResponse",
|
||||
"AutomationScheduleParams",
|
||||
"AutomationSchedulePreviewResponse",
|
||||
"AutomationScheduleValidateResponse",
|
||||
"AutomationSchedulerConfigReadResponse",
|
||||
"AutomationSchedulerConfigUpdateParams",
|
||||
"AutomationSchedulerConfigUpdateResponse",
|
||||
"AutomationSchedulerStatusResponse",
|
||||
"BusinessObjectRef",
|
||||
"CapabilityDescriptor",
|
||||
"CapabilityListParams",
|
||||
@@ -267,6 +425,11 @@
|
||||
"InitializeResponse",
|
||||
"KnowledgeListPacksParams",
|
||||
"KnowledgeListPacksResponse",
|
||||
"McpPromptListResponse",
|
||||
"McpResourceListResponse",
|
||||
"McpServerListResponse",
|
||||
"McpServerStatusListResponse",
|
||||
"McpToolListResponse",
|
||||
"ModelListParams",
|
||||
"ModelListResponse",
|
||||
"ModelPreferencesListResponse",
|
||||
@@ -274,7 +437,38 @@
|
||||
"ModelProviderAliasReadParams",
|
||||
"ModelProviderAliasReadResponse",
|
||||
"ModelProviderCatalogListResponse",
|
||||
"ModelProviderConfigExportParams",
|
||||
"ModelProviderConfigExportResponse",
|
||||
"ModelProviderConfigImportParams",
|
||||
"ModelProviderConfigImportResponse",
|
||||
"ModelProviderCreateParams",
|
||||
"ModelProviderDeleteParams",
|
||||
"ModelProviderDeleteResponse",
|
||||
"ModelProviderFetchModelsParams",
|
||||
"ModelProviderFetchModelsResponse",
|
||||
"ModelProviderKeyCreateParams",
|
||||
"ModelProviderKeyDeleteParams",
|
||||
"ModelProviderKeyDeleteResponse",
|
||||
"ModelProviderKeyEventParams",
|
||||
"ModelProviderKeyNextParams",
|
||||
"ModelProviderKeyNextResponse",
|
||||
"ModelProviderKeyUpdateParams",
|
||||
"ModelProviderKeyWriteResponse",
|
||||
"ModelProviderListResponse",
|
||||
"ModelProviderMutationResponse",
|
||||
"ModelProviderReadParams",
|
||||
"ModelProviderReadResponse",
|
||||
"ModelProviderSortOrderItem",
|
||||
"ModelProviderSortOrdersUpdateParams",
|
||||
"ModelProviderTestChatParams",
|
||||
"ModelProviderTestChatResponse",
|
||||
"ModelProviderTestConnectionParams",
|
||||
"ModelProviderTestConnectionResponse",
|
||||
"ModelProviderUiStateReadParams",
|
||||
"ModelProviderUiStateReadResponse",
|
||||
"ModelProviderUiStateWriteParams",
|
||||
"ModelProviderUpdateParams",
|
||||
"ModelProviderWriteResponse",
|
||||
"ModelSyncStateReadResponse",
|
||||
"OpenDeepLinkPayload",
|
||||
"PlatformInfo",
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"request": true
|
||||
},
|
||||
"required": [
|
||||
"request"
|
||||
],
|
||||
"title": "AutomationJobCreateParams",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"deleted": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"deleted"
|
||||
],
|
||||
"title": "AutomationJobDeleteResponse",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"query": true
|
||||
},
|
||||
"title": "AutomationJobHealthParams",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"health": true
|
||||
},
|
||||
"required": [
|
||||
"health"
|
||||
],
|
||||
"title": "AutomationJobHealthResponse",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id"
|
||||
],
|
||||
"title": "AutomationJobIdParams",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"job": true
|
||||
},
|
||||
"title": "AutomationJobReadResponse",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"limit": {
|
||||
"format": "uint",
|
||||
"minimum": 0,
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id"
|
||||
],
|
||||
"title": "AutomationJobRunHistoryParams",
|
||||
"type": "object"
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"runs": {
|
||||
"default": [],
|
||||
"items": true,
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"title": "AutomationJobRunHistoryResponse",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"result": true
|
||||
},
|
||||
"required": [
|
||||
"result"
|
||||
],
|
||||
"title": "AutomationJobRunNowResponse",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"request": true
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"request"
|
||||
],
|
||||
"title": "AutomationJobUpdateParams",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"job": true
|
||||
},
|
||||
"required": [
|
||||
"job"
|
||||
],
|
||||
"title": "AutomationJobWriteResponse",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"schedule": true
|
||||
},
|
||||
"required": [
|
||||
"schedule"
|
||||
],
|
||||
"title": "AutomationScheduleParams",
|
||||
"type": "object"
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"nextRunAt": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
}
|
||||
},
|
||||
"title": "AutomationSchedulePreviewResponse",
|
||||
"type": "object"
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"error": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"valid": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"valid"
|
||||
],
|
||||
"title": "AutomationScheduleValidateResponse",
|
||||
"type": "object"
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"config": true
|
||||
},
|
||||
"required": [
|
||||
"config"
|
||||
],
|
||||
"title": "AutomationSchedulerConfigReadResponse",
|
||||
"type": "object"
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"config": true
|
||||
},
|
||||
"required": [
|
||||
"config"
|
||||
],
|
||||
"title": "AutomationSchedulerConfigUpdateParams",
|
||||
"type": "object"
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"config": true
|
||||
},
|
||||
"required": [
|
||||
"config"
|
||||
],
|
||||
"title": "AutomationSchedulerConfigUpdateResponse",
|
||||
"type": "object"
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"status": true
|
||||
},
|
||||
"required": [
|
||||
"status"
|
||||
],
|
||||
"title": "AutomationSchedulerStatusResponse",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"prompts": {
|
||||
"default": [],
|
||||
"items": true,
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"title": "McpPromptListResponse",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"resources": {
|
||||
"default": [],
|
||||
"items": true,
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"title": "McpResourceListResponse",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"servers": {
|
||||
"default": [],
|
||||
"items": true,
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"title": "McpServerListResponse",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"servers": {
|
||||
"default": [],
|
||||
"items": true,
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"title": "McpServerStatusListResponse",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"tools": {
|
||||
"default": [],
|
||||
"items": true,
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"title": "McpToolListResponse",
|
||||
"type": "object"
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"includeKeys": {
|
||||
"default": null,
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"title": "ModelProviderConfigExportParams",
|
||||
"type": "object"
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"configJson": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"configJson"
|
||||
],
|
||||
"title": "ModelProviderConfigExportResponse",
|
||||
"type": "object"
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"configJson": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"configJson"
|
||||
],
|
||||
"title": "ModelProviderConfigImportParams",
|
||||
"type": "object"
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"errors": {
|
||||
"default": [],
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"importedApiKeys": {
|
||||
"format": "uint",
|
||||
"minimum": 0,
|
||||
"type": "integer"
|
||||
},
|
||||
"importedProviders": {
|
||||
"format": "uint",
|
||||
"minimum": 0,
|
||||
"type": "integer"
|
||||
},
|
||||
"skippedProviders": {
|
||||
"format": "uint",
|
||||
"minimum": 0,
|
||||
"type": "integer"
|
||||
},
|
||||
"success": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"importedApiKeys",
|
||||
"importedProviders",
|
||||
"skippedProviders",
|
||||
"success"
|
||||
],
|
||||
"title": "ModelProviderConfigImportResponse",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"provider": true
|
||||
},
|
||||
"required": [
|
||||
"provider"
|
||||
],
|
||||
"title": "ModelProviderCreateParams",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"providerId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"providerId"
|
||||
],
|
||||
"title": "ModelProviderDeleteParams",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"deleted": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"deleted"
|
||||
],
|
||||
"title": "ModelProviderDeleteResponse",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"providerId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"providerId"
|
||||
],
|
||||
"title": "ModelProviderFetchModelsParams",
|
||||
"type": "object"
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"diagnosticHint": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"error": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"errorKind": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"fromCache": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"models": {
|
||||
"default": [],
|
||||
"items": true,
|
||||
"type": "array"
|
||||
},
|
||||
"requestUrl": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"shouldPromptError": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"source": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"fromCache",
|
||||
"shouldPromptError",
|
||||
"source"
|
||||
],
|
||||
"title": "ModelProviderFetchModelsResponse",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"alias": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"apiKey": {
|
||||
"type": "string"
|
||||
},
|
||||
"providerId": {
|
||||
"type": "string"
|
||||
},
|
||||
"replaceExisting": {
|
||||
"default": null,
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"apiKey",
|
||||
"providerId"
|
||||
],
|
||||
"title": "ModelProviderKeyCreateParams",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"keyId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"keyId"
|
||||
],
|
||||
"title": "ModelProviderKeyDeleteParams",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"deleted": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"deleted"
|
||||
],
|
||||
"title": "ModelProviderKeyDeleteResponse",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"keyId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"keyId"
|
||||
],
|
||||
"title": "ModelProviderKeyEventParams",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"providerId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"providerId"
|
||||
],
|
||||
"title": "ModelProviderKeyNextParams",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"apiKey": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"keyId": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
}
|
||||
},
|
||||
"title": "ModelProviderKeyNextResponse",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"alias": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"enabled": {
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"keyId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"keyId"
|
||||
],
|
||||
"title": "ModelProviderKeyUpdateParams",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"key": true
|
||||
},
|
||||
"required": [
|
||||
"key"
|
||||
],
|
||||
"title": "ModelProviderKeyWriteResponse",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "ModelProviderMutationResponse",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"providerId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"providerId"
|
||||
],
|
||||
"title": "ModelProviderReadParams",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"provider": true
|
||||
},
|
||||
"title": "ModelProviderReadResponse",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"providerId": {
|
||||
"type": "string"
|
||||
},
|
||||
"sortOrder": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"providerId",
|
||||
"sortOrder"
|
||||
],
|
||||
"title": "ModelProviderSortOrderItem",
|
||||
"type": "object"
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"$defs": {
|
||||
"ModelProviderSortOrderItem": {
|
||||
"properties": {
|
||||
"providerId": {
|
||||
"type": "string"
|
||||
},
|
||||
"sortOrder": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"providerId",
|
||||
"sortOrder"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"sortOrders": {
|
||||
"default": [],
|
||||
"items": {
|
||||
"$ref": "#/$defs/ModelProviderSortOrderItem"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"title": "ModelProviderSortOrdersUpdateParams",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"modelName": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"prompt": {
|
||||
"type": "string"
|
||||
},
|
||||
"providerId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"prompt",
|
||||
"providerId"
|
||||
],
|
||||
"title": "ModelProviderTestChatParams",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"error": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"latencyMs": {
|
||||
"format": "uint64",
|
||||
"minimum": 0,
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"raw": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"success": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"success"
|
||||
],
|
||||
"title": "ModelProviderTestChatResponse",
|
||||
"type": "object"
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"modelName": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"providerId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"providerId"
|
||||
],
|
||||
"title": "ModelProviderTestConnectionParams",
|
||||
"type": "object"
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"error": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"latencyMs": {
|
||||
"format": "uint64",
|
||||
"minimum": 0,
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"models": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"success": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"success"
|
||||
],
|
||||
"title": "ModelProviderTestConnectionResponse",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"key"
|
||||
],
|
||||
"title": "ModelProviderUiStateReadParams",
|
||||
"type": "object"
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"value": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
}
|
||||
},
|
||||
"title": "ModelProviderUiStateReadResponse",
|
||||
"type": "object"
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"key",
|
||||
"value"
|
||||
],
|
||||
"title": "ModelProviderUiStateWriteParams",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"patch": true,
|
||||
"providerId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"patch",
|
||||
"providerId"
|
||||
],
|
||||
"title": "ModelProviderUpdateParams",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"provider": true
|
||||
},
|
||||
"required": [
|
||||
"provider"
|
||||
],
|
||||
"title": "ModelProviderWriteResponse",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -55,7 +55,30 @@ pub const V0_SCHEMA_TYPE_NAMES: &[&str] = &[
|
||||
"AgentAppUiRuntimeStatusResponse",
|
||||
"KnowledgeListPacksParams",
|
||||
"KnowledgeListPacksResponse",
|
||||
"AutomationSchedulerConfigReadResponse",
|
||||
"AutomationSchedulerConfigUpdateParams",
|
||||
"AutomationSchedulerConfigUpdateResponse",
|
||||
"AutomationSchedulerStatusResponse",
|
||||
"AutomationJobListResponse",
|
||||
"AutomationJobIdParams",
|
||||
"AutomationJobReadResponse",
|
||||
"AutomationJobCreateParams",
|
||||
"AutomationJobWriteResponse",
|
||||
"AutomationJobUpdateParams",
|
||||
"AutomationJobDeleteResponse",
|
||||
"AutomationJobRunNowResponse",
|
||||
"AutomationJobHealthParams",
|
||||
"AutomationJobHealthResponse",
|
||||
"AutomationJobRunHistoryParams",
|
||||
"AutomationJobRunHistoryResponse",
|
||||
"AutomationScheduleParams",
|
||||
"AutomationSchedulePreviewResponse",
|
||||
"AutomationScheduleValidateResponse",
|
||||
"McpServerListResponse",
|
||||
"McpServerStatusListResponse",
|
||||
"McpToolListResponse",
|
||||
"McpPromptListResponse",
|
||||
"McpResourceListResponse",
|
||||
"ProjectMemoryReadParams",
|
||||
"ProjectMemoryReadResponse",
|
||||
"ModelListParams",
|
||||
@@ -64,6 +87,37 @@ pub const V0_SCHEMA_TYPE_NAMES: &[&str] = &[
|
||||
"ModelSyncStateReadResponse",
|
||||
"ModelProviderListResponse",
|
||||
"ModelProviderCatalogListResponse",
|
||||
"ModelProviderReadParams",
|
||||
"ModelProviderReadResponse",
|
||||
"ModelProviderCreateParams",
|
||||
"ModelProviderWriteResponse",
|
||||
"ModelProviderUpdateParams",
|
||||
"ModelProviderDeleteParams",
|
||||
"ModelProviderDeleteResponse",
|
||||
"ModelProviderSortOrderItem",
|
||||
"ModelProviderSortOrdersUpdateParams",
|
||||
"ModelProviderMutationResponse",
|
||||
"ModelProviderConfigExportParams",
|
||||
"ModelProviderConfigExportResponse",
|
||||
"ModelProviderConfigImportParams",
|
||||
"ModelProviderConfigImportResponse",
|
||||
"ModelProviderTestConnectionParams",
|
||||
"ModelProviderTestConnectionResponse",
|
||||
"ModelProviderTestChatParams",
|
||||
"ModelProviderTestChatResponse",
|
||||
"ModelProviderFetchModelsParams",
|
||||
"ModelProviderFetchModelsResponse",
|
||||
"ModelProviderKeyCreateParams",
|
||||
"ModelProviderKeyWriteResponse",
|
||||
"ModelProviderKeyUpdateParams",
|
||||
"ModelProviderKeyDeleteParams",
|
||||
"ModelProviderKeyDeleteResponse",
|
||||
"ModelProviderKeyNextParams",
|
||||
"ModelProviderKeyNextResponse",
|
||||
"ModelProviderKeyEventParams",
|
||||
"ModelProviderUiStateReadParams",
|
||||
"ModelProviderUiStateReadResponse",
|
||||
"ModelProviderUiStateWriteParams",
|
||||
"ModelProviderAliasReadParams",
|
||||
"ModelProviderAliasReadResponse",
|
||||
"ModelProviderAliasListResponse",
|
||||
@@ -128,13 +182,48 @@ pub const METHOD_AGENT_APP_UI_RUNTIME_START: &str = "agentAppUiRuntime/start";
|
||||
pub const METHOD_AGENT_APP_UI_RUNTIME_STATUS: &str = "agentAppUiRuntime/status";
|
||||
pub const METHOD_AGENT_APP_UI_RUNTIME_STOP: &str = "agentAppUiRuntime/stop";
|
||||
pub const METHOD_KNOWLEDGE_PACK_LIST: &str = "knowledgePack/list";
|
||||
pub const METHOD_AUTOMATION_SCHEDULER_CONFIG_READ: &str = "automationScheduler/config/read";
|
||||
pub const METHOD_AUTOMATION_SCHEDULER_CONFIG_UPDATE: &str = "automationScheduler/config/update";
|
||||
pub const METHOD_AUTOMATION_SCHEDULER_STATUS: &str = "automationScheduler/status";
|
||||
pub const METHOD_AUTOMATION_JOB_LIST: &str = "automationJob/list";
|
||||
pub const METHOD_AUTOMATION_JOB_READ: &str = "automationJob/read";
|
||||
pub const METHOD_AUTOMATION_JOB_CREATE: &str = "automationJob/create";
|
||||
pub const METHOD_AUTOMATION_JOB_UPDATE: &str = "automationJob/update";
|
||||
pub const METHOD_AUTOMATION_JOB_DELETE: &str = "automationJob/delete";
|
||||
pub const METHOD_AUTOMATION_JOB_RUN_NOW: &str = "automationJob/runNow";
|
||||
pub const METHOD_AUTOMATION_JOB_HEALTH: &str = "automationJob/health";
|
||||
pub const METHOD_AUTOMATION_JOB_RUN_HISTORY: &str = "automationJob/runHistory";
|
||||
pub const METHOD_AUTOMATION_SCHEDULE_PREVIEW: &str = "automationSchedule/preview";
|
||||
pub const METHOD_AUTOMATION_SCHEDULE_VALIDATE: &str = "automationSchedule/validate";
|
||||
pub const METHOD_MCP_SERVER_LIST: &str = "mcpServer/list";
|
||||
pub const METHOD_MCP_SERVER_STATUS_LIST: &str = "mcpServerStatus/list";
|
||||
pub const METHOD_MCP_TOOL_LIST: &str = "mcpTool/list";
|
||||
pub const METHOD_MCP_PROMPT_LIST: &str = "mcpPrompt/list";
|
||||
pub const METHOD_MCP_RESOURCE_LIST: &str = "mcpResource/list";
|
||||
pub const METHOD_PROJECT_MEMORY_READ: &str = "projectMemory/read";
|
||||
pub const METHOD_MODEL_LIST: &str = "model/list";
|
||||
pub const METHOD_MODEL_PREFERENCES_LIST: &str = "modelPreferences/list";
|
||||
pub const METHOD_MODEL_SYNC_STATE_READ: &str = "modelSyncState/read";
|
||||
pub const METHOD_MODEL_PROVIDER_LIST: &str = "modelProvider/list";
|
||||
pub const METHOD_MODEL_PROVIDER_CATALOG_LIST: &str = "modelProvider/catalog/list";
|
||||
pub const METHOD_MODEL_PROVIDER_READ: &str = "modelProvider/read";
|
||||
pub const METHOD_MODEL_PROVIDER_CREATE: &str = "modelProvider/create";
|
||||
pub const METHOD_MODEL_PROVIDER_UPDATE: &str = "modelProvider/update";
|
||||
pub const METHOD_MODEL_PROVIDER_DELETE: &str = "modelProvider/delete";
|
||||
pub const METHOD_MODEL_PROVIDER_SORT_ORDERS_UPDATE: &str = "modelProvider/sortOrders/update";
|
||||
pub const METHOD_MODEL_PROVIDER_CONFIG_EXPORT: &str = "modelProviderConfig/export";
|
||||
pub const METHOD_MODEL_PROVIDER_CONFIG_IMPORT: &str = "modelProviderConfig/import";
|
||||
pub const METHOD_MODEL_PROVIDER_TEST_CONNECTION: &str = "modelProvider/testConnection";
|
||||
pub const METHOD_MODEL_PROVIDER_TEST_CHAT: &str = "modelProvider/testChat";
|
||||
pub const METHOD_MODEL_PROVIDER_FETCH_MODELS: &str = "modelProvider/fetchModels";
|
||||
pub const METHOD_MODEL_PROVIDER_KEY_CREATE: &str = "modelProviderKey/create";
|
||||
pub const METHOD_MODEL_PROVIDER_KEY_UPDATE: &str = "modelProviderKey/update";
|
||||
pub const METHOD_MODEL_PROVIDER_KEY_DELETE: &str = "modelProviderKey/delete";
|
||||
pub const METHOD_MODEL_PROVIDER_KEY_NEXT: &str = "modelProviderKey/next";
|
||||
pub const METHOD_MODEL_PROVIDER_KEY_USAGE_RECORD: &str = "modelProviderKey/usage/record";
|
||||
pub const METHOD_MODEL_PROVIDER_KEY_ERROR_RECORD: &str = "modelProviderKey/error/record";
|
||||
pub const METHOD_MODEL_PROVIDER_UI_STATE_READ: &str = "modelProviderUiState/read";
|
||||
pub const METHOD_MODEL_PROVIDER_UI_STATE_WRITE: &str = "modelProviderUiState/write";
|
||||
pub const METHOD_MODEL_PROVIDER_ALIAS_READ: &str = "modelProviderAlias/read";
|
||||
pub const METHOD_MODEL_PROVIDER_ALIAS_LIST: &str = "modelProviderAlias/list";
|
||||
pub const METHOD_CONNECT_DEEP_LINK_RESOLVE: &str = "connectDeepLink/resolve";
|
||||
@@ -267,10 +356,78 @@ pub const APP_SERVER_METHODS: &[AppServerMethodSpec] = &[
|
||||
method: METHOD_KNOWLEDGE_PACK_LIST,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_AUTOMATION_SCHEDULER_CONFIG_READ,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_AUTOMATION_SCHEDULER_CONFIG_UPDATE,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_AUTOMATION_SCHEDULER_STATUS,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_AUTOMATION_JOB_LIST,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_AUTOMATION_JOB_READ,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_AUTOMATION_JOB_CREATE,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_AUTOMATION_JOB_UPDATE,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_AUTOMATION_JOB_DELETE,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_AUTOMATION_JOB_RUN_NOW,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_AUTOMATION_JOB_HEALTH,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_AUTOMATION_JOB_RUN_HISTORY,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_AUTOMATION_SCHEDULE_PREVIEW,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_AUTOMATION_SCHEDULE_VALIDATE,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_MCP_SERVER_LIST,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_MCP_SERVER_STATUS_LIST,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_MCP_TOOL_LIST,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_MCP_PROMPT_LIST,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_MCP_RESOURCE_LIST,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_PROJECT_MEMORY_READ,
|
||||
kind: AppServerMethodKind::Request,
|
||||
@@ -295,6 +452,78 @@ pub const APP_SERVER_METHODS: &[AppServerMethodSpec] = &[
|
||||
method: METHOD_MODEL_PROVIDER_CATALOG_LIST,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_MODEL_PROVIDER_READ,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_MODEL_PROVIDER_CREATE,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_MODEL_PROVIDER_UPDATE,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_MODEL_PROVIDER_DELETE,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_MODEL_PROVIDER_SORT_ORDERS_UPDATE,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_MODEL_PROVIDER_CONFIG_EXPORT,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_MODEL_PROVIDER_CONFIG_IMPORT,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_MODEL_PROVIDER_TEST_CONNECTION,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_MODEL_PROVIDER_TEST_CHAT,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_MODEL_PROVIDER_FETCH_MODELS,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_MODEL_PROVIDER_KEY_CREATE,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_MODEL_PROVIDER_KEY_UPDATE,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_MODEL_PROVIDER_KEY_DELETE,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_MODEL_PROVIDER_KEY_NEXT,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_MODEL_PROVIDER_KEY_USAGE_RECORD,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_MODEL_PROVIDER_KEY_ERROR_RECORD,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_MODEL_PROVIDER_UI_STATE_READ,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_MODEL_PROVIDER_UI_STATE_WRITE,
|
||||
kind: AppServerMethodKind::Request,
|
||||
},
|
||||
AppServerMethodSpec {
|
||||
method: METHOD_MODEL_PROVIDER_ALIAS_READ,
|
||||
kind: AppServerMethodKind::Request,
|
||||
@@ -639,6 +868,41 @@ pub struct EvidencePackArtifact {
|
||||
pub bytes: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct McpServerListResponse {
|
||||
#[serde(default)]
|
||||
pub servers: Vec<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct McpServerStatusListResponse {
|
||||
#[serde(default)]
|
||||
pub servers: Vec<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct McpToolListResponse {
|
||||
#[serde(default)]
|
||||
pub tools: Vec<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct McpPromptListResponse {
|
||||
#[serde(default)]
|
||||
pub prompts: Vec<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct McpResourceListResponse {
|
||||
#[serde(default)]
|
||||
pub resources: Vec<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AgentSessionListParams {
|
||||
@@ -886,6 +1150,30 @@ pub struct KnowledgeListPacksResponse {
|
||||
pub packs: Vec<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AutomationSchedulerConfigReadResponse {
|
||||
pub config: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AutomationSchedulerConfigUpdateParams {
|
||||
pub config: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AutomationSchedulerConfigUpdateResponse {
|
||||
pub config: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AutomationSchedulerStatusResponse {
|
||||
pub status: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AutomationJobListResponse {
|
||||
@@ -893,6 +1181,99 @@ pub struct AutomationJobListResponse {
|
||||
pub jobs: Vec<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AutomationJobIdParams {
|
||||
pub id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AutomationJobReadResponse {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub job: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AutomationJobCreateParams {
|
||||
pub request: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AutomationJobWriteResponse {
|
||||
pub job: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AutomationJobUpdateParams {
|
||||
pub id: String,
|
||||
pub request: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AutomationJobDeleteResponse {
|
||||
pub deleted: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AutomationJobRunNowResponse {
|
||||
pub result: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AutomationJobHealthParams {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub query: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AutomationJobHealthResponse {
|
||||
pub health: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AutomationJobRunHistoryParams {
|
||||
pub id: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub limit: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AutomationJobRunHistoryResponse {
|
||||
#[serde(default)]
|
||||
pub runs: Vec<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AutomationScheduleParams {
|
||||
pub schedule: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AutomationSchedulePreviewResponse {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub next_run_at: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AutomationScheduleValidateResponse {
|
||||
pub valid: bool,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ProjectMemoryReadParams {
|
||||
@@ -948,6 +1329,245 @@ pub struct ModelProviderCatalogListResponse {
|
||||
pub providers: Vec<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderReadParams {
|
||||
pub provider_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderReadResponse {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub provider: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderCreateParams {
|
||||
pub provider: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderWriteResponse {
|
||||
pub provider: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderUpdateParams {
|
||||
pub provider_id: String,
|
||||
pub patch: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderDeleteParams {
|
||||
pub provider_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderDeleteResponse {
|
||||
pub deleted: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderSortOrderItem {
|
||||
pub provider_id: String,
|
||||
pub sort_order: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderSortOrdersUpdateParams {
|
||||
#[serde(default)]
|
||||
pub sort_orders: Vec<ModelProviderSortOrderItem>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderMutationResponse {}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderConfigExportParams {
|
||||
#[serde(default)]
|
||||
pub include_keys: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderConfigExportResponse {
|
||||
pub config_json: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderConfigImportParams {
|
||||
pub config_json: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderConfigImportResponse {
|
||||
pub success: bool,
|
||||
pub imported_providers: usize,
|
||||
pub imported_api_keys: usize,
|
||||
pub skipped_providers: usize,
|
||||
#[serde(default)]
|
||||
pub errors: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderTestConnectionParams {
|
||||
pub provider_id: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub model_name: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderTestConnectionResponse {
|
||||
pub success: bool,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub latency_ms: Option<u64>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub error: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub models: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderTestChatParams {
|
||||
pub provider_id: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub model_name: Option<String>,
|
||||
pub prompt: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderTestChatResponse {
|
||||
pub success: bool,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub latency_ms: Option<u64>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub error: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub content: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub raw: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderFetchModelsParams {
|
||||
pub provider_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderFetchModelsResponse {
|
||||
#[serde(default)]
|
||||
pub models: Vec<serde_json::Value>,
|
||||
pub source: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub error: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub request_url: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub diagnostic_hint: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub error_kind: Option<String>,
|
||||
pub should_prompt_error: bool,
|
||||
pub from_cache: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderKeyCreateParams {
|
||||
pub provider_id: String,
|
||||
pub api_key: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub alias: Option<String>,
|
||||
#[serde(default)]
|
||||
pub replace_existing: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderKeyWriteResponse {
|
||||
pub key: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderKeyUpdateParams {
|
||||
pub key_id: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub enabled: Option<bool>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub alias: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderKeyDeleteParams {
|
||||
pub key_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderKeyDeleteResponse {
|
||||
pub deleted: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderKeyNextParams {
|
||||
pub provider_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderKeyNextResponse {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub api_key: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub key_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderKeyEventParams {
|
||||
pub key_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderUiStateReadParams {
|
||||
pub key: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderUiStateReadResponse {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub value: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderUiStateWriteParams {
|
||||
pub key: String,
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModelProviderAliasReadParams {
|
||||
@@ -1994,13 +2614,48 @@ mod tests {
|
||||
METHOD_AGENT_APP_UI_RUNTIME_STATUS,
|
||||
METHOD_AGENT_APP_UI_RUNTIME_STOP,
|
||||
METHOD_KNOWLEDGE_PACK_LIST,
|
||||
METHOD_AUTOMATION_SCHEDULER_CONFIG_READ,
|
||||
METHOD_AUTOMATION_SCHEDULER_CONFIG_UPDATE,
|
||||
METHOD_AUTOMATION_SCHEDULER_STATUS,
|
||||
METHOD_AUTOMATION_JOB_LIST,
|
||||
METHOD_AUTOMATION_JOB_READ,
|
||||
METHOD_AUTOMATION_JOB_CREATE,
|
||||
METHOD_AUTOMATION_JOB_UPDATE,
|
||||
METHOD_AUTOMATION_JOB_DELETE,
|
||||
METHOD_AUTOMATION_JOB_RUN_NOW,
|
||||
METHOD_AUTOMATION_JOB_HEALTH,
|
||||
METHOD_AUTOMATION_JOB_RUN_HISTORY,
|
||||
METHOD_AUTOMATION_SCHEDULE_PREVIEW,
|
||||
METHOD_AUTOMATION_SCHEDULE_VALIDATE,
|
||||
METHOD_MCP_SERVER_LIST,
|
||||
METHOD_MCP_SERVER_STATUS_LIST,
|
||||
METHOD_MCP_TOOL_LIST,
|
||||
METHOD_MCP_PROMPT_LIST,
|
||||
METHOD_MCP_RESOURCE_LIST,
|
||||
METHOD_PROJECT_MEMORY_READ,
|
||||
METHOD_MODEL_LIST,
|
||||
METHOD_MODEL_PREFERENCES_LIST,
|
||||
METHOD_MODEL_SYNC_STATE_READ,
|
||||
METHOD_MODEL_PROVIDER_LIST,
|
||||
METHOD_MODEL_PROVIDER_CATALOG_LIST,
|
||||
METHOD_MODEL_PROVIDER_READ,
|
||||
METHOD_MODEL_PROVIDER_CREATE,
|
||||
METHOD_MODEL_PROVIDER_UPDATE,
|
||||
METHOD_MODEL_PROVIDER_DELETE,
|
||||
METHOD_MODEL_PROVIDER_SORT_ORDERS_UPDATE,
|
||||
METHOD_MODEL_PROVIDER_CONFIG_EXPORT,
|
||||
METHOD_MODEL_PROVIDER_CONFIG_IMPORT,
|
||||
METHOD_MODEL_PROVIDER_TEST_CONNECTION,
|
||||
METHOD_MODEL_PROVIDER_TEST_CHAT,
|
||||
METHOD_MODEL_PROVIDER_FETCH_MODELS,
|
||||
METHOD_MODEL_PROVIDER_KEY_CREATE,
|
||||
METHOD_MODEL_PROVIDER_KEY_UPDATE,
|
||||
METHOD_MODEL_PROVIDER_KEY_DELETE,
|
||||
METHOD_MODEL_PROVIDER_KEY_NEXT,
|
||||
METHOD_MODEL_PROVIDER_KEY_USAGE_RECORD,
|
||||
METHOD_MODEL_PROVIDER_KEY_ERROR_RECORD,
|
||||
METHOD_MODEL_PROVIDER_UI_STATE_READ,
|
||||
METHOD_MODEL_PROVIDER_UI_STATE_WRITE,
|
||||
METHOD_MODEL_PROVIDER_ALIAS_READ,
|
||||
METHOD_MODEL_PROVIDER_ALIAS_LIST,
|
||||
METHOD_CONNECT_DEEP_LINK_RESOLVE,
|
||||
|
||||
@@ -33,7 +33,25 @@ use crate::ArtifactContentStatus;
|
||||
use crate::ArtifactReadParams;
|
||||
use crate::ArtifactReadResponse;
|
||||
use crate::ArtifactSummary;
|
||||
use crate::AutomationJobCreateParams;
|
||||
use crate::AutomationJobDeleteResponse;
|
||||
use crate::AutomationJobHealthParams;
|
||||
use crate::AutomationJobHealthResponse;
|
||||
use crate::AutomationJobIdParams;
|
||||
use crate::AutomationJobListResponse;
|
||||
use crate::AutomationJobReadResponse;
|
||||
use crate::AutomationJobRunHistoryParams;
|
||||
use crate::AutomationJobRunHistoryResponse;
|
||||
use crate::AutomationJobRunNowResponse;
|
||||
use crate::AutomationJobUpdateParams;
|
||||
use crate::AutomationJobWriteResponse;
|
||||
use crate::AutomationScheduleParams;
|
||||
use crate::AutomationSchedulePreviewResponse;
|
||||
use crate::AutomationScheduleValidateResponse;
|
||||
use crate::AutomationSchedulerConfigReadResponse;
|
||||
use crate::AutomationSchedulerConfigUpdateParams;
|
||||
use crate::AutomationSchedulerConfigUpdateResponse;
|
||||
use crate::AutomationSchedulerStatusResponse;
|
||||
use crate::BusinessObjectRef;
|
||||
use crate::CapabilityDescriptor;
|
||||
use crate::CapabilityListParams;
|
||||
@@ -69,6 +87,11 @@ use crate::JsonRpcRequest;
|
||||
use crate::JsonRpcResponse;
|
||||
use crate::KnowledgeListPacksParams;
|
||||
use crate::KnowledgeListPacksResponse;
|
||||
use crate::McpPromptListResponse;
|
||||
use crate::McpResourceListResponse;
|
||||
use crate::McpServerListResponse;
|
||||
use crate::McpServerStatusListResponse;
|
||||
use crate::McpToolListResponse;
|
||||
use crate::ModelListParams;
|
||||
use crate::ModelListResponse;
|
||||
use crate::ModelPreferencesListResponse;
|
||||
@@ -76,7 +99,38 @@ use crate::ModelProviderAliasListResponse;
|
||||
use crate::ModelProviderAliasReadParams;
|
||||
use crate::ModelProviderAliasReadResponse;
|
||||
use crate::ModelProviderCatalogListResponse;
|
||||
use crate::ModelProviderConfigExportParams;
|
||||
use crate::ModelProviderConfigExportResponse;
|
||||
use crate::ModelProviderConfigImportParams;
|
||||
use crate::ModelProviderConfigImportResponse;
|
||||
use crate::ModelProviderCreateParams;
|
||||
use crate::ModelProviderDeleteParams;
|
||||
use crate::ModelProviderDeleteResponse;
|
||||
use crate::ModelProviderFetchModelsParams;
|
||||
use crate::ModelProviderFetchModelsResponse;
|
||||
use crate::ModelProviderKeyCreateParams;
|
||||
use crate::ModelProviderKeyDeleteParams;
|
||||
use crate::ModelProviderKeyDeleteResponse;
|
||||
use crate::ModelProviderKeyEventParams;
|
||||
use crate::ModelProviderKeyNextParams;
|
||||
use crate::ModelProviderKeyNextResponse;
|
||||
use crate::ModelProviderKeyUpdateParams;
|
||||
use crate::ModelProviderKeyWriteResponse;
|
||||
use crate::ModelProviderListResponse;
|
||||
use crate::ModelProviderMutationResponse;
|
||||
use crate::ModelProviderReadParams;
|
||||
use crate::ModelProviderReadResponse;
|
||||
use crate::ModelProviderSortOrderItem;
|
||||
use crate::ModelProviderSortOrdersUpdateParams;
|
||||
use crate::ModelProviderTestChatParams;
|
||||
use crate::ModelProviderTestChatResponse;
|
||||
use crate::ModelProviderTestConnectionParams;
|
||||
use crate::ModelProviderTestConnectionResponse;
|
||||
use crate::ModelProviderUiStateReadParams;
|
||||
use crate::ModelProviderUiStateReadResponse;
|
||||
use crate::ModelProviderUiStateWriteParams;
|
||||
use crate::ModelProviderUpdateParams;
|
||||
use crate::ModelProviderWriteResponse;
|
||||
use crate::ModelSyncStateReadResponse;
|
||||
use crate::OpenDeepLinkPayload;
|
||||
use crate::PlatformInfo;
|
||||
@@ -284,7 +338,36 @@ fn v0_schemas() -> Vec<GeneratedJsonSchema> {
|
||||
typed_schema::<AgentAppUiRuntimeStatusResponse>("AgentAppUiRuntimeStatusResponse"),
|
||||
typed_schema::<KnowledgeListPacksParams>("KnowledgeListPacksParams"),
|
||||
typed_schema::<KnowledgeListPacksResponse>("KnowledgeListPacksResponse"),
|
||||
typed_schema::<AutomationSchedulerConfigReadResponse>(
|
||||
"AutomationSchedulerConfigReadResponse",
|
||||
),
|
||||
typed_schema::<AutomationSchedulerConfigUpdateParams>(
|
||||
"AutomationSchedulerConfigUpdateParams",
|
||||
),
|
||||
typed_schema::<AutomationSchedulerConfigUpdateResponse>(
|
||||
"AutomationSchedulerConfigUpdateResponse",
|
||||
),
|
||||
typed_schema::<AutomationSchedulerStatusResponse>("AutomationSchedulerStatusResponse"),
|
||||
typed_schema::<AutomationJobListResponse>("AutomationJobListResponse"),
|
||||
typed_schema::<AutomationJobIdParams>("AutomationJobIdParams"),
|
||||
typed_schema::<AutomationJobReadResponse>("AutomationJobReadResponse"),
|
||||
typed_schema::<AutomationJobCreateParams>("AutomationJobCreateParams"),
|
||||
typed_schema::<AutomationJobWriteResponse>("AutomationJobWriteResponse"),
|
||||
typed_schema::<AutomationJobUpdateParams>("AutomationJobUpdateParams"),
|
||||
typed_schema::<AutomationJobDeleteResponse>("AutomationJobDeleteResponse"),
|
||||
typed_schema::<AutomationJobRunNowResponse>("AutomationJobRunNowResponse"),
|
||||
typed_schema::<AutomationJobHealthParams>("AutomationJobHealthParams"),
|
||||
typed_schema::<AutomationJobHealthResponse>("AutomationJobHealthResponse"),
|
||||
typed_schema::<AutomationJobRunHistoryParams>("AutomationJobRunHistoryParams"),
|
||||
typed_schema::<AutomationJobRunHistoryResponse>("AutomationJobRunHistoryResponse"),
|
||||
typed_schema::<AutomationScheduleParams>("AutomationScheduleParams"),
|
||||
typed_schema::<AutomationSchedulePreviewResponse>("AutomationSchedulePreviewResponse"),
|
||||
typed_schema::<AutomationScheduleValidateResponse>("AutomationScheduleValidateResponse"),
|
||||
typed_schema::<McpServerListResponse>("McpServerListResponse"),
|
||||
typed_schema::<McpServerStatusListResponse>("McpServerStatusListResponse"),
|
||||
typed_schema::<McpToolListResponse>("McpToolListResponse"),
|
||||
typed_schema::<McpPromptListResponse>("McpPromptListResponse"),
|
||||
typed_schema::<McpResourceListResponse>("McpResourceListResponse"),
|
||||
typed_schema::<ProjectMemoryReadParams>("ProjectMemoryReadParams"),
|
||||
typed_schema::<ProjectMemoryReadResponse>("ProjectMemoryReadResponse"),
|
||||
typed_schema::<ModelListParams>("ModelListParams"),
|
||||
@@ -293,6 +376,37 @@ fn v0_schemas() -> Vec<GeneratedJsonSchema> {
|
||||
typed_schema::<ModelSyncStateReadResponse>("ModelSyncStateReadResponse"),
|
||||
typed_schema::<ModelProviderListResponse>("ModelProviderListResponse"),
|
||||
typed_schema::<ModelProviderCatalogListResponse>("ModelProviderCatalogListResponse"),
|
||||
typed_schema::<ModelProviderReadParams>("ModelProviderReadParams"),
|
||||
typed_schema::<ModelProviderReadResponse>("ModelProviderReadResponse"),
|
||||
typed_schema::<ModelProviderCreateParams>("ModelProviderCreateParams"),
|
||||
typed_schema::<ModelProviderWriteResponse>("ModelProviderWriteResponse"),
|
||||
typed_schema::<ModelProviderUpdateParams>("ModelProviderUpdateParams"),
|
||||
typed_schema::<ModelProviderDeleteParams>("ModelProviderDeleteParams"),
|
||||
typed_schema::<ModelProviderDeleteResponse>("ModelProviderDeleteResponse"),
|
||||
typed_schema::<ModelProviderSortOrderItem>("ModelProviderSortOrderItem"),
|
||||
typed_schema::<ModelProviderSortOrdersUpdateParams>("ModelProviderSortOrdersUpdateParams"),
|
||||
typed_schema::<ModelProviderMutationResponse>("ModelProviderMutationResponse"),
|
||||
typed_schema::<ModelProviderConfigExportParams>("ModelProviderConfigExportParams"),
|
||||
typed_schema::<ModelProviderConfigExportResponse>("ModelProviderConfigExportResponse"),
|
||||
typed_schema::<ModelProviderConfigImportParams>("ModelProviderConfigImportParams"),
|
||||
typed_schema::<ModelProviderConfigImportResponse>("ModelProviderConfigImportResponse"),
|
||||
typed_schema::<ModelProviderTestConnectionParams>("ModelProviderTestConnectionParams"),
|
||||
typed_schema::<ModelProviderTestConnectionResponse>("ModelProviderTestConnectionResponse"),
|
||||
typed_schema::<ModelProviderTestChatParams>("ModelProviderTestChatParams"),
|
||||
typed_schema::<ModelProviderTestChatResponse>("ModelProviderTestChatResponse"),
|
||||
typed_schema::<ModelProviderFetchModelsParams>("ModelProviderFetchModelsParams"),
|
||||
typed_schema::<ModelProviderFetchModelsResponse>("ModelProviderFetchModelsResponse"),
|
||||
typed_schema::<ModelProviderKeyCreateParams>("ModelProviderKeyCreateParams"),
|
||||
typed_schema::<ModelProviderKeyWriteResponse>("ModelProviderKeyWriteResponse"),
|
||||
typed_schema::<ModelProviderKeyUpdateParams>("ModelProviderKeyUpdateParams"),
|
||||
typed_schema::<ModelProviderKeyDeleteParams>("ModelProviderKeyDeleteParams"),
|
||||
typed_schema::<ModelProviderKeyDeleteResponse>("ModelProviderKeyDeleteResponse"),
|
||||
typed_schema::<ModelProviderKeyNextParams>("ModelProviderKeyNextParams"),
|
||||
typed_schema::<ModelProviderKeyNextResponse>("ModelProviderKeyNextResponse"),
|
||||
typed_schema::<ModelProviderKeyEventParams>("ModelProviderKeyEventParams"),
|
||||
typed_schema::<ModelProviderUiStateReadParams>("ModelProviderUiStateReadParams"),
|
||||
typed_schema::<ModelProviderUiStateReadResponse>("ModelProviderUiStateReadResponse"),
|
||||
typed_schema::<ModelProviderUiStateWriteParams>("ModelProviderUiStateWriteParams"),
|
||||
typed_schema::<ModelProviderAliasReadParams>("ModelProviderAliasReadParams"),
|
||||
typed_schema::<ModelProviderAliasReadResponse>("ModelProviderAliasReadResponse"),
|
||||
typed_schema::<ModelProviderAliasListResponse>("ModelProviderAliasListResponse"),
|
||||
|
||||
@@ -23,6 +23,8 @@ anyhow.workspace = true
|
||||
async-trait.workspace = true
|
||||
aster.workspace = true
|
||||
chrono.workspace = true
|
||||
chrono-tz.workspace = true
|
||||
cron.workspace = true
|
||||
app-server-protocol.workspace = true
|
||||
app-server-transport.workspace = true
|
||||
lime-core.workspace = true
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,13 @@ use app_server_protocol::AgentSessionEventParams;
|
||||
use app_server_protocol::AgentSessionListParams;
|
||||
use app_server_protocol::AgentSessionUpdateParams;
|
||||
use app_server_protocol::ArtifactReadParams;
|
||||
use app_server_protocol::AutomationJobCreateParams;
|
||||
use app_server_protocol::AutomationJobHealthParams;
|
||||
use app_server_protocol::AutomationJobIdParams;
|
||||
use app_server_protocol::AutomationJobRunHistoryParams;
|
||||
use app_server_protocol::AutomationJobUpdateParams;
|
||||
use app_server_protocol::AutomationScheduleParams;
|
||||
use app_server_protocol::AutomationSchedulerConfigUpdateParams;
|
||||
use app_server_protocol::CapabilityListParams;
|
||||
use app_server_protocol::ClientInfo;
|
||||
use app_server_protocol::ConnectCallbackSendParams;
|
||||
@@ -32,6 +39,23 @@ use app_server_protocol::JsonRpcResponse;
|
||||
use app_server_protocol::KnowledgeListPacksParams;
|
||||
use app_server_protocol::ModelListParams;
|
||||
use app_server_protocol::ModelProviderAliasReadParams;
|
||||
use app_server_protocol::ModelProviderConfigExportParams;
|
||||
use app_server_protocol::ModelProviderConfigImportParams;
|
||||
use app_server_protocol::ModelProviderCreateParams;
|
||||
use app_server_protocol::ModelProviderDeleteParams;
|
||||
use app_server_protocol::ModelProviderFetchModelsParams;
|
||||
use app_server_protocol::ModelProviderKeyCreateParams;
|
||||
use app_server_protocol::ModelProviderKeyDeleteParams;
|
||||
use app_server_protocol::ModelProviderKeyEventParams;
|
||||
use app_server_protocol::ModelProviderKeyNextParams;
|
||||
use app_server_protocol::ModelProviderKeyUpdateParams;
|
||||
use app_server_protocol::ModelProviderReadParams;
|
||||
use app_server_protocol::ModelProviderSortOrdersUpdateParams;
|
||||
use app_server_protocol::ModelProviderTestChatParams;
|
||||
use app_server_protocol::ModelProviderTestConnectionParams;
|
||||
use app_server_protocol::ModelProviderUiStateReadParams;
|
||||
use app_server_protocol::ModelProviderUiStateWriteParams;
|
||||
use app_server_protocol::ModelProviderUpdateParams;
|
||||
use app_server_protocol::PlatformInfo;
|
||||
use app_server_protocol::ProjectMemoryReadParams;
|
||||
use app_server_protocol::ServerCapabilities;
|
||||
@@ -56,7 +80,19 @@ use app_server_protocol::METHOD_AGENT_SESSION_TURN_CANCEL;
|
||||
use app_server_protocol::METHOD_AGENT_SESSION_TURN_START;
|
||||
use app_server_protocol::METHOD_AGENT_SESSION_UPDATE;
|
||||
use app_server_protocol::METHOD_ARTIFACT_READ;
|
||||
use app_server_protocol::METHOD_AUTOMATION_JOB_CREATE;
|
||||
use app_server_protocol::METHOD_AUTOMATION_JOB_DELETE;
|
||||
use app_server_protocol::METHOD_AUTOMATION_JOB_HEALTH;
|
||||
use app_server_protocol::METHOD_AUTOMATION_JOB_LIST;
|
||||
use app_server_protocol::METHOD_AUTOMATION_JOB_READ;
|
||||
use app_server_protocol::METHOD_AUTOMATION_JOB_RUN_HISTORY;
|
||||
use app_server_protocol::METHOD_AUTOMATION_JOB_RUN_NOW;
|
||||
use app_server_protocol::METHOD_AUTOMATION_JOB_UPDATE;
|
||||
use app_server_protocol::METHOD_AUTOMATION_SCHEDULER_CONFIG_READ;
|
||||
use app_server_protocol::METHOD_AUTOMATION_SCHEDULER_CONFIG_UPDATE;
|
||||
use app_server_protocol::METHOD_AUTOMATION_SCHEDULER_STATUS;
|
||||
use app_server_protocol::METHOD_AUTOMATION_SCHEDULE_PREVIEW;
|
||||
use app_server_protocol::METHOD_AUTOMATION_SCHEDULE_VALIDATE;
|
||||
use app_server_protocol::METHOD_CAPABILITY_LIST;
|
||||
use app_server_protocol::METHOD_CONNECT_CALLBACK_SEND;
|
||||
use app_server_protocol::METHOD_CONNECT_DEEP_LINK_RESOLVE;
|
||||
@@ -68,12 +104,35 @@ use app_server_protocol::METHOD_FILE_SYSTEM_READ_FILE_PREVIEW;
|
||||
use app_server_protocol::METHOD_INITIALIZE;
|
||||
use app_server_protocol::METHOD_INITIALIZED;
|
||||
use app_server_protocol::METHOD_KNOWLEDGE_PACK_LIST;
|
||||
use app_server_protocol::METHOD_MCP_PROMPT_LIST;
|
||||
use app_server_protocol::METHOD_MCP_RESOURCE_LIST;
|
||||
use app_server_protocol::METHOD_MCP_SERVER_LIST;
|
||||
use app_server_protocol::METHOD_MCP_SERVER_STATUS_LIST;
|
||||
use app_server_protocol::METHOD_MCP_TOOL_LIST;
|
||||
use app_server_protocol::METHOD_MODEL_LIST;
|
||||
use app_server_protocol::METHOD_MODEL_PREFERENCES_LIST;
|
||||
use app_server_protocol::METHOD_MODEL_PROVIDER_ALIAS_LIST;
|
||||
use app_server_protocol::METHOD_MODEL_PROVIDER_ALIAS_READ;
|
||||
use app_server_protocol::METHOD_MODEL_PROVIDER_CATALOG_LIST;
|
||||
use app_server_protocol::METHOD_MODEL_PROVIDER_CONFIG_EXPORT;
|
||||
use app_server_protocol::METHOD_MODEL_PROVIDER_CONFIG_IMPORT;
|
||||
use app_server_protocol::METHOD_MODEL_PROVIDER_CREATE;
|
||||
use app_server_protocol::METHOD_MODEL_PROVIDER_DELETE;
|
||||
use app_server_protocol::METHOD_MODEL_PROVIDER_FETCH_MODELS;
|
||||
use app_server_protocol::METHOD_MODEL_PROVIDER_KEY_CREATE;
|
||||
use app_server_protocol::METHOD_MODEL_PROVIDER_KEY_DELETE;
|
||||
use app_server_protocol::METHOD_MODEL_PROVIDER_KEY_ERROR_RECORD;
|
||||
use app_server_protocol::METHOD_MODEL_PROVIDER_KEY_NEXT;
|
||||
use app_server_protocol::METHOD_MODEL_PROVIDER_KEY_UPDATE;
|
||||
use app_server_protocol::METHOD_MODEL_PROVIDER_KEY_USAGE_RECORD;
|
||||
use app_server_protocol::METHOD_MODEL_PROVIDER_LIST;
|
||||
use app_server_protocol::METHOD_MODEL_PROVIDER_READ;
|
||||
use app_server_protocol::METHOD_MODEL_PROVIDER_SORT_ORDERS_UPDATE;
|
||||
use app_server_protocol::METHOD_MODEL_PROVIDER_TEST_CHAT;
|
||||
use app_server_protocol::METHOD_MODEL_PROVIDER_TEST_CONNECTION;
|
||||
use app_server_protocol::METHOD_MODEL_PROVIDER_UI_STATE_READ;
|
||||
use app_server_protocol::METHOD_MODEL_PROVIDER_UI_STATE_WRITE;
|
||||
use app_server_protocol::METHOD_MODEL_PROVIDER_UPDATE;
|
||||
use app_server_protocol::METHOD_MODEL_SYNC_STATE_READ;
|
||||
use app_server_protocol::METHOD_PROJECT_MEMORY_READ;
|
||||
use app_server_protocol::METHOD_SKILL_LIST;
|
||||
@@ -184,13 +243,76 @@ impl RequestProcessor {
|
||||
}
|
||||
METHOD_AGENT_APP_UI_RUNTIME_STOP => self.handle_agent_app_ui_runtime_stop(params).await,
|
||||
METHOD_KNOWLEDGE_PACK_LIST => self.handle_knowledge_pack_list(params).await,
|
||||
METHOD_AUTOMATION_SCHEDULER_CONFIG_READ => {
|
||||
self.handle_automation_scheduler_config_read().await
|
||||
}
|
||||
METHOD_AUTOMATION_SCHEDULER_CONFIG_UPDATE => {
|
||||
self.handle_automation_scheduler_config_update(params).await
|
||||
}
|
||||
METHOD_AUTOMATION_SCHEDULER_STATUS => self.handle_automation_scheduler_status().await,
|
||||
METHOD_AUTOMATION_JOB_LIST => self.handle_automation_job_list().await,
|
||||
METHOD_AUTOMATION_JOB_READ => self.handle_automation_job_read(params).await,
|
||||
METHOD_AUTOMATION_JOB_CREATE => self.handle_automation_job_create(params).await,
|
||||
METHOD_AUTOMATION_JOB_UPDATE => self.handle_automation_job_update(params).await,
|
||||
METHOD_AUTOMATION_JOB_DELETE => self.handle_automation_job_delete(params).await,
|
||||
METHOD_AUTOMATION_JOB_RUN_NOW => self.handle_automation_job_run_now(params).await,
|
||||
METHOD_AUTOMATION_JOB_HEALTH => self.handle_automation_job_health(params).await,
|
||||
METHOD_AUTOMATION_JOB_RUN_HISTORY => {
|
||||
self.handle_automation_job_run_history(params).await
|
||||
}
|
||||
METHOD_AUTOMATION_SCHEDULE_PREVIEW => {
|
||||
self.handle_automation_schedule_preview(params).await
|
||||
}
|
||||
METHOD_AUTOMATION_SCHEDULE_VALIDATE => {
|
||||
self.handle_automation_schedule_validate(params).await
|
||||
}
|
||||
METHOD_MCP_SERVER_LIST => self.handle_mcp_server_list().await,
|
||||
METHOD_MCP_SERVER_STATUS_LIST => self.handle_mcp_server_status_list().await,
|
||||
METHOD_MCP_TOOL_LIST => self.handle_mcp_tool_list().await,
|
||||
METHOD_MCP_PROMPT_LIST => self.handle_mcp_prompt_list().await,
|
||||
METHOD_MCP_RESOURCE_LIST => self.handle_mcp_resource_list().await,
|
||||
METHOD_PROJECT_MEMORY_READ => self.handle_project_memory_read(params).await,
|
||||
METHOD_MODEL_LIST => self.handle_model_list(params).await,
|
||||
METHOD_MODEL_PREFERENCES_LIST => self.handle_model_preferences_list().await,
|
||||
METHOD_MODEL_SYNC_STATE_READ => self.handle_model_sync_state_read().await,
|
||||
METHOD_MODEL_PROVIDER_LIST => self.handle_model_provider_list().await,
|
||||
METHOD_MODEL_PROVIDER_CATALOG_LIST => self.handle_model_provider_catalog_list().await,
|
||||
METHOD_MODEL_PROVIDER_READ => self.handle_model_provider_read(params).await,
|
||||
METHOD_MODEL_PROVIDER_CREATE => self.handle_model_provider_create(params).await,
|
||||
METHOD_MODEL_PROVIDER_UPDATE => self.handle_model_provider_update(params).await,
|
||||
METHOD_MODEL_PROVIDER_DELETE => self.handle_model_provider_delete(params).await,
|
||||
METHOD_MODEL_PROVIDER_SORT_ORDERS_UPDATE => {
|
||||
self.handle_model_provider_sort_orders_update(params).await
|
||||
}
|
||||
METHOD_MODEL_PROVIDER_CONFIG_EXPORT => {
|
||||
self.handle_model_provider_config_export(params).await
|
||||
}
|
||||
METHOD_MODEL_PROVIDER_CONFIG_IMPORT => {
|
||||
self.handle_model_provider_config_import(params).await
|
||||
}
|
||||
METHOD_MODEL_PROVIDER_TEST_CONNECTION => {
|
||||
self.handle_model_provider_test_connection(params).await
|
||||
}
|
||||
METHOD_MODEL_PROVIDER_TEST_CHAT => self.handle_model_provider_test_chat(params).await,
|
||||
METHOD_MODEL_PROVIDER_FETCH_MODELS => {
|
||||
self.handle_model_provider_fetch_models(params).await
|
||||
}
|
||||
METHOD_MODEL_PROVIDER_KEY_CREATE => self.handle_model_provider_key_create(params).await,
|
||||
METHOD_MODEL_PROVIDER_KEY_UPDATE => self.handle_model_provider_key_update(params).await,
|
||||
METHOD_MODEL_PROVIDER_KEY_DELETE => self.handle_model_provider_key_delete(params).await,
|
||||
METHOD_MODEL_PROVIDER_KEY_NEXT => self.handle_model_provider_key_next(params).await,
|
||||
METHOD_MODEL_PROVIDER_KEY_USAGE_RECORD => {
|
||||
self.handle_model_provider_key_usage_record(params).await
|
||||
}
|
||||
METHOD_MODEL_PROVIDER_KEY_ERROR_RECORD => {
|
||||
self.handle_model_provider_key_error_record(params).await
|
||||
}
|
||||
METHOD_MODEL_PROVIDER_UI_STATE_READ => {
|
||||
self.handle_model_provider_ui_state_read(params).await
|
||||
}
|
||||
METHOD_MODEL_PROVIDER_UI_STATE_WRITE => {
|
||||
self.handle_model_provider_ui_state_write(params).await
|
||||
}
|
||||
METHOD_MODEL_PROVIDER_ALIAS_READ => self.handle_model_provider_alias_read(params).await,
|
||||
METHOD_MODEL_PROVIDER_ALIAS_LIST => self.handle_model_provider_alias_list().await,
|
||||
METHOD_CONNECT_DEEP_LINK_RESOLVE => self.handle_connect_deep_link_resolve(params).await,
|
||||
@@ -528,6 +650,166 @@ impl RequestProcessor {
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_automation_scheduler_config_read(&self) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let response = self
|
||||
.runtime
|
||||
.read_automation_scheduler_config()
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_automation_scheduler_config_update(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let params: AutomationSchedulerConfigUpdateParams = parse_params(params)?;
|
||||
let response = self
|
||||
.runtime
|
||||
.update_automation_scheduler_config(params)
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_automation_scheduler_status(&self) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let response = self
|
||||
.runtime
|
||||
.read_automation_scheduler_status()
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_automation_job_read(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let params: AutomationJobIdParams = parse_params(params)?;
|
||||
let response = self
|
||||
.runtime
|
||||
.read_automation_job(params)
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_automation_job_create(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let params: AutomationJobCreateParams = parse_params(params)?;
|
||||
let response = self
|
||||
.runtime
|
||||
.create_automation_job(params)
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_automation_job_update(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let params: AutomationJobUpdateParams = parse_params(params)?;
|
||||
let response = self
|
||||
.runtime
|
||||
.update_automation_job(params)
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_automation_job_delete(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let params: AutomationJobIdParams = parse_params(params)?;
|
||||
let response = self
|
||||
.runtime
|
||||
.delete_automation_job(params)
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_automation_job_run_now(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let params: AutomationJobIdParams = parse_params(params)?;
|
||||
let response = self
|
||||
.runtime
|
||||
.run_automation_job_now(params)
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_automation_job_health(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let params: AutomationJobHealthParams = parse_params(params)?;
|
||||
let response = self
|
||||
.runtime
|
||||
.read_automation_health(params)
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_automation_job_run_history(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let params: AutomationJobRunHistoryParams = parse_params(params)?;
|
||||
let response = self
|
||||
.runtime
|
||||
.read_automation_run_history(params)
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_automation_schedule_preview(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let params: AutomationScheduleParams = parse_params(params)?;
|
||||
let response = self
|
||||
.runtime
|
||||
.preview_automation_schedule(params)
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_automation_schedule_validate(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let params: AutomationScheduleParams = parse_params(params)?;
|
||||
let response = self
|
||||
.runtime
|
||||
.validate_automation_schedule(params)
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_project_memory_read(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
@@ -542,6 +824,56 @@ impl RequestProcessor {
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_mcp_server_list(&self) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let response = self
|
||||
.runtime
|
||||
.list_mcp_servers()
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_mcp_server_status_list(&self) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let response = self
|
||||
.runtime
|
||||
.list_mcp_servers_with_status()
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_mcp_tool_list(&self) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let response = self
|
||||
.runtime
|
||||
.list_mcp_tools()
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_mcp_prompt_list(&self) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let response = self
|
||||
.runtime
|
||||
.list_mcp_prompts()
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_mcp_resource_list(&self) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let response = self
|
||||
.runtime
|
||||
.list_mcp_resources()
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_model_list(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
@@ -596,6 +928,258 @@ impl RequestProcessor {
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_model_provider_read(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let params: ModelProviderReadParams = parse_params(params)?;
|
||||
let response = self
|
||||
.runtime
|
||||
.read_model_provider(params)
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_model_provider_create(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let params: ModelProviderCreateParams = parse_params(params)?;
|
||||
let response = self
|
||||
.runtime
|
||||
.create_model_provider(params)
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_model_provider_update(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let params: ModelProviderUpdateParams = parse_params(params)?;
|
||||
let response = self
|
||||
.runtime
|
||||
.update_model_provider(params)
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_model_provider_delete(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let params: ModelProviderDeleteParams = parse_params(params)?;
|
||||
let response = self
|
||||
.runtime
|
||||
.delete_model_provider(params)
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_model_provider_sort_orders_update(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let params: ModelProviderSortOrdersUpdateParams = parse_params(params)?;
|
||||
let response = self
|
||||
.runtime
|
||||
.update_model_provider_sort_orders(params)
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_model_provider_config_export(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let params: ModelProviderConfigExportParams = parse_params(params)?;
|
||||
let response = self
|
||||
.runtime
|
||||
.export_model_provider_config(params)
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_model_provider_config_import(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let params: ModelProviderConfigImportParams = parse_params(params)?;
|
||||
let response = self
|
||||
.runtime
|
||||
.import_model_provider_config(params)
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_model_provider_test_connection(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let params: ModelProviderTestConnectionParams = parse_params(params)?;
|
||||
let response = self
|
||||
.runtime
|
||||
.test_model_provider_connection(params)
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_model_provider_test_chat(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let params: ModelProviderTestChatParams = parse_params(params)?;
|
||||
let response = self
|
||||
.runtime
|
||||
.test_model_provider_chat(params)
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_model_provider_fetch_models(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let params: ModelProviderFetchModelsParams = parse_params(params)?;
|
||||
let response = self
|
||||
.runtime
|
||||
.fetch_model_provider_models(params)
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_model_provider_key_create(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let params: ModelProviderKeyCreateParams = parse_params(params)?;
|
||||
let response = self
|
||||
.runtime
|
||||
.create_model_provider_key(params)
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_model_provider_key_update(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let params: ModelProviderKeyUpdateParams = parse_params(params)?;
|
||||
let response = self
|
||||
.runtime
|
||||
.update_model_provider_key(params)
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_model_provider_key_delete(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let params: ModelProviderKeyDeleteParams = parse_params(params)?;
|
||||
let response = self
|
||||
.runtime
|
||||
.delete_model_provider_key(params)
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_model_provider_key_next(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let params: ModelProviderKeyNextParams = parse_params(params)?;
|
||||
let response = self
|
||||
.runtime
|
||||
.read_next_model_provider_key(params)
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_model_provider_key_usage_record(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let params: ModelProviderKeyEventParams = parse_params(params)?;
|
||||
let response = self
|
||||
.runtime
|
||||
.record_model_provider_key_usage(params)
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_model_provider_key_error_record(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let params: ModelProviderKeyEventParams = parse_params(params)?;
|
||||
let response = self
|
||||
.runtime
|
||||
.record_model_provider_key_error(params)
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_model_provider_ui_state_read(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let params: ModelProviderUiStateReadParams = parse_params(params)?;
|
||||
let response = self
|
||||
.runtime
|
||||
.read_model_provider_ui_state(params)
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_model_provider_ui_state_write(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<RpcDispatch, JsonRpcError> {
|
||||
self.ensure_initialized()?;
|
||||
let params: ModelProviderUiStateWriteParams = parse_params(params)?;
|
||||
let response = self
|
||||
.runtime
|
||||
.write_model_provider_ui_state(params)
|
||||
.await
|
||||
.map_err(to_jsonrpc_error)?;
|
||||
dispatch_result(response)
|
||||
}
|
||||
|
||||
async fn handle_model_provider_alias_read(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
@@ -1232,6 +1816,72 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn mcp_list_methods_require_initialized_and_return_current_empty_state() {
|
||||
let processor = RequestProcessor::new(RuntimeCore::default());
|
||||
let blocked = processor
|
||||
.handle_request(JsonRpcRequest::new(
|
||||
RequestId::Integer(1),
|
||||
METHOD_MCP_TOOL_LIST,
|
||||
Some(json!({})),
|
||||
))
|
||||
.await
|
||||
.expect("blocked response");
|
||||
assert!(matches!(
|
||||
&blocked[0],
|
||||
JsonRpcMessage::Error(error) if error.error.code == error_codes::NOT_INITIALIZED
|
||||
));
|
||||
|
||||
processor
|
||||
.handle_request(JsonRpcRequest::new(
|
||||
RequestId::Integer(2),
|
||||
METHOD_INITIALIZE,
|
||||
Some(
|
||||
serde_json::to_value(InitializeParams {
|
||||
client_info: ClientInfo {
|
||||
name: "test-client".to_string(),
|
||||
title: None,
|
||||
version: None,
|
||||
},
|
||||
capabilities: ClientCapabilities::default(),
|
||||
})
|
||||
.expect("initialize params"),
|
||||
),
|
||||
))
|
||||
.await
|
||||
.expect("initialize");
|
||||
processor.handle_notification(JsonRpcNotification::new(
|
||||
METHOD_INITIALIZED,
|
||||
Some(json!({})),
|
||||
));
|
||||
|
||||
let cases = [
|
||||
(RequestId::Integer(3), METHOD_MCP_SERVER_LIST, "servers"),
|
||||
(
|
||||
RequestId::Integer(4),
|
||||
METHOD_MCP_SERVER_STATUS_LIST,
|
||||
"servers",
|
||||
),
|
||||
(RequestId::Integer(5), METHOD_MCP_TOOL_LIST, "tools"),
|
||||
(RequestId::Integer(6), METHOD_MCP_PROMPT_LIST, "prompts"),
|
||||
(RequestId::Integer(7), METHOD_MCP_RESOURCE_LIST, "resources"),
|
||||
];
|
||||
|
||||
for (id, method, field) in cases {
|
||||
let messages = processor
|
||||
.handle_request(JsonRpcRequest::new(id, method, Some(json!({}))))
|
||||
.await
|
||||
.expect("mcp list response");
|
||||
|
||||
match &messages[0] {
|
||||
JsonRpcMessage::Response(response) => {
|
||||
assert_eq!(response.result[field], json!([]));
|
||||
}
|
||||
other => panic!("expected response, got {other:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn evidence_export_requires_initialized_and_returns_read_model_snapshot() {
|
||||
let runtime = RuntimeCore::default();
|
||||
|
||||
@@ -35,7 +35,25 @@ use app_server_protocol::ArtifactContentStatus;
|
||||
use app_server_protocol::ArtifactReadParams;
|
||||
use app_server_protocol::ArtifactReadResponse;
|
||||
use app_server_protocol::ArtifactSummary;
|
||||
use app_server_protocol::AutomationJobCreateParams;
|
||||
use app_server_protocol::AutomationJobDeleteResponse;
|
||||
use app_server_protocol::AutomationJobHealthParams;
|
||||
use app_server_protocol::AutomationJobHealthResponse;
|
||||
use app_server_protocol::AutomationJobIdParams;
|
||||
use app_server_protocol::AutomationJobListResponse;
|
||||
use app_server_protocol::AutomationJobReadResponse;
|
||||
use app_server_protocol::AutomationJobRunHistoryParams;
|
||||
use app_server_protocol::AutomationJobRunHistoryResponse;
|
||||
use app_server_protocol::AutomationJobRunNowResponse;
|
||||
use app_server_protocol::AutomationJobUpdateParams;
|
||||
use app_server_protocol::AutomationJobWriteResponse;
|
||||
use app_server_protocol::AutomationScheduleParams;
|
||||
use app_server_protocol::AutomationSchedulePreviewResponse;
|
||||
use app_server_protocol::AutomationScheduleValidateResponse;
|
||||
use app_server_protocol::AutomationSchedulerConfigReadResponse;
|
||||
use app_server_protocol::AutomationSchedulerConfigUpdateParams;
|
||||
use app_server_protocol::AutomationSchedulerConfigUpdateResponse;
|
||||
use app_server_protocol::AutomationSchedulerStatusResponse;
|
||||
use app_server_protocol::CapabilityListParams;
|
||||
use app_server_protocol::CapabilityListResponse;
|
||||
use app_server_protocol::ClientInfo;
|
||||
@@ -58,6 +76,11 @@ use app_server_protocol::FileSystemReadFilePreviewParams;
|
||||
use app_server_protocol::JsonRpcError;
|
||||
use app_server_protocol::KnowledgeListPacksParams;
|
||||
use app_server_protocol::KnowledgeListPacksResponse;
|
||||
use app_server_protocol::McpPromptListResponse;
|
||||
use app_server_protocol::McpResourceListResponse;
|
||||
use app_server_protocol::McpServerListResponse;
|
||||
use app_server_protocol::McpServerStatusListResponse;
|
||||
use app_server_protocol::McpToolListResponse;
|
||||
use app_server_protocol::ModelListParams;
|
||||
use app_server_protocol::ModelListResponse;
|
||||
use app_server_protocol::ModelPreferencesListResponse;
|
||||
@@ -65,7 +88,37 @@ use app_server_protocol::ModelProviderAliasListResponse;
|
||||
use app_server_protocol::ModelProviderAliasReadParams;
|
||||
use app_server_protocol::ModelProviderAliasReadResponse;
|
||||
use app_server_protocol::ModelProviderCatalogListResponse;
|
||||
use app_server_protocol::ModelProviderConfigExportParams;
|
||||
use app_server_protocol::ModelProviderConfigExportResponse;
|
||||
use app_server_protocol::ModelProviderConfigImportParams;
|
||||
use app_server_protocol::ModelProviderConfigImportResponse;
|
||||
use app_server_protocol::ModelProviderCreateParams;
|
||||
use app_server_protocol::ModelProviderDeleteParams;
|
||||
use app_server_protocol::ModelProviderDeleteResponse;
|
||||
use app_server_protocol::ModelProviderFetchModelsParams;
|
||||
use app_server_protocol::ModelProviderFetchModelsResponse;
|
||||
use app_server_protocol::ModelProviderKeyCreateParams;
|
||||
use app_server_protocol::ModelProviderKeyDeleteParams;
|
||||
use app_server_protocol::ModelProviderKeyDeleteResponse;
|
||||
use app_server_protocol::ModelProviderKeyEventParams;
|
||||
use app_server_protocol::ModelProviderKeyNextParams;
|
||||
use app_server_protocol::ModelProviderKeyNextResponse;
|
||||
use app_server_protocol::ModelProviderKeyUpdateParams;
|
||||
use app_server_protocol::ModelProviderKeyWriteResponse;
|
||||
use app_server_protocol::ModelProviderListResponse;
|
||||
use app_server_protocol::ModelProviderMutationResponse;
|
||||
use app_server_protocol::ModelProviderReadParams;
|
||||
use app_server_protocol::ModelProviderReadResponse;
|
||||
use app_server_protocol::ModelProviderSortOrdersUpdateParams;
|
||||
use app_server_protocol::ModelProviderTestChatParams;
|
||||
use app_server_protocol::ModelProviderTestChatResponse;
|
||||
use app_server_protocol::ModelProviderTestConnectionParams;
|
||||
use app_server_protocol::ModelProviderTestConnectionResponse;
|
||||
use app_server_protocol::ModelProviderUiStateReadParams;
|
||||
use app_server_protocol::ModelProviderUiStateReadResponse;
|
||||
use app_server_protocol::ModelProviderUiStateWriteParams;
|
||||
use app_server_protocol::ModelProviderUpdateParams;
|
||||
use app_server_protocol::ModelProviderWriteResponse;
|
||||
use app_server_protocol::ModelSyncStateReadResponse;
|
||||
use app_server_protocol::ProjectMemoryReadParams;
|
||||
use app_server_protocol::ProjectMemoryReadResponse;
|
||||
@@ -290,6 +343,146 @@ pub trait AppDataSource: Send + Sync {
|
||||
|
||||
async fn list_automation_jobs(&self) -> Result<AutomationJobListResponse, RuntimeCoreError>;
|
||||
|
||||
async fn list_mcp_servers(&self) -> Result<McpServerListResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"mcpServer/list is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn list_mcp_servers_with_status(
|
||||
&self,
|
||||
) -> Result<McpServerStatusListResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"mcpServerStatus/list is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn list_mcp_tools(&self) -> Result<McpToolListResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"mcpTool/list is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn list_mcp_prompts(&self) -> Result<McpPromptListResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"mcpPrompt/list is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn list_mcp_resources(&self) -> Result<McpResourceListResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"mcpResource/list is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn read_automation_scheduler_config(
|
||||
&self,
|
||||
) -> Result<AutomationSchedulerConfigReadResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"automationScheduler/config/read is not available without an app data source"
|
||||
.to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn update_automation_scheduler_config(
|
||||
&self,
|
||||
_params: AutomationSchedulerConfigUpdateParams,
|
||||
) -> Result<AutomationSchedulerConfigUpdateResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"automationScheduler/config/update is not available without an app data source"
|
||||
.to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn read_automation_scheduler_status(
|
||||
&self,
|
||||
) -> Result<AutomationSchedulerStatusResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"automationScheduler/status is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn read_automation_job(
|
||||
&self,
|
||||
_params: AutomationJobIdParams,
|
||||
) -> Result<AutomationJobReadResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"automationJob/read is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn create_automation_job(
|
||||
&self,
|
||||
_params: AutomationJobCreateParams,
|
||||
) -> Result<AutomationJobWriteResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"automationJob/create is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn update_automation_job(
|
||||
&self,
|
||||
_params: AutomationJobUpdateParams,
|
||||
) -> Result<AutomationJobWriteResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"automationJob/update is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn delete_automation_job(
|
||||
&self,
|
||||
_params: AutomationJobIdParams,
|
||||
) -> Result<AutomationJobDeleteResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"automationJob/delete is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn run_automation_job_now(
|
||||
&self,
|
||||
_params: AutomationJobIdParams,
|
||||
) -> Result<AutomationJobRunNowResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"automationJob/runNow requires the App Server automation executor".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn read_automation_health(
|
||||
&self,
|
||||
_params: AutomationJobHealthParams,
|
||||
) -> Result<AutomationJobHealthResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"automationJob/health is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn read_automation_run_history(
|
||||
&self,
|
||||
_params: AutomationJobRunHistoryParams,
|
||||
) -> Result<AutomationJobRunHistoryResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"automationJob/runHistory is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn preview_automation_schedule(
|
||||
&self,
|
||||
_params: AutomationScheduleParams,
|
||||
) -> Result<AutomationSchedulePreviewResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"automationSchedule/preview is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn validate_automation_schedule(
|
||||
&self,
|
||||
_params: AutomationScheduleParams,
|
||||
) -> Result<AutomationScheduleValidateResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"automationSchedule/validate is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn read_project_memory(
|
||||
&self,
|
||||
params: ProjectMemoryReadParams,
|
||||
@@ -312,6 +505,169 @@ pub trait AppDataSource: Send + Sync {
|
||||
&self,
|
||||
) -> Result<ModelProviderCatalogListResponse, RuntimeCoreError>;
|
||||
|
||||
async fn read_model_provider(
|
||||
&self,
|
||||
_params: ModelProviderReadParams,
|
||||
) -> Result<ModelProviderReadResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"modelProvider/read is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn create_model_provider(
|
||||
&self,
|
||||
_params: ModelProviderCreateParams,
|
||||
) -> Result<ModelProviderWriteResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"modelProvider/create is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn update_model_provider(
|
||||
&self,
|
||||
_params: ModelProviderUpdateParams,
|
||||
) -> Result<ModelProviderWriteResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"modelProvider/update is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn delete_model_provider(
|
||||
&self,
|
||||
_params: ModelProviderDeleteParams,
|
||||
) -> Result<ModelProviderDeleteResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"modelProvider/delete is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn update_model_provider_sort_orders(
|
||||
&self,
|
||||
_params: ModelProviderSortOrdersUpdateParams,
|
||||
) -> Result<ModelProviderMutationResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"modelProvider/sortOrders/update is not available without an app data source"
|
||||
.to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn export_model_provider_config(
|
||||
&self,
|
||||
_params: ModelProviderConfigExportParams,
|
||||
) -> Result<ModelProviderConfigExportResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"modelProviderConfig/export is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn import_model_provider_config(
|
||||
&self,
|
||||
_params: ModelProviderConfigImportParams,
|
||||
) -> Result<ModelProviderConfigImportResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"modelProviderConfig/import is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn test_model_provider_connection(
|
||||
&self,
|
||||
_params: ModelProviderTestConnectionParams,
|
||||
) -> Result<ModelProviderTestConnectionResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"modelProvider/testConnection is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn test_model_provider_chat(
|
||||
&self,
|
||||
_params: ModelProviderTestChatParams,
|
||||
) -> Result<ModelProviderTestChatResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"modelProvider/testChat is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn fetch_model_provider_models(
|
||||
&self,
|
||||
_params: ModelProviderFetchModelsParams,
|
||||
) -> Result<ModelProviderFetchModelsResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"modelProvider/fetchModels is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn create_model_provider_key(
|
||||
&self,
|
||||
_params: ModelProviderKeyCreateParams,
|
||||
) -> Result<ModelProviderKeyWriteResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"modelProviderKey/create is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn update_model_provider_key(
|
||||
&self,
|
||||
_params: ModelProviderKeyUpdateParams,
|
||||
) -> Result<ModelProviderKeyWriteResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"modelProviderKey/update is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn delete_model_provider_key(
|
||||
&self,
|
||||
_params: ModelProviderKeyDeleteParams,
|
||||
) -> Result<ModelProviderKeyDeleteResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"modelProviderKey/delete is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn read_next_model_provider_key(
|
||||
&self,
|
||||
_params: ModelProviderKeyNextParams,
|
||||
) -> Result<ModelProviderKeyNextResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"modelProviderKey/next is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn record_model_provider_key_usage(
|
||||
&self,
|
||||
_params: ModelProviderKeyEventParams,
|
||||
) -> Result<ModelProviderMutationResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"modelProviderKey/usage/record is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn record_model_provider_key_error(
|
||||
&self,
|
||||
_params: ModelProviderKeyEventParams,
|
||||
) -> Result<ModelProviderMutationResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"modelProviderKey/error/record is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn read_model_provider_ui_state(
|
||||
&self,
|
||||
_params: ModelProviderUiStateReadParams,
|
||||
) -> Result<ModelProviderUiStateReadResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"modelProviderUiState/read is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn write_model_provider_ui_state(
|
||||
&self,
|
||||
_params: ModelProviderUiStateWriteParams,
|
||||
) -> Result<ModelProviderMutationResponse, RuntimeCoreError> {
|
||||
Err(RuntimeCoreError::Backend(
|
||||
"modelProviderUiState/write is not available without an app data source".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn read_model_provider_alias(
|
||||
&self,
|
||||
params: ModelProviderAliasReadParams,
|
||||
@@ -560,6 +916,28 @@ impl AppDataSource for NoopAppDataSource {
|
||||
Ok(AutomationJobListResponse::default())
|
||||
}
|
||||
|
||||
async fn list_mcp_servers(&self) -> Result<McpServerListResponse, RuntimeCoreError> {
|
||||
Ok(McpServerListResponse::default())
|
||||
}
|
||||
|
||||
async fn list_mcp_servers_with_status(
|
||||
&self,
|
||||
) -> Result<McpServerStatusListResponse, RuntimeCoreError> {
|
||||
Ok(McpServerStatusListResponse::default())
|
||||
}
|
||||
|
||||
async fn list_mcp_tools(&self) -> Result<McpToolListResponse, RuntimeCoreError> {
|
||||
Ok(McpToolListResponse::default())
|
||||
}
|
||||
|
||||
async fn list_mcp_prompts(&self) -> Result<McpPromptListResponse, RuntimeCoreError> {
|
||||
Ok(McpPromptListResponse::default())
|
||||
}
|
||||
|
||||
async fn list_mcp_resources(&self) -> Result<McpResourceListResponse, RuntimeCoreError> {
|
||||
Ok(McpResourceListResponse::default())
|
||||
}
|
||||
|
||||
async fn read_project_memory(
|
||||
&self,
|
||||
_params: ProjectMemoryReadParams,
|
||||
@@ -1571,6 +1949,122 @@ impl RuntimeCore {
|
||||
self.app_data_source.list_automation_jobs().await
|
||||
}
|
||||
|
||||
pub async fn list_mcp_servers(&self) -> Result<McpServerListResponse, RuntimeCoreError> {
|
||||
self.app_data_source.list_mcp_servers().await
|
||||
}
|
||||
|
||||
pub async fn list_mcp_servers_with_status(
|
||||
&self,
|
||||
) -> Result<McpServerStatusListResponse, RuntimeCoreError> {
|
||||
self.app_data_source.list_mcp_servers_with_status().await
|
||||
}
|
||||
|
||||
pub async fn list_mcp_tools(&self) -> Result<McpToolListResponse, RuntimeCoreError> {
|
||||
self.app_data_source.list_mcp_tools().await
|
||||
}
|
||||
|
||||
pub async fn list_mcp_prompts(&self) -> Result<McpPromptListResponse, RuntimeCoreError> {
|
||||
self.app_data_source.list_mcp_prompts().await
|
||||
}
|
||||
|
||||
pub async fn list_mcp_resources(&self) -> Result<McpResourceListResponse, RuntimeCoreError> {
|
||||
self.app_data_source.list_mcp_resources().await
|
||||
}
|
||||
|
||||
pub async fn read_automation_scheduler_config(
|
||||
&self,
|
||||
) -> Result<AutomationSchedulerConfigReadResponse, RuntimeCoreError> {
|
||||
self.app_data_source
|
||||
.read_automation_scheduler_config()
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn update_automation_scheduler_config(
|
||||
&self,
|
||||
params: AutomationSchedulerConfigUpdateParams,
|
||||
) -> Result<AutomationSchedulerConfigUpdateResponse, RuntimeCoreError> {
|
||||
self.app_data_source
|
||||
.update_automation_scheduler_config(params)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn read_automation_scheduler_status(
|
||||
&self,
|
||||
) -> Result<AutomationSchedulerStatusResponse, RuntimeCoreError> {
|
||||
self.app_data_source
|
||||
.read_automation_scheduler_status()
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn read_automation_job(
|
||||
&self,
|
||||
params: AutomationJobIdParams,
|
||||
) -> Result<AutomationJobReadResponse, RuntimeCoreError> {
|
||||
self.app_data_source.read_automation_job(params).await
|
||||
}
|
||||
|
||||
pub async fn create_automation_job(
|
||||
&self,
|
||||
params: AutomationJobCreateParams,
|
||||
) -> Result<AutomationJobWriteResponse, RuntimeCoreError> {
|
||||
self.app_data_source.create_automation_job(params).await
|
||||
}
|
||||
|
||||
pub async fn update_automation_job(
|
||||
&self,
|
||||
params: AutomationJobUpdateParams,
|
||||
) -> Result<AutomationJobWriteResponse, RuntimeCoreError> {
|
||||
self.app_data_source.update_automation_job(params).await
|
||||
}
|
||||
|
||||
pub async fn delete_automation_job(
|
||||
&self,
|
||||
params: AutomationJobIdParams,
|
||||
) -> Result<AutomationJobDeleteResponse, RuntimeCoreError> {
|
||||
self.app_data_source.delete_automation_job(params).await
|
||||
}
|
||||
|
||||
pub async fn run_automation_job_now(
|
||||
&self,
|
||||
params: AutomationJobIdParams,
|
||||
) -> Result<AutomationJobRunNowResponse, RuntimeCoreError> {
|
||||
self.app_data_source.run_automation_job_now(params).await
|
||||
}
|
||||
|
||||
pub async fn read_automation_health(
|
||||
&self,
|
||||
params: AutomationJobHealthParams,
|
||||
) -> Result<AutomationJobHealthResponse, RuntimeCoreError> {
|
||||
self.app_data_source.read_automation_health(params).await
|
||||
}
|
||||
|
||||
pub async fn read_automation_run_history(
|
||||
&self,
|
||||
params: AutomationJobRunHistoryParams,
|
||||
) -> Result<AutomationJobRunHistoryResponse, RuntimeCoreError> {
|
||||
self.app_data_source
|
||||
.read_automation_run_history(params)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn preview_automation_schedule(
|
||||
&self,
|
||||
params: AutomationScheduleParams,
|
||||
) -> Result<AutomationSchedulePreviewResponse, RuntimeCoreError> {
|
||||
self.app_data_source
|
||||
.preview_automation_schedule(params)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn validate_automation_schedule(
|
||||
&self,
|
||||
params: AutomationScheduleParams,
|
||||
) -> Result<AutomationScheduleValidateResponse, RuntimeCoreError> {
|
||||
self.app_data_source
|
||||
.validate_automation_schedule(params)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn read_project_memory(
|
||||
&self,
|
||||
params: ProjectMemoryReadParams,
|
||||
@@ -1609,6 +2103,152 @@ impl RuntimeCore {
|
||||
self.app_data_source.list_model_provider_catalog().await
|
||||
}
|
||||
|
||||
pub async fn read_model_provider(
|
||||
&self,
|
||||
params: ModelProviderReadParams,
|
||||
) -> Result<ModelProviderReadResponse, RuntimeCoreError> {
|
||||
self.app_data_source.read_model_provider(params).await
|
||||
}
|
||||
|
||||
pub async fn create_model_provider(
|
||||
&self,
|
||||
params: ModelProviderCreateParams,
|
||||
) -> Result<ModelProviderWriteResponse, RuntimeCoreError> {
|
||||
self.app_data_source.create_model_provider(params).await
|
||||
}
|
||||
|
||||
pub async fn update_model_provider(
|
||||
&self,
|
||||
params: ModelProviderUpdateParams,
|
||||
) -> Result<ModelProviderWriteResponse, RuntimeCoreError> {
|
||||
self.app_data_source.update_model_provider(params).await
|
||||
}
|
||||
|
||||
pub async fn delete_model_provider(
|
||||
&self,
|
||||
params: ModelProviderDeleteParams,
|
||||
) -> Result<ModelProviderDeleteResponse, RuntimeCoreError> {
|
||||
self.app_data_source.delete_model_provider(params).await
|
||||
}
|
||||
|
||||
pub async fn update_model_provider_sort_orders(
|
||||
&self,
|
||||
params: ModelProviderSortOrdersUpdateParams,
|
||||
) -> Result<ModelProviderMutationResponse, RuntimeCoreError> {
|
||||
self.app_data_source
|
||||
.update_model_provider_sort_orders(params)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn export_model_provider_config(
|
||||
&self,
|
||||
params: ModelProviderConfigExportParams,
|
||||
) -> Result<ModelProviderConfigExportResponse, RuntimeCoreError> {
|
||||
self.app_data_source
|
||||
.export_model_provider_config(params)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn import_model_provider_config(
|
||||
&self,
|
||||
params: ModelProviderConfigImportParams,
|
||||
) -> Result<ModelProviderConfigImportResponse, RuntimeCoreError> {
|
||||
self.app_data_source
|
||||
.import_model_provider_config(params)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn test_model_provider_connection(
|
||||
&self,
|
||||
params: ModelProviderTestConnectionParams,
|
||||
) -> Result<ModelProviderTestConnectionResponse, RuntimeCoreError> {
|
||||
self.app_data_source
|
||||
.test_model_provider_connection(params)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn test_model_provider_chat(
|
||||
&self,
|
||||
params: ModelProviderTestChatParams,
|
||||
) -> Result<ModelProviderTestChatResponse, RuntimeCoreError> {
|
||||
self.app_data_source.test_model_provider_chat(params).await
|
||||
}
|
||||
|
||||
pub async fn fetch_model_provider_models(
|
||||
&self,
|
||||
params: ModelProviderFetchModelsParams,
|
||||
) -> Result<ModelProviderFetchModelsResponse, RuntimeCoreError> {
|
||||
self.app_data_source
|
||||
.fetch_model_provider_models(params)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn create_model_provider_key(
|
||||
&self,
|
||||
params: ModelProviderKeyCreateParams,
|
||||
) -> Result<ModelProviderKeyWriteResponse, RuntimeCoreError> {
|
||||
self.app_data_source.create_model_provider_key(params).await
|
||||
}
|
||||
|
||||
pub async fn update_model_provider_key(
|
||||
&self,
|
||||
params: ModelProviderKeyUpdateParams,
|
||||
) -> Result<ModelProviderKeyWriteResponse, RuntimeCoreError> {
|
||||
self.app_data_source.update_model_provider_key(params).await
|
||||
}
|
||||
|
||||
pub async fn delete_model_provider_key(
|
||||
&self,
|
||||
params: ModelProviderKeyDeleteParams,
|
||||
) -> Result<ModelProviderKeyDeleteResponse, RuntimeCoreError> {
|
||||
self.app_data_source.delete_model_provider_key(params).await
|
||||
}
|
||||
|
||||
pub async fn read_next_model_provider_key(
|
||||
&self,
|
||||
params: ModelProviderKeyNextParams,
|
||||
) -> Result<ModelProviderKeyNextResponse, RuntimeCoreError> {
|
||||
self.app_data_source
|
||||
.read_next_model_provider_key(params)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn record_model_provider_key_usage(
|
||||
&self,
|
||||
params: ModelProviderKeyEventParams,
|
||||
) -> Result<ModelProviderMutationResponse, RuntimeCoreError> {
|
||||
self.app_data_source
|
||||
.record_model_provider_key_usage(params)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn record_model_provider_key_error(
|
||||
&self,
|
||||
params: ModelProviderKeyEventParams,
|
||||
) -> Result<ModelProviderMutationResponse, RuntimeCoreError> {
|
||||
self.app_data_source
|
||||
.record_model_provider_key_error(params)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn read_model_provider_ui_state(
|
||||
&self,
|
||||
params: ModelProviderUiStateReadParams,
|
||||
) -> Result<ModelProviderUiStateReadResponse, RuntimeCoreError> {
|
||||
self.app_data_source
|
||||
.read_model_provider_ui_state(params)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn write_model_provider_ui_state(
|
||||
&self,
|
||||
params: ModelProviderUiStateWriteParams,
|
||||
) -> Result<ModelProviderMutationResponse, RuntimeCoreError> {
|
||||
self.app_data_source
|
||||
.write_model_provider_ui_state(params)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn read_model_provider_alias(
|
||||
&self,
|
||||
params: ModelProviderAliasReadParams,
|
||||
|
||||
@@ -10,10 +10,6 @@ default = []
|
||||
local-whisper = ["voice-core/local-whisper"]
|
||||
local-sensevoice = ["voice-core/local-sensevoice"]
|
||||
|
||||
[lints.rust]
|
||||
# 允许 objc 宏展开时携带的 cargo-clippy 伪 feature,避免 unexpected_cfgs 噪音。
|
||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(feature, values("cargo-clippy"))'] }
|
||||
|
||||
[dependencies]
|
||||
# 项目内 crate
|
||||
lime-core.workspace = true
|
||||
@@ -75,7 +71,3 @@ proptest.workspace = true
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi.workspace = true
|
||||
winreg.workspace = true
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
cocoa.workspace = true
|
||||
objc.workspace = true
|
||||
|
||||
@@ -9,30 +9,14 @@
|
||||
//! - 获取文件元信息
|
||||
//! - 获取文件权限和 MIME 类型
|
||||
|
||||
#![allow(deprecated, unexpected_cfgs)]
|
||||
|
||||
use base64::{engine::general_purpose, Engine as _};
|
||||
use once_cell::sync::Lazy;
|
||||
use parking_lot::Mutex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::fs::{self, Metadata};
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
use tokio::sync::Semaphore;
|
||||
use std::time::UNIX_EPOCH;
|
||||
use tracing::{debug, error};
|
||||
|
||||
static FILE_ICON_DATA_URL_CACHE: Lazy<Mutex<HashMap<String, Option<String>>>> =
|
||||
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
static FILE_ICON_RESOLVE_SEMAPHORE: Lazy<Arc<Semaphore>> =
|
||||
Lazy::new(|| Arc::new(Semaphore::new(2)));
|
||||
|
||||
const FILE_ICON_RESOLVE_ACQUIRE_TIMEOUT: Duration = Duration::from_millis(80);
|
||||
const FILE_ICON_RESOLVE_TIMEOUT: Duration = Duration::from_millis(900);
|
||||
|
||||
/// 文件条目
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct FileEntry {
|
||||
@@ -100,19 +84,6 @@ pub struct FilePreview {
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
/// 文件管理器快捷入口
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct FileManagerLocation {
|
||||
/// 稳定 ID
|
||||
pub id: String,
|
||||
/// 展示名称
|
||||
pub label: String,
|
||||
/// 入口目录绝对路径
|
||||
pub path: String,
|
||||
/// 入口类型
|
||||
pub kind: String,
|
||||
}
|
||||
|
||||
/// 获取文件扩展名
|
||||
fn get_file_extension(path: &Path) -> Option<String> {
|
||||
path.extension()
|
||||
@@ -125,469 +96,6 @@ fn is_hidden_file(name: &str) -> bool {
|
||||
name.starts_with('.')
|
||||
}
|
||||
|
||||
fn encode_png_data_url(bytes: &[u8]) -> Option<String> {
|
||||
if bytes.is_empty() {
|
||||
return None;
|
||||
}
|
||||
Some(format!(
|
||||
"data:image/png;base64,{}",
|
||||
general_purpose::STANDARD.encode(bytes)
|
||||
))
|
||||
}
|
||||
|
||||
fn temporary_icon_png_path() -> PathBuf {
|
||||
let unique = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map(|duration| duration.as_nanos())
|
||||
.unwrap_or(0);
|
||||
std::env::temp_dir().join(format!(
|
||||
"lime-file-icon-{}-{unique}.png",
|
||||
std::process::id()
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "macos", test))]
|
||||
fn normalize_bundle_icon_file_name(value: &str) -> Option<String> {
|
||||
let trimmed = value.trim().trim_matches('"').trim();
|
||||
if trimmed.is_empty() {
|
||||
return None;
|
||||
}
|
||||
Path::new(trimmed)
|
||||
.file_name()
|
||||
.and_then(|name| name.to_str())
|
||||
.map(|name| name.trim().to_string())
|
||||
.filter(|name| !name.is_empty())
|
||||
}
|
||||
|
||||
fn resolve_cached_file_icon_data_url(
|
||||
path: &Path,
|
||||
metadata: &Metadata,
|
||||
name: &str,
|
||||
) -> Option<String> {
|
||||
if !should_resolve_file_icon(path, metadata, name) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let cache_key = path.to_string_lossy().to_string();
|
||||
if let Some(cached) = FILE_ICON_DATA_URL_CACHE.lock().get(&cache_key).cloned() {
|
||||
return cached;
|
||||
}
|
||||
|
||||
let icon_data_url = resolve_platform_file_icon_data_url(path, metadata, name);
|
||||
FILE_ICON_DATA_URL_CACHE
|
||||
.lock()
|
||||
.insert(cache_key, icon_data_url.clone());
|
||||
icon_data_url
|
||||
}
|
||||
|
||||
fn get_cached_file_icon_data_url(path: &Path) -> Option<String> {
|
||||
FILE_ICON_DATA_URL_CACHE
|
||||
.lock()
|
||||
.get(&path.to_string_lossy().to_string())
|
||||
.and_then(|cached| cached.clone())
|
||||
}
|
||||
|
||||
fn get_file_icon_cache_entry(path: &Path) -> Option<Option<String>> {
|
||||
FILE_ICON_DATA_URL_CACHE
|
||||
.lock()
|
||||
.get(&path.to_string_lossy().to_string())
|
||||
.cloned()
|
||||
}
|
||||
|
||||
fn should_resolve_file_icon(path: &Path, metadata: &Metadata, name: &str) -> bool {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
let _ = (path, metadata, name);
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
let _ = metadata;
|
||||
let extension = path
|
||||
.extension()
|
||||
.and_then(|extension| extension.to_str())
|
||||
.unwrap_or("");
|
||||
matches!(
|
||||
extension.to_ascii_lowercase().as_str(),
|
||||
"exe" | "lnk" | "appref-ms"
|
||||
) || name.ends_with(".lnk")
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
|
||||
{
|
||||
let _ = (path, metadata, name);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn resolve_platform_file_icon_data_url(
|
||||
path: &Path,
|
||||
metadata: &Metadata,
|
||||
_name: &str,
|
||||
) -> Option<String> {
|
||||
resolve_macos_system_icon_data_url(path).or_else(|| {
|
||||
if is_macos_app_bundle(path, metadata) {
|
||||
resolve_macos_app_icon_data_url(path)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn resolve_platform_file_icon_data_url(
|
||||
path: &Path,
|
||||
_metadata: &Metadata,
|
||||
_name: &str,
|
||||
) -> Option<String> {
|
||||
resolve_windows_associated_icon_data_url(path)
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
|
||||
fn resolve_platform_file_icon_data_url(
|
||||
_path: &Path,
|
||||
_metadata: &Metadata,
|
||||
_name: &str,
|
||||
) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn resolve_macos_app_icon_data_url(app_path: &Path) -> Option<String> {
|
||||
let resources_dir = app_path.join("Contents").join("Resources");
|
||||
let icon_path = resolve_macos_bundle_icon_path(app_path, &resources_dir)
|
||||
.or_else(|| find_first_icns_file(&resources_dir))?;
|
||||
convert_macos_icns_to_png_data_url(&icon_path)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn is_macos_app_bundle(path: &Path, metadata: &Metadata) -> bool {
|
||||
metadata.is_dir()
|
||||
&& path
|
||||
.extension()
|
||||
.and_then(|extension| extension.to_str())
|
||||
.map(|extension| extension.eq_ignore_ascii_case("app"))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn resolve_macos_system_icon_data_url(path: &Path) -> Option<String> {
|
||||
use cocoa::base::{id, nil};
|
||||
use cocoa::foundation::{NSAutoreleasePool, NSDictionary, NSSize, NSString, NSUInteger};
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
|
||||
unsafe {
|
||||
let pool = NSAutoreleasePool::new(nil);
|
||||
let path_string = path.to_string_lossy();
|
||||
let ns_path = NSString::alloc(nil).init_str(path_string.as_ref());
|
||||
let workspace: id = msg_send![class!(NSWorkspace), sharedWorkspace];
|
||||
let icon: id = msg_send![workspace, iconForFile: ns_path];
|
||||
if icon == nil {
|
||||
pool.drain();
|
||||
return None;
|
||||
}
|
||||
|
||||
let _: () = msg_send![icon, setSize: NSSize::new(128.0, 128.0)];
|
||||
let tiff_data: id = msg_send![icon, TIFFRepresentation];
|
||||
if tiff_data == nil {
|
||||
pool.drain();
|
||||
return None;
|
||||
}
|
||||
|
||||
let bitmap_rep: id = msg_send![class!(NSBitmapImageRep), imageRepWithData: tiff_data];
|
||||
if bitmap_rep == nil {
|
||||
pool.drain();
|
||||
return None;
|
||||
}
|
||||
|
||||
let properties = NSDictionary::dictionary(nil);
|
||||
let png_type = 4 as NSUInteger;
|
||||
let png_data: id =
|
||||
msg_send![bitmap_rep, representationUsingType: png_type properties: properties];
|
||||
let bytes = nsdata_to_vec(png_data);
|
||||
pool.drain();
|
||||
bytes.and_then(|bytes| encode_png_data_url(&bytes))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
unsafe fn nsdata_to_vec(data: cocoa::base::id) -> Option<Vec<u8>> {
|
||||
use cocoa::base::nil;
|
||||
use cocoa::foundation::NSData;
|
||||
|
||||
if data == nil {
|
||||
return None;
|
||||
}
|
||||
let len = data.length() as usize;
|
||||
if len == 0 {
|
||||
return None;
|
||||
}
|
||||
let ptr = data.bytes() as *const u8;
|
||||
if ptr.is_null() {
|
||||
return None;
|
||||
}
|
||||
Some(std::slice::from_raw_parts(ptr, len).to_vec())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn resolve_macos_bundle_icon_path(app_path: &Path, resources_dir: &Path) -> Option<PathBuf> {
|
||||
let info_plist = app_path.join("Contents").join("Info.plist");
|
||||
if !info_plist.is_file() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let output = std::process::Command::new("/usr/bin/plutil")
|
||||
.args(["-extract", "CFBundleIconFile", "raw", "-o", "-"])
|
||||
.arg(info_plist)
|
||||
.output()
|
||||
.ok()?;
|
||||
|
||||
if !output.status.success() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let icon_name = normalize_bundle_icon_file_name(&String::from_utf8_lossy(&output.stdout))?;
|
||||
let mut candidates = Vec::with_capacity(2);
|
||||
candidates.push(resources_dir.join(&icon_name));
|
||||
if Path::new(&icon_name).extension().is_none() {
|
||||
candidates.push(resources_dir.join(format!("{icon_name}.icns")));
|
||||
}
|
||||
|
||||
candidates.into_iter().find(|candidate| candidate.is_file())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn find_first_icns_file(resources_dir: &Path) -> Option<PathBuf> {
|
||||
let read_dir = fs::read_dir(resources_dir).ok()?;
|
||||
let mut candidates: Vec<PathBuf> = read_dir
|
||||
.filter_map(|entry| entry.ok().map(|entry| entry.path()))
|
||||
.filter(|path| {
|
||||
path.extension()
|
||||
.and_then(|extension| extension.to_str())
|
||||
.map(|extension| extension.eq_ignore_ascii_case("icns"))
|
||||
.unwrap_or(false)
|
||||
})
|
||||
.collect();
|
||||
|
||||
candidates.sort_by_key(|path| {
|
||||
let file_name = path
|
||||
.file_name()
|
||||
.and_then(|name| name.to_str())
|
||||
.unwrap_or("")
|
||||
.to_ascii_lowercase();
|
||||
(
|
||||
!file_name.contains("appicon"),
|
||||
!file_name.contains("icon"),
|
||||
file_name,
|
||||
)
|
||||
});
|
||||
candidates.into_iter().next()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn convert_macos_icns_to_png_data_url(icon_path: &Path) -> Option<String> {
|
||||
let output_path = temporary_icon_png_path();
|
||||
let result = std::process::Command::new("/usr/bin/sips")
|
||||
.args(["-s", "format", "png"])
|
||||
.arg(icon_path)
|
||||
.arg("--out")
|
||||
.arg(&output_path)
|
||||
.output();
|
||||
|
||||
let converted = match result {
|
||||
Ok(output) if output.status.success() => fs::read(&output_path).ok(),
|
||||
_ => None,
|
||||
};
|
||||
let _ = fs::remove_file(&output_path);
|
||||
converted.and_then(|bytes| encode_png_data_url(&bytes))
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn resolve_windows_associated_icon_data_url(path: &Path) -> Option<String> {
|
||||
let output_path = temporary_icon_png_path();
|
||||
let script = r#"
|
||||
$ErrorActionPreference = 'Stop'
|
||||
Add-Type -AssemblyName System.Drawing
|
||||
$icon = [System.Drawing.Icon]::ExtractAssociatedIcon($env:LIME_ICON_SOURCE)
|
||||
if ($null -eq $icon) { exit 2 }
|
||||
$bitmap = $icon.ToBitmap()
|
||||
$bitmap.Save($env:LIME_ICON_OUTPUT, [System.Drawing.Imaging.ImageFormat]::Png)
|
||||
$bitmap.Dispose()
|
||||
$icon.Dispose()
|
||||
"#;
|
||||
|
||||
let encoded_script = encode_powershell_script(script);
|
||||
let result = std::process::Command::new("powershell")
|
||||
.args([
|
||||
"-NoProfile",
|
||||
"-NonInteractive",
|
||||
"-ExecutionPolicy",
|
||||
"Bypass",
|
||||
"-EncodedCommand",
|
||||
&encoded_script,
|
||||
])
|
||||
.env("LIME_ICON_SOURCE", path)
|
||||
.env("LIME_ICON_OUTPUT", &output_path)
|
||||
.output();
|
||||
|
||||
let rendered = match result {
|
||||
Ok(output) if output.status.success() => fs::read(&output_path).ok(),
|
||||
_ => None,
|
||||
};
|
||||
let _ = fs::remove_file(&output_path);
|
||||
rendered.and_then(|bytes| encode_png_data_url(&bytes))
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn encode_powershell_script(script: &str) -> String {
|
||||
let mut bytes = Vec::with_capacity(script.len() * 2);
|
||||
for unit in script.encode_utf16() {
|
||||
bytes.extend_from_slice(&unit.to_le_bytes());
|
||||
}
|
||||
general_purpose::STANDARD.encode(bytes)
|
||||
}
|
||||
|
||||
fn append_file_manager_location(
|
||||
locations: &mut Vec<FileManagerLocation>,
|
||||
seen_paths: &mut std::collections::HashSet<String>,
|
||||
id: &str,
|
||||
label: &str,
|
||||
kind: &str,
|
||||
path: Option<PathBuf>,
|
||||
) {
|
||||
let Some(path) = path else {
|
||||
return;
|
||||
};
|
||||
|
||||
if !path.is_dir() {
|
||||
return;
|
||||
}
|
||||
|
||||
let normalized_path = path.to_string_lossy().to_string();
|
||||
if normalized_path.trim().is_empty() || !seen_paths.insert(normalized_path.clone()) {
|
||||
return;
|
||||
}
|
||||
|
||||
locations.push(FileManagerLocation {
|
||||
id: id.to_string(),
|
||||
label: label.to_string(),
|
||||
path: normalized_path,
|
||||
kind: kind.to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
/// 获取文件管理器快捷入口
|
||||
pub fn file_manager_locations() -> Vec<FileManagerLocation> {
|
||||
let mut locations = Vec::new();
|
||||
let mut seen_paths = std::collections::HashSet::new();
|
||||
let home_dir = dirs::home_dir();
|
||||
|
||||
append_file_manager_location(
|
||||
&mut locations,
|
||||
&mut seen_paths,
|
||||
"home",
|
||||
"个人",
|
||||
"home",
|
||||
home_dir.clone(),
|
||||
);
|
||||
append_file_manager_location(
|
||||
&mut locations,
|
||||
&mut seen_paths,
|
||||
"desktop",
|
||||
"桌面",
|
||||
"desktop",
|
||||
dirs::desktop_dir(),
|
||||
);
|
||||
append_file_manager_location(
|
||||
&mut locations,
|
||||
&mut seen_paths,
|
||||
"documents",
|
||||
"文档",
|
||||
"documents",
|
||||
dirs::document_dir(),
|
||||
);
|
||||
append_file_manager_location(
|
||||
&mut locations,
|
||||
&mut seen_paths,
|
||||
"downloads",
|
||||
"下载",
|
||||
"downloads",
|
||||
dirs::download_dir(),
|
||||
);
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
append_file_manager_location(
|
||||
&mut locations,
|
||||
&mut seen_paths,
|
||||
"applications",
|
||||
"应用程序",
|
||||
"applications",
|
||||
Some(PathBuf::from("/Applications")),
|
||||
);
|
||||
append_file_manager_location(
|
||||
&mut locations,
|
||||
&mut seen_paths,
|
||||
"user-applications",
|
||||
"用户应用程序",
|
||||
"applications",
|
||||
home_dir.map(|home| home.join("Applications")),
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
append_file_manager_location(
|
||||
&mut locations,
|
||||
&mut seen_paths,
|
||||
"start-menu-programs",
|
||||
"应用程序",
|
||||
"applications",
|
||||
std::env::var_os("APPDATA").map(|dir| {
|
||||
PathBuf::from(dir)
|
||||
.join("Microsoft")
|
||||
.join("Windows")
|
||||
.join("Start Menu")
|
||||
.join("Programs")
|
||||
}),
|
||||
);
|
||||
append_file_manager_location(
|
||||
&mut locations,
|
||||
&mut seen_paths,
|
||||
"common-start-menu-programs",
|
||||
"公共应用程序",
|
||||
"applications",
|
||||
std::env::var_os("PROGRAMDATA").map(|dir| {
|
||||
PathBuf::from(dir)
|
||||
.join("Microsoft")
|
||||
.join("Windows")
|
||||
.join("Start Menu")
|
||||
.join("Programs")
|
||||
}),
|
||||
);
|
||||
append_file_manager_location(
|
||||
&mut locations,
|
||||
&mut seen_paths,
|
||||
"program-files",
|
||||
"Program Files",
|
||||
"applications",
|
||||
std::env::var_os("ProgramFiles").map(PathBuf::from),
|
||||
);
|
||||
append_file_manager_location(
|
||||
&mut locations,
|
||||
&mut seen_paths,
|
||||
"program-files-x86",
|
||||
"Program Files (x86)",
|
||||
"applications",
|
||||
std::env::var_os("ProgramFiles(x86)").map(PathBuf::from),
|
||||
);
|
||||
}
|
||||
|
||||
locations
|
||||
}
|
||||
|
||||
/// 将 Unix 文件模式转换为权限字符串(如 -rw-r--r--)
|
||||
#[cfg(unix)]
|
||||
fn mode_to_string(mode: u32, is_dir: bool, is_symlink: bool) -> String {
|
||||
@@ -886,8 +394,6 @@ pub fn list_directory(path: &str) -> DirectoryListing {
|
||||
|
||||
// 获取 MIME 类型
|
||||
let mime_type = get_mime_type(&path, &metadata);
|
||||
let icon_data_url = get_cached_file_icon_data_url(&path);
|
||||
|
||||
Some(FileEntry {
|
||||
name: name.clone(),
|
||||
path: path.to_string_lossy().to_string(),
|
||||
@@ -900,7 +406,7 @@ pub fn list_directory(path: &str) -> DirectoryListing {
|
||||
mode,
|
||||
mime_type: Some(mime_type),
|
||||
is_symlink,
|
||||
icon_data_url,
|
||||
icon_data_url: None,
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
@@ -1018,52 +524,6 @@ pub fn read_file_preview(path: &str, max_size: Option<usize>) -> FilePreview {
|
||||
}
|
||||
}
|
||||
|
||||
/// 服务接口:异步获取文件图标
|
||||
pub async fn get_file_icon_data_url(path: String) -> Result<Option<String>, String> {
|
||||
let path_buf = PathBuf::from(&path);
|
||||
if let Some(cached) = get_file_icon_cache_entry(&path_buf) {
|
||||
return Ok(cached);
|
||||
}
|
||||
|
||||
let semaphore = Arc::clone(&FILE_ICON_RESOLVE_SEMAPHORE);
|
||||
let permit =
|
||||
match tokio::time::timeout(FILE_ICON_RESOLVE_ACQUIRE_TIMEOUT, semaphore.acquire_owned())
|
||||
.await
|
||||
{
|
||||
Ok(Ok(permit)) => permit,
|
||||
Ok(Err(_)) => return Ok(None),
|
||||
Err(_) => return Ok(None),
|
||||
};
|
||||
|
||||
let resolve_task = tokio::task::spawn_blocking(move || {
|
||||
let _permit = permit;
|
||||
let metadata = fs::metadata(&path_buf).ok()?;
|
||||
let name = path_buf
|
||||
.file_name()
|
||||
.and_then(|name| name.to_str())
|
||||
.unwrap_or("");
|
||||
resolve_cached_file_icon_data_url(&path_buf, &metadata, name)
|
||||
});
|
||||
|
||||
match tokio::time::timeout(FILE_ICON_RESOLVE_TIMEOUT, resolve_task).await {
|
||||
Ok(Ok(icon_data_url)) => Ok(icon_data_url),
|
||||
Ok(Err(join_error)) => Err(format!("文件图标读取任务失败: {join_error}")),
|
||||
Err(_) => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// 服务接口:获取用户主目录
|
||||
pub async fn get_home_dir() -> Result<String, String> {
|
||||
dirs::home_dir()
|
||||
.map(|p| p.to_string_lossy().to_string())
|
||||
.ok_or_else(|| "无法获取主目录".to_string())
|
||||
}
|
||||
|
||||
/// 服务接口:获取文件管理器快捷入口
|
||||
pub async fn get_file_manager_locations() -> Result<Vec<FileManagerLocation>, String> {
|
||||
Ok(file_manager_locations())
|
||||
}
|
||||
|
||||
/// 服务接口:创建新文件
|
||||
pub async fn create_file(path: String) -> Result<(), String> {
|
||||
let path_buf = PathBuf::from(&path);
|
||||
@@ -1154,79 +614,6 @@ pub async fn get_file_name(path: String) -> Result<String, String> {
|
||||
.map(|s| s.to_string())
|
||||
.ok_or_else(|| "无法获取文件名".to_string())
|
||||
}
|
||||
|
||||
/// 服务接口:在 Finder 中显示文件
|
||||
pub async fn reveal_in_finder(path: String) -> Result<(), String> {
|
||||
let path_buf = PathBuf::from(&path);
|
||||
|
||||
if !path_buf.exists() {
|
||||
return Err("文件或目录不存在".to_string());
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
std::process::Command::new("open")
|
||||
.args(["-R", &path])
|
||||
.spawn()
|
||||
.map_err(|e| format!("无法打开 Finder: {e}"))?;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
std::process::Command::new("explorer")
|
||||
.args(["/select,", &path])
|
||||
.spawn()
|
||||
.map_err(|e| format!("无法打开资源管理器: {}", e))?;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
// 尝试使用 xdg-open 打开父目录
|
||||
let parent = path_buf.parent().unwrap_or(&path_buf);
|
||||
std::process::Command::new("xdg-open")
|
||||
.arg(parent)
|
||||
.spawn()
|
||||
.map_err(|e| format!("无法打开文件管理器: {}", e))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 服务接口:使用默认应用打开文件
|
||||
pub async fn open_with_default_app(path: String) -> Result<(), String> {
|
||||
let path_buf = PathBuf::from(&path);
|
||||
|
||||
if !path_buf.exists() {
|
||||
return Err("文件不存在".to_string());
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
std::process::Command::new("open")
|
||||
.arg(&path)
|
||||
.spawn()
|
||||
.map_err(|e| format!("无法打开文件: {e}"))?;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
std::process::Command::new("cmd")
|
||||
.args(["/C", "start", "", &path])
|
||||
.spawn()
|
||||
.map_err(|e| format!("无法打开文件: {}", e))?;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
std::process::Command::new("xdg-open")
|
||||
.arg(&path)
|
||||
.spawn()
|
||||
.map_err(|e| format!("无法打开文件: {}", e))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -1270,48 +657,6 @@ mod tests {
|
||||
assert_eq!(error, "目录已存在");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normalize_bundle_icon_file_name() {
|
||||
assert_eq!(
|
||||
normalize_bundle_icon_file_name(" AppIcon.icns\n"),
|
||||
Some("AppIcon.icns".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
normalize_bundle_icon_file_name("../Resources/AppIcon"),
|
||||
Some("AppIcon".to_string())
|
||||
);
|
||||
assert_eq!(normalize_bundle_icon_file_name(" "), None);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[test]
|
||||
fn test_resolve_macos_bundle_icon_path_from_plist() {
|
||||
let temp_dir = tempfile::tempdir().expect("应创建临时目录");
|
||||
let app_dir = temp_dir.path().join("Demo.app");
|
||||
let contents_dir = app_dir.join("Contents");
|
||||
let resources_dir = contents_dir.join("Resources");
|
||||
fs::create_dir_all(&resources_dir).expect("应创建应用资源目录");
|
||||
fs::write(resources_dir.join("AppIcon.icns"), []).expect("应创建图标文件");
|
||||
fs::write(
|
||||
contents_dir.join("Info.plist"),
|
||||
r#"<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>AppIcon</string>
|
||||
</dict>
|
||||
</plist>
|
||||
"#,
|
||||
)
|
||||
.expect("应写入 Info.plist");
|
||||
|
||||
assert_eq!(
|
||||
resolve_macos_bundle_icon_path(&app_dir, &resources_dir),
|
||||
Some(resources_dir.join("AppIcon.icns"))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_text_file() {
|
||||
assert!(is_text_file(Some("txt")));
|
||||
|
||||
@@ -1234,28 +1234,6 @@ pub fn run() {
|
||||
commands::site_capability_cmd::site_run_adapter,
|
||||
commands::site_capability_cmd::site_debug_run_adapter,
|
||||
commands::site_capability_cmd::site_save_adapter_result,
|
||||
// API Key Provider commands
|
||||
commands::api_key_provider_cmd::get_system_provider_catalog,
|
||||
commands::api_key_provider_cmd::get_api_key_providers,
|
||||
commands::api_key_provider_cmd::get_api_key_provider,
|
||||
commands::api_key_provider_cmd::add_custom_api_key_provider,
|
||||
commands::api_key_provider_cmd::update_api_key_provider,
|
||||
commands::api_key_provider_cmd::delete_custom_api_key_provider,
|
||||
commands::api_key_provider_cmd::add_api_key,
|
||||
commands::api_key_provider_cmd::delete_api_key,
|
||||
commands::api_key_provider_cmd::toggle_api_key,
|
||||
commands::api_key_provider_cmd::update_api_key_alias,
|
||||
commands::api_key_provider_cmd::get_next_api_key,
|
||||
commands::api_key_provider_cmd::record_api_key_usage,
|
||||
commands::api_key_provider_cmd::record_api_key_error,
|
||||
commands::api_key_provider_cmd::get_provider_ui_state,
|
||||
commands::api_key_provider_cmd::set_provider_ui_state,
|
||||
commands::api_key_provider_cmd::update_provider_sort_orders,
|
||||
commands::api_key_provider_cmd::export_api_key_providers,
|
||||
commands::api_key_provider_cmd::import_api_key_providers,
|
||||
// API Key Provider connection test command
|
||||
commands::api_key_provider_cmd::test_api_key_provider_connection,
|
||||
commands::api_key_provider_cmd::test_api_key_provider_chat,
|
||||
// Telemetry commands
|
||||
commands::telemetry_cmd::get_request_logs,
|
||||
commands::telemetry_cmd::get_request_log_detail,
|
||||
@@ -1386,7 +1364,6 @@ pub fn run() {
|
||||
commands::model_registry_cmd::get_provider_alias_config,
|
||||
commands::model_registry_cmd::get_all_alias_configs,
|
||||
commands::model_registry_cmd::fetch_provider_models_from_api,
|
||||
commands::model_registry_cmd::fetch_provider_models_auto,
|
||||
// WebSocket commands
|
||||
commands::websocket_cmd::get_websocket_status,
|
||||
commands::websocket_cmd::get_websocket_connections,
|
||||
@@ -1418,16 +1395,11 @@ pub fn run() {
|
||||
crate::services::sysinfo_service::subscribe_sysinfo,
|
||||
crate::services::sysinfo_service::unsubscribe_sysinfo,
|
||||
// File browser commands
|
||||
crate::services::file_browser_service::get_home_dir,
|
||||
crate::services::file_browser_service::get_file_manager_locations,
|
||||
crate::services::file_browser_service::get_file_icon_data_url,
|
||||
crate::services::file_browser_service::create_file,
|
||||
crate::services::file_browser_service::create_directory,
|
||||
crate::services::file_browser_service::delete_file,
|
||||
crate::services::file_browser_service::rename_file,
|
||||
crate::services::file_browser_service::get_file_name,
|
||||
crate::services::file_browser_service::reveal_in_finder,
|
||||
crate::services::file_browser_service::open_with_default_app,
|
||||
// Webview commands
|
||||
commands::webview_cmd::create_webview_panel,
|
||||
commands::webview_cmd::close_webview_panel,
|
||||
@@ -1671,20 +1643,6 @@ pub fn run() {
|
||||
crate::voice::commands::cancel_recording,
|
||||
crate::voice::commands::get_recording_status,
|
||||
crate::voice::commands::list_audio_devices,
|
||||
// Automation commands
|
||||
commands::automation_cmd::get_automation_scheduler_config,
|
||||
commands::automation_cmd::update_automation_scheduler_config,
|
||||
commands::automation_cmd::get_automation_status,
|
||||
commands::automation_cmd::get_automation_jobs,
|
||||
commands::automation_cmd::get_automation_job,
|
||||
commands::automation_cmd::create_automation_job,
|
||||
commands::automation_cmd::update_automation_job,
|
||||
commands::automation_cmd::delete_automation_job,
|
||||
commands::automation_cmd::run_automation_job_now,
|
||||
commands::automation_cmd::get_automation_health,
|
||||
commands::automation_cmd::get_automation_run_history,
|
||||
commands::automation_cmd::preview_automation_schedule,
|
||||
commands::automation_cmd::validate_automation_schedule,
|
||||
// Telegram 远程触发命令
|
||||
commands::gateway_channel_cmd::gateway_channel_start,
|
||||
commands::gateway_channel_cmd::gateway_channel_stop,
|
||||
|
||||
@@ -1,556 +1,9 @@
|
||||
//! API Key Provider Tauri 命令
|
||||
//! API Key Provider service state.
|
||||
//!
|
||||
//! 提供 API Key Provider 管理的前端调用接口。
|
||||
//!
|
||||
//! **Feature: provider-ui-refactor**
|
||||
//! **Validates: Requirements 9.1**
|
||||
//! Provider 管理命令已迁入 App Server `modelProvider/*` current 主链。
|
||||
|
||||
use crate::database::dao::api_key_provider::{
|
||||
ApiKeyEntry, ApiKeyProvider, ApiProviderPromptCacheMode, ApiProviderType, ProviderWithKeys,
|
||||
};
|
||||
use crate::database::system_providers::get_system_providers;
|
||||
use crate::database::DbConnection;
|
||||
use lime_services::api_key_provider_service::{
|
||||
ApiKeyProviderService, ChatTestResult, ConnectionTestResult, ImportResult,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use lime_services::api_key_provider_service::ApiKeyProviderService;
|
||||
use std::sync::Arc;
|
||||
use tauri::State;
|
||||
|
||||
/// API Key Provider 服务状态封装
|
||||
/// API Key Provider 服务状态封装。
|
||||
pub struct ApiKeyProviderServiceState(pub Arc<ApiKeyProviderService>);
|
||||
|
||||
// ============================================================================
|
||||
// 请求/响应类型
|
||||
// ============================================================================
|
||||
|
||||
/// 添加自定义 Provider 请求
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AddCustomProviderRequest {
|
||||
pub name: String,
|
||||
#[serde(rename = "type")]
|
||||
pub provider_type: String,
|
||||
pub api_host: String,
|
||||
pub api_version: Option<String>,
|
||||
pub project: Option<String>,
|
||||
pub location: Option<String>,
|
||||
pub region: Option<String>,
|
||||
pub prompt_cache_mode: Option<String>,
|
||||
}
|
||||
|
||||
/// 更新 Provider 请求
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct UpdateProviderRequest {
|
||||
pub name: Option<String>,
|
||||
/// Provider 类型(系统/自定义 Provider 均可修改)
|
||||
#[serde(rename = "type")]
|
||||
pub provider_type: Option<String>,
|
||||
pub api_host: Option<String>,
|
||||
pub enabled: Option<bool>,
|
||||
pub sort_order: Option<i32>,
|
||||
pub api_version: Option<String>,
|
||||
pub project: Option<String>,
|
||||
pub location: Option<String>,
|
||||
pub region: Option<String>,
|
||||
pub prompt_cache_mode: Option<String>,
|
||||
/// 自定义模型列表
|
||||
pub custom_models: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
/// 添加 API Key 请求
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AddApiKeyRequest {
|
||||
pub provider_id: String,
|
||||
pub api_key: String,
|
||||
pub alias: Option<String>,
|
||||
#[serde(default)]
|
||||
pub replace_existing: bool,
|
||||
}
|
||||
|
||||
/// Provider 显示数据(用于前端)
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ProviderDisplay {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
#[serde(rename = "type")]
|
||||
pub provider_type: String,
|
||||
pub api_host: String,
|
||||
pub is_system: bool,
|
||||
pub group: String,
|
||||
pub enabled: bool,
|
||||
pub sort_order: i32,
|
||||
pub api_version: Option<String>,
|
||||
pub project: Option<String>,
|
||||
pub location: Option<String>,
|
||||
pub region: Option<String>,
|
||||
/// 自定义模型列表
|
||||
pub custom_models: Vec<String>,
|
||||
/// 当前 Provider 声明的 Prompt Cache 模式(前端优先使用该值,不再只按 type 猜)
|
||||
pub prompt_cache_mode: Option<String>,
|
||||
pub api_key_count: usize,
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
}
|
||||
|
||||
/// API Key 显示数据(用于前端,掩码显示)
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ApiKeyDisplay {
|
||||
pub id: String,
|
||||
pub provider_id: String,
|
||||
/// 掩码后的 API Key
|
||||
pub api_key_masked: String,
|
||||
pub alias: Option<String>,
|
||||
pub enabled: bool,
|
||||
pub usage_count: i64,
|
||||
pub error_count: i64,
|
||||
pub last_used_at: Option<String>,
|
||||
pub created_at: String,
|
||||
}
|
||||
|
||||
/// Provider 完整显示数据(包含 API Keys)
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ProviderWithKeysDisplay {
|
||||
#[serde(flatten)]
|
||||
pub provider: ProviderDisplay,
|
||||
pub api_keys: Vec<ApiKeyDisplay>,
|
||||
}
|
||||
|
||||
/// 系统 Provider Catalog 条目(用于前端快速填充)
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SystemProviderCatalogItem {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
#[serde(rename = "type")]
|
||||
pub provider_type: String,
|
||||
pub api_host: String,
|
||||
pub group: String,
|
||||
pub sort_order: i32,
|
||||
pub api_version: Option<String>,
|
||||
/// 兼容旧版本前端/历史配置的别名 ID
|
||||
pub legacy_ids: Vec<String>,
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 辅助函数
|
||||
// ============================================================================
|
||||
|
||||
/// 将 API Key 转换为掩码显示
|
||||
fn mask_api_key(key: &str) -> String {
|
||||
let chars: Vec<char> = key.chars().collect();
|
||||
if chars.len() <= 12 {
|
||||
"****".to_string()
|
||||
} else {
|
||||
let prefix: String = chars[..6].iter().collect();
|
||||
let suffix: String = chars[chars.len() - 4..].iter().collect();
|
||||
format!("{prefix}****{suffix}")
|
||||
}
|
||||
}
|
||||
|
||||
/// 将 ApiKeyProvider 转换为 ProviderDisplay
|
||||
fn provider_to_display(provider: &ApiKeyProvider, api_key_count: usize) -> ProviderDisplay {
|
||||
ProviderDisplay {
|
||||
id: provider.id.clone(),
|
||||
name: provider.name.clone(),
|
||||
provider_type: provider.effective_provider_type().to_string(),
|
||||
api_host: provider.api_host.clone(),
|
||||
is_system: provider.is_system,
|
||||
group: provider.group.to_string(),
|
||||
enabled: provider.enabled,
|
||||
sort_order: provider.sort_order,
|
||||
api_version: provider.api_version.clone(),
|
||||
project: provider.project.clone(),
|
||||
location: provider.location.clone(),
|
||||
region: provider.region.clone(),
|
||||
custom_models: provider.custom_models.clone(),
|
||||
prompt_cache_mode: provider
|
||||
.effective_prompt_cache_mode()
|
||||
.map(|mode| mode.to_string()),
|
||||
api_key_count,
|
||||
created_at: provider.created_at.to_rfc3339(),
|
||||
updated_at: provider.updated_at.to_rfc3339(),
|
||||
}
|
||||
}
|
||||
|
||||
/// 将 ApiKeyEntry 转换为 ApiKeyDisplay(需要解密后掩码)
|
||||
fn api_key_to_display(key: &ApiKeyEntry, service: &ApiKeyProviderService) -> ApiKeyDisplay {
|
||||
// 解密后掩码显示
|
||||
let masked = match service.decrypt_api_key(&key.api_key_encrypted) {
|
||||
Ok(decrypted) => mask_api_key(&decrypted),
|
||||
Err(_) => "****".to_string(),
|
||||
};
|
||||
|
||||
ApiKeyDisplay {
|
||||
id: key.id.clone(),
|
||||
provider_id: key.provider_id.clone(),
|
||||
api_key_masked: masked,
|
||||
alias: key.alias.clone(),
|
||||
enabled: key.enabled,
|
||||
usage_count: key.usage_count,
|
||||
error_count: key.error_count,
|
||||
last_used_at: key.last_used_at.map(|t| t.to_rfc3339()),
|
||||
created_at: key.created_at.to_rfc3339(),
|
||||
}
|
||||
}
|
||||
|
||||
/// 将 ProviderWithKeys 转换为 ProviderWithKeysDisplay
|
||||
fn provider_with_keys_to_display(
|
||||
pwk: &ProviderWithKeys,
|
||||
service: &ApiKeyProviderService,
|
||||
) -> ProviderWithKeysDisplay {
|
||||
let api_keys: Vec<ApiKeyDisplay> = pwk
|
||||
.api_keys
|
||||
.iter()
|
||||
.map(|k| api_key_to_display(k, service))
|
||||
.collect();
|
||||
|
||||
ProviderWithKeysDisplay {
|
||||
provider: provider_to_display(&pwk.provider, pwk.api_keys.len()),
|
||||
api_keys,
|
||||
}
|
||||
}
|
||||
|
||||
/// 为系统 Provider 提供兼容旧版本的别名 ID
|
||||
fn get_legacy_ids(provider_id: &str) -> Vec<String> {
|
||||
match provider_id {
|
||||
"lime-hub" => vec![format!("{}{}", "lobe", "hub")],
|
||||
"google" => vec!["gemini".to_string()],
|
||||
"zhipuai" => vec!["zhipu".to_string()],
|
||||
"alibaba" => vec!["dashscope".to_string(), "qwen".to_string()],
|
||||
"moonshotai" => vec!["moonshot".to_string()],
|
||||
"xai" => vec!["grok".to_string()],
|
||||
"github-models" => vec!["github".to_string()],
|
||||
"github-copilot" => vec!["copilot".to_string()],
|
||||
"google-vertex" => vec!["vertexai".to_string()],
|
||||
"azure-openai" => vec!["azure".to_string()],
|
||||
"amazon-bedrock" => vec!["aws-bedrock".to_string(), "bedrock".to_string()],
|
||||
"togetherai" => vec!["together".to_string()],
|
||||
"fireworks-ai" => vec!["fireworks".to_string(), "fireworksai".to_string()],
|
||||
"xiaomi" => vec!["mimo".to_string(), "xiaomimimo".to_string()],
|
||||
"siliconflow" => vec!["silicon".to_string(), "siliconcloud".to_string()],
|
||||
"302ai" => vec!["ai302".to_string()],
|
||||
"new-api" => vec!["newapi".to_string()],
|
||||
"vercel-gateway" => vec!["vercelaigateway".to_string()],
|
||||
"fal" => vec!["falai".to_string()],
|
||||
"yi" => vec!["zeroone".to_string()],
|
||||
"infini" => vec!["infiniai".to_string()],
|
||||
"doubao" => vec!["volcengine".to_string()],
|
||||
"airgate-openai-images" => vec!["airgate".to_string(), "k8ray".to_string()],
|
||||
"baidu-cloud" => vec!["wenxin".to_string()],
|
||||
"tencent-cloud-ti" => vec!["tencentcloud".to_string()],
|
||||
_ => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn system_provider_to_catalog_item(
|
||||
provider: crate::database::system_providers::SystemProviderDef,
|
||||
) -> SystemProviderCatalogItem {
|
||||
SystemProviderCatalogItem {
|
||||
id: provider.id.to_string(),
|
||||
name: provider.name.to_string(),
|
||||
provider_type: provider.provider_type.to_string(),
|
||||
api_host: provider.api_host.to_string(),
|
||||
group: provider.group.to_string(),
|
||||
sort_order: provider.sort_order,
|
||||
api_version: provider.api_version.map(|v| v.to_string()),
|
||||
legacy_ids: get_legacy_ids(provider.id),
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Tauri 命令
|
||||
// ============================================================================
|
||||
|
||||
/// 获取系统 Provider Catalog
|
||||
///
|
||||
/// 用于前端动态构建 Provider 列表,避免前后端维护两份静态清单。
|
||||
#[tauri::command]
|
||||
pub fn get_system_provider_catalog() -> Result<Vec<SystemProviderCatalogItem>, String> {
|
||||
Ok(get_system_providers()
|
||||
.into_iter()
|
||||
.map(system_provider_to_catalog_item)
|
||||
.collect())
|
||||
}
|
||||
|
||||
/// 获取所有 API Key Provider(包含 API Keys)
|
||||
#[tauri::command]
|
||||
pub fn get_api_key_providers(
|
||||
db: State<'_, DbConnection>,
|
||||
service: State<'_, ApiKeyProviderServiceState>,
|
||||
) -> Result<Vec<ProviderWithKeysDisplay>, String> {
|
||||
let providers = service.0.get_all_providers(&db)?;
|
||||
Ok(providers
|
||||
.iter()
|
||||
.map(|p| provider_with_keys_to_display(p, &service.0))
|
||||
.collect())
|
||||
}
|
||||
|
||||
/// 获取单个 API Key Provider(包含 API Keys)
|
||||
#[tauri::command]
|
||||
pub fn get_api_key_provider(
|
||||
db: State<'_, DbConnection>,
|
||||
service: State<'_, ApiKeyProviderServiceState>,
|
||||
id: String,
|
||||
) -> Result<Option<ProviderWithKeysDisplay>, String> {
|
||||
let provider = service.0.get_provider(&db, &id)?;
|
||||
Ok(provider.map(|p| provider_with_keys_to_display(&p, &service.0)))
|
||||
}
|
||||
|
||||
/// 添加自定义 Provider
|
||||
#[tauri::command]
|
||||
pub fn add_custom_api_key_provider(
|
||||
db: State<'_, DbConnection>,
|
||||
service: State<'_, ApiKeyProviderServiceState>,
|
||||
request: AddCustomProviderRequest,
|
||||
) -> Result<ProviderDisplay, String> {
|
||||
let provider_type: ApiProviderType = request
|
||||
.provider_type
|
||||
.parse()
|
||||
.map_err(|e: String| format!("无效的 Provider 类型: {e}"))?;
|
||||
|
||||
let provider = service.0.add_custom_provider(
|
||||
&db,
|
||||
request.name,
|
||||
provider_type,
|
||||
request.api_host,
|
||||
request.api_version,
|
||||
request.project,
|
||||
request.location,
|
||||
request.region,
|
||||
request
|
||||
.prompt_cache_mode
|
||||
.map(|mode| mode.parse::<ApiProviderPromptCacheMode>())
|
||||
.transpose()
|
||||
.map_err(|e: String| format!("无效的 Prompt Cache 模式: {e}"))?,
|
||||
)?;
|
||||
|
||||
Ok(provider_to_display(&provider, 0))
|
||||
}
|
||||
|
||||
/// 更新 Provider 配置
|
||||
#[tauri::command]
|
||||
pub fn update_api_key_provider(
|
||||
db: State<'_, DbConnection>,
|
||||
service: State<'_, ApiKeyProviderServiceState>,
|
||||
id: String,
|
||||
request: UpdateProviderRequest,
|
||||
) -> Result<ProviderDisplay, String> {
|
||||
// 解析 provider_type(如果提供)
|
||||
let provider_type: Option<ApiProviderType> = request
|
||||
.provider_type
|
||||
.map(|t| t.parse())
|
||||
.transpose()
|
||||
.map_err(|e: String| format!("无效的 Provider 类型: {e}"))?;
|
||||
|
||||
let provider = service.0.update_provider(
|
||||
&db,
|
||||
&id,
|
||||
request.name,
|
||||
provider_type,
|
||||
request.api_host,
|
||||
request.enabled,
|
||||
request.sort_order,
|
||||
request.api_version,
|
||||
request.project,
|
||||
request.location,
|
||||
request.region,
|
||||
request
|
||||
.prompt_cache_mode
|
||||
.map(|mode| mode.parse::<ApiProviderPromptCacheMode>())
|
||||
.transpose()
|
||||
.map_err(|e: String| format!("无效的 Prompt Cache 模式: {e}"))?,
|
||||
request.custom_models,
|
||||
)?;
|
||||
|
||||
// 获取 API Key 数量
|
||||
let full_provider = service.0.get_provider(&db, &id)?;
|
||||
let api_key_count = full_provider.map(|p| p.api_keys.len()).unwrap_or(0);
|
||||
|
||||
Ok(provider_to_display(&provider, api_key_count))
|
||||
}
|
||||
|
||||
/// 删除自定义 Provider
|
||||
#[tauri::command]
|
||||
pub fn delete_custom_api_key_provider(
|
||||
db: State<'_, DbConnection>,
|
||||
service: State<'_, ApiKeyProviderServiceState>,
|
||||
id: String,
|
||||
) -> Result<bool, String> {
|
||||
service.0.delete_custom_provider(&db, &id)
|
||||
}
|
||||
|
||||
/// 添加 API Key
|
||||
#[tauri::command]
|
||||
pub fn add_api_key(
|
||||
db: State<'_, DbConnection>,
|
||||
service: State<'_, ApiKeyProviderServiceState>,
|
||||
request: AddApiKeyRequest,
|
||||
) -> Result<ApiKeyDisplay, String> {
|
||||
let key = service.0.add_api_key(
|
||||
&db,
|
||||
&request.provider_id,
|
||||
&request.api_key,
|
||||
request.alias,
|
||||
request.replace_existing,
|
||||
)?;
|
||||
|
||||
Ok(api_key_to_display(&key, &service.0))
|
||||
}
|
||||
|
||||
/// 删除 API Key
|
||||
#[tauri::command]
|
||||
pub fn delete_api_key(
|
||||
db: State<'_, DbConnection>,
|
||||
service: State<'_, ApiKeyProviderServiceState>,
|
||||
key_id: String,
|
||||
) -> Result<bool, String> {
|
||||
service.0.delete_api_key(&db, &key_id)
|
||||
}
|
||||
|
||||
/// 切换 API Key 启用状态
|
||||
#[tauri::command]
|
||||
pub fn toggle_api_key(
|
||||
db: State<'_, DbConnection>,
|
||||
service: State<'_, ApiKeyProviderServiceState>,
|
||||
key_id: String,
|
||||
enabled: bool,
|
||||
) -> Result<ApiKeyDisplay, String> {
|
||||
let key = service.0.toggle_api_key(&db, &key_id, enabled)?;
|
||||
Ok(api_key_to_display(&key, &service.0))
|
||||
}
|
||||
|
||||
/// 更新 API Key 别名
|
||||
#[tauri::command]
|
||||
pub fn update_api_key_alias(
|
||||
db: State<'_, DbConnection>,
|
||||
service: State<'_, ApiKeyProviderServiceState>,
|
||||
key_id: String,
|
||||
alias: Option<String>,
|
||||
) -> Result<ApiKeyDisplay, String> {
|
||||
let key = service.0.update_api_key_alias(&db, &key_id, alias)?;
|
||||
Ok(api_key_to_display(&key, &service.0))
|
||||
}
|
||||
|
||||
/// 获取下一个可用的 API Key(用于 API 调用)
|
||||
#[tauri::command]
|
||||
pub fn get_next_api_key(
|
||||
db: State<'_, DbConnection>,
|
||||
service: State<'_, ApiKeyProviderServiceState>,
|
||||
provider_id: String,
|
||||
) -> Result<Option<String>, String> {
|
||||
service.0.get_next_api_key(&db, &provider_id)
|
||||
}
|
||||
|
||||
/// 记录 API Key 使用
|
||||
#[tauri::command]
|
||||
pub fn record_api_key_usage(
|
||||
db: State<'_, DbConnection>,
|
||||
service: State<'_, ApiKeyProviderServiceState>,
|
||||
key_id: String,
|
||||
) -> Result<(), String> {
|
||||
service.0.record_usage(&db, &key_id)
|
||||
}
|
||||
|
||||
/// 记录 API Key 错误
|
||||
#[tauri::command]
|
||||
pub fn record_api_key_error(
|
||||
db: State<'_, DbConnection>,
|
||||
service: State<'_, ApiKeyProviderServiceState>,
|
||||
key_id: String,
|
||||
) -> Result<(), String> {
|
||||
service.0.record_error(&db, &key_id)
|
||||
}
|
||||
|
||||
/// 获取 UI 状态
|
||||
#[tauri::command]
|
||||
pub fn get_provider_ui_state(
|
||||
db: State<'_, DbConnection>,
|
||||
service: State<'_, ApiKeyProviderServiceState>,
|
||||
key: String,
|
||||
) -> Result<Option<String>, String> {
|
||||
service.0.get_ui_state(&db, &key)
|
||||
}
|
||||
|
||||
/// 设置 UI 状态
|
||||
#[tauri::command]
|
||||
pub fn set_provider_ui_state(
|
||||
db: State<'_, DbConnection>,
|
||||
service: State<'_, ApiKeyProviderServiceState>,
|
||||
key: String,
|
||||
value: String,
|
||||
) -> Result<(), String> {
|
||||
service.0.set_ui_state(&db, &key, &value)
|
||||
}
|
||||
|
||||
/// 批量更新 Provider 排序顺序
|
||||
/// **Validates: Requirements 8.4**
|
||||
#[tauri::command]
|
||||
pub fn update_provider_sort_orders(
|
||||
db: State<'_, DbConnection>,
|
||||
service: State<'_, ApiKeyProviderServiceState>,
|
||||
sort_orders: Vec<(String, i32)>,
|
||||
) -> Result<(), String> {
|
||||
service.0.update_provider_sort_orders(&db, sort_orders)
|
||||
}
|
||||
|
||||
/// 导出 Provider 配置
|
||||
#[tauri::command]
|
||||
pub fn export_api_key_providers(
|
||||
db: State<'_, DbConnection>,
|
||||
service: State<'_, ApiKeyProviderServiceState>,
|
||||
include_keys: bool,
|
||||
) -> Result<String, String> {
|
||||
let config = service.0.export_config(&db, include_keys)?;
|
||||
serde_json::to_string_pretty(&config).map_err(|e| format!("序列化失败: {e}"))
|
||||
}
|
||||
|
||||
/// 导入 Provider 配置
|
||||
#[tauri::command]
|
||||
pub fn import_api_key_providers(
|
||||
db: State<'_, DbConnection>,
|
||||
service: State<'_, ApiKeyProviderServiceState>,
|
||||
config_json: String,
|
||||
) -> Result<ImportResult, String> {
|
||||
service.0.import_config(&db, &config_json)
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 连接测试命令
|
||||
// ============================================================================
|
||||
|
||||
/// 测试 API Key Provider 连接
|
||||
///
|
||||
/// 方案 C 实现:
|
||||
/// 1. 默认使用 /v1/models 端点测试
|
||||
/// 2. 如果提供了 model_name,用该模型发送简单请求
|
||||
///
|
||||
/// # 参数
|
||||
/// - `provider_id`: Provider ID
|
||||
/// - `model_name`: 可选的模型名称,用于发送测试请求
|
||||
#[tauri::command]
|
||||
pub async fn test_api_key_provider_connection(
|
||||
db: State<'_, DbConnection>,
|
||||
service: State<'_, ApiKeyProviderServiceState>,
|
||||
_model_registry_state: State<'_, crate::commands::model_registry_cmd::ModelRegistryState>,
|
||||
provider_id: String,
|
||||
model_name: Option<String>,
|
||||
) -> Result<ConnectionTestResult, String> {
|
||||
service
|
||||
.0
|
||||
.test_connection_with_fallback_models(&db, &provider_id, model_name, Vec::new())
|
||||
.await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn test_api_key_provider_chat(
|
||||
db: State<'_, DbConnection>,
|
||||
service: State<'_, ApiKeyProviderServiceState>,
|
||||
_model_registry_state: State<'_, crate::commands::model_registry_cmd::ModelRegistryState>,
|
||||
provider_id: String,
|
||||
model_name: Option<String>,
|
||||
prompt: String,
|
||||
) -> Result<ChatTestResult, String> {
|
||||
service
|
||||
.0
|
||||
.test_chat_with_fallback_models(&db, &provider_id, model_name, prompt, Vec::new())
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -1,245 +0,0 @@
|
||||
//! 自动化任务命令
|
||||
|
||||
use crate::app::AppState;
|
||||
use crate::config::GlobalConfigManagerState;
|
||||
use crate::database::{lock_db, DbConnection};
|
||||
use crate::services::automation_service::health::{
|
||||
query_automation_health, AutomationHealthQuery, AutomationHealthResult,
|
||||
};
|
||||
use crate::services::automation_service::schedule::{
|
||||
preview_next_run as preview_next_run_for_schedule, validate_schedule as validate_schedule_value,
|
||||
};
|
||||
use crate::services::automation_service::{
|
||||
AutomationCycleResult, AutomationJobDraft, AutomationJobRecord, AutomationJobUpdate,
|
||||
AutomationPayload, AutomationServiceState, AutomationStatus,
|
||||
};
|
||||
use lime_core::config::{AutomationExecutionMode, DeliveryConfig, TaskSchedule};
|
||||
use lime_core::database::dao::agent_run::AgentRun;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tauri::{AppHandle, State};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AutomationSchedulerConfigResponse {
|
||||
pub enabled: bool,
|
||||
pub poll_interval_secs: u64,
|
||||
pub enable_history: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AutomationJobRequest {
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
pub enabled: Option<bool>,
|
||||
pub workspace_id: String,
|
||||
pub execution_mode: Option<AutomationExecutionMode>,
|
||||
pub schedule: TaskSchedule,
|
||||
pub payload: AutomationPayload,
|
||||
pub delivery: Option<DeliveryConfig>,
|
||||
pub timeout_secs: Option<u64>,
|
||||
pub max_retries: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct UpdateAutomationJobRequest {
|
||||
pub name: Option<String>,
|
||||
pub description: Option<String>,
|
||||
pub enabled: Option<bool>,
|
||||
pub workspace_id: Option<String>,
|
||||
pub execution_mode: Option<AutomationExecutionMode>,
|
||||
pub schedule: Option<TaskSchedule>,
|
||||
pub payload: Option<AutomationPayload>,
|
||||
pub delivery: Option<DeliveryConfig>,
|
||||
pub timeout_secs: Option<u64>,
|
||||
pub clear_timeout_secs: Option<bool>,
|
||||
pub max_retries: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ScheduleValidationResult {
|
||||
pub valid: bool,
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_automation_scheduler_config(
|
||||
config_manager: State<'_, GlobalConfigManagerState>,
|
||||
) -> Result<AutomationSchedulerConfigResponse, String> {
|
||||
let config = config_manager.0.config();
|
||||
Ok(AutomationSchedulerConfigResponse {
|
||||
enabled: config.automation.enabled,
|
||||
poll_interval_secs: config.automation.poll_interval_secs,
|
||||
enable_history: config.automation.enable_history,
|
||||
})
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn update_automation_scheduler_config(
|
||||
state: State<'_, AppState>,
|
||||
config_manager: State<'_, GlobalConfigManagerState>,
|
||||
automation_state: State<'_, AutomationServiceState>,
|
||||
config: AutomationSchedulerConfigResponse,
|
||||
app: AppHandle,
|
||||
) -> Result<(), String> {
|
||||
let was_enabled = {
|
||||
let state = state.read().await;
|
||||
state.config.automation.enabled
|
||||
};
|
||||
|
||||
let next_config = {
|
||||
let mut state = state.write().await;
|
||||
state.config.automation.enabled = config.enabled;
|
||||
state.config.automation.poll_interval_secs = config.poll_interval_secs.max(5);
|
||||
state.config.automation.enable_history = config.enable_history;
|
||||
state.config.clone()
|
||||
};
|
||||
config_manager.0.save_config(&next_config).await?;
|
||||
|
||||
let new_config = next_config.automation.clone();
|
||||
let mut service = automation_state.0.write().await;
|
||||
service.update_config(new_config);
|
||||
service.set_app_handle(app);
|
||||
let self_ref = automation_state.0.clone();
|
||||
if config.enabled && !was_enabled {
|
||||
service.start(self_ref).await?;
|
||||
} else if !config.enabled && was_enabled {
|
||||
service.stop().await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_automation_status(
|
||||
automation_state: State<'_, AutomationServiceState>,
|
||||
) -> Result<AutomationStatus, String> {
|
||||
Ok(automation_state.1.read().clone())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_automation_jobs(
|
||||
automation_state: State<'_, AutomationServiceState>,
|
||||
) -> Result<Vec<AutomationJobRecord>, String> {
|
||||
let service = automation_state.0.read().await;
|
||||
service.list_jobs()
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_automation_job(
|
||||
automation_state: State<'_, AutomationServiceState>,
|
||||
id: String,
|
||||
) -> Result<Option<AutomationJobRecord>, String> {
|
||||
let service = automation_state.0.read().await;
|
||||
service.get_job(id.trim())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn create_automation_job(
|
||||
automation_state: State<'_, AutomationServiceState>,
|
||||
request: AutomationJobRequest,
|
||||
) -> Result<AutomationJobRecord, String> {
|
||||
let service = automation_state.0.read().await;
|
||||
service.create_job(AutomationJobDraft {
|
||||
name: request.name,
|
||||
description: request.description,
|
||||
enabled: request.enabled.unwrap_or(true),
|
||||
workspace_id: request.workspace_id,
|
||||
execution_mode: request
|
||||
.execution_mode
|
||||
.unwrap_or(AutomationExecutionMode::Intelligent),
|
||||
schedule: request.schedule,
|
||||
payload: request.payload,
|
||||
delivery: request.delivery.unwrap_or_default(),
|
||||
timeout_secs: request.timeout_secs,
|
||||
max_retries: request.max_retries.unwrap_or(3),
|
||||
})
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn update_automation_job(
|
||||
automation_state: State<'_, AutomationServiceState>,
|
||||
id: String,
|
||||
request: UpdateAutomationJobRequest,
|
||||
) -> Result<AutomationJobRecord, String> {
|
||||
let service = automation_state.0.read().await;
|
||||
service.update_job(
|
||||
id.trim(),
|
||||
AutomationJobUpdate {
|
||||
name: request.name,
|
||||
description: request.description,
|
||||
enabled: request.enabled,
|
||||
workspace_id: request.workspace_id,
|
||||
execution_mode: request.execution_mode,
|
||||
schedule: request.schedule,
|
||||
payload: request.payload,
|
||||
delivery: request.delivery,
|
||||
timeout_secs: if request.clear_timeout_secs.unwrap_or(false) {
|
||||
Some(None)
|
||||
} else {
|
||||
request.timeout_secs.map(Some)
|
||||
},
|
||||
max_retries: request.max_retries,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn delete_automation_job(
|
||||
automation_state: State<'_, AutomationServiceState>,
|
||||
id: String,
|
||||
) -> Result<bool, String> {
|
||||
let service = automation_state.0.read().await;
|
||||
service.delete_job(id.trim())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn run_automation_job_now(
|
||||
automation_state: State<'_, AutomationServiceState>,
|
||||
id: String,
|
||||
) -> Result<AutomationCycleResult, String> {
|
||||
let service = automation_state.0.read().await;
|
||||
service.run_job_now(id.trim()).await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_automation_health(
|
||||
db: State<'_, DbConnection>,
|
||||
query: Option<AutomationHealthQuery>,
|
||||
) -> Result<AutomationHealthResult, String> {
|
||||
query_automation_health(db.inner(), query)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_automation_run_history(
|
||||
db: State<'_, DbConnection>,
|
||||
id: String,
|
||||
limit: Option<usize>,
|
||||
) -> Result<Vec<AgentRun>, String> {
|
||||
let conn = lock_db(db.inner())?;
|
||||
lime_core::database::dao::agent_run::AgentRunDao::list_runs_by_source_ref(
|
||||
&conn,
|
||||
"automation",
|
||||
id.trim(),
|
||||
limit.unwrap_or(20),
|
||||
)
|
||||
.map_err(|e| format!("查询自动化运行历史失败: {e}"))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn preview_automation_schedule(schedule: TaskSchedule) -> Result<Option<String>, String> {
|
||||
preview_next_run_for_schedule(&schedule).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn validate_automation_schedule(
|
||||
schedule: TaskSchedule,
|
||||
) -> Result<ScheduleValidationResult, String> {
|
||||
match validate_schedule_value(&schedule, chrono::Utc::now()) {
|
||||
Ok(()) => Ok(ScheduleValidationResult {
|
||||
valid: true,
|
||||
error: None,
|
||||
}),
|
||||
Err(error) => Ok(ScheduleValidationResult {
|
||||
valid: false,
|
||||
error: Some(error.to_string()),
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ pub mod agent_cmd;
|
||||
pub mod api_key_provider_cmd;
|
||||
pub mod asr_cmd;
|
||||
pub mod aster_agent_cmd;
|
||||
pub mod automation_cmd;
|
||||
pub mod auxiliary_model_selection;
|
||||
pub mod browser_connector_cmd;
|
||||
pub mod browser_environment_cmd;
|
||||
|
||||
@@ -28,7 +28,7 @@ pub async fn get_model_registry(
|
||||
|
||||
/// 获取模型注册表中所有 provider_id(去重且有序)。
|
||||
///
|
||||
/// 本地模型目录已下线,Provider 元信息改由 `get_system_provider_catalog`
|
||||
/// 本地模型目录已下线,Provider 元信息改由 App Server `modelProvider/catalog/list`
|
||||
/// 和用户配置提供;这里保留命令兼容并返回空集合。
|
||||
#[tauri::command]
|
||||
pub async fn get_model_registry_provider_ids(
|
||||
@@ -217,84 +217,3 @@ pub async fn fetch_provider_models_from_api(
|
||||
.fetch_models_from_api(&provider_id, &api_host, &api_key)
|
||||
.await
|
||||
}
|
||||
|
||||
/// 从 Provider API 获取模型列表(自动获取 API Key)
|
||||
///
|
||||
/// 自动从数据库获取 Provider 的 API Key,然后调用 /v1/models 端点
|
||||
///
|
||||
/// # 参数
|
||||
/// - `provider_id`: Provider ID(如 "siliconflow", "openai")
|
||||
#[tauri::command]
|
||||
pub async fn fetch_provider_models_auto(
|
||||
state: State<'_, ModelRegistryState>,
|
||||
db: tauri::State<'_, crate::database::DbConnection>,
|
||||
api_key_service: tauri::State<
|
||||
'_,
|
||||
crate::commands::api_key_provider_cmd::ApiKeyProviderServiceState,
|
||||
>,
|
||||
provider_id: String,
|
||||
) -> Result<FetchModelsResult, String> {
|
||||
// 获取 Provider 信息
|
||||
let provider = api_key_service
|
||||
.0
|
||||
.get_provider(&db, &provider_id)?
|
||||
.ok_or_else(|| format!("Provider 不存在: {provider_id}"))?;
|
||||
|
||||
// 获取 API Host
|
||||
let api_host = provider.provider.api_host.clone();
|
||||
if api_host.is_empty() {
|
||||
return Err("Provider 没有配置 API Host".to_string());
|
||||
}
|
||||
|
||||
let provider_type = provider.provider.effective_provider_type();
|
||||
|
||||
{
|
||||
let guard = state.read().await;
|
||||
let service = guard
|
||||
.as_ref()
|
||||
.ok_or_else(|| "模型注册服务未初始化".to_string())?;
|
||||
match service.get_cached_provider_models(&provider_id, &api_host, Some(provider_type)) {
|
||||
Ok(Some(cached)) => return Ok(cached),
|
||||
Ok(None) => {}
|
||||
Err(error) => {
|
||||
tracing::warn!(
|
||||
"[ModelRegistry] 读取 Provider 模型缓存失败,继续获取 API Key: {error}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let requires_api_key = ModelRegistryService::requires_api_key_for_model_fetch(
|
||||
&provider_id,
|
||||
&api_host,
|
||||
provider_type,
|
||||
);
|
||||
|
||||
// 获取 API Key(支持本地免 Key 渠道)
|
||||
let api_key = if requires_api_key {
|
||||
api_key_service
|
||||
.0
|
||||
.get_next_api_key(&db, &provider_id)?
|
||||
.ok_or_else(|| format!("Provider {provider_id} 没有可用的 API Key"))?
|
||||
} else {
|
||||
api_key_service
|
||||
.0
|
||||
.get_next_api_key(&db, &provider_id)?
|
||||
.unwrap_or_default()
|
||||
};
|
||||
|
||||
let guard = state.read().await;
|
||||
let service = guard
|
||||
.as_ref()
|
||||
.ok_or_else(|| "模型注册服务未初始化".to_string())?;
|
||||
|
||||
service
|
||||
.fetch_models_from_api_with_hints(
|
||||
&provider_id,
|
||||
&api_host,
|
||||
&api_key,
|
||||
Some(provider_type),
|
||||
&provider.provider.custom_models,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
mod agent_apps;
|
||||
mod agent_sessions;
|
||||
mod app_runtime;
|
||||
mod automation;
|
||||
mod browser;
|
||||
mod capability_drafts;
|
||||
mod channels;
|
||||
@@ -105,10 +104,6 @@ pub async fn handle_command(
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
if let Some(result) = automation::try_handle(state, cmd, args.as_ref()).await? {
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
if let Some(result) = logs::try_handle(state, cmd, args.as_ref()).await? {
|
||||
return Ok(result);
|
||||
}
|
||||
@@ -863,23 +858,6 @@ mod tests {
|
||||
assert!(error.to_string().contains("Dev Bridge 未持有 AppHandle"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn fetch_provider_models_auto_is_bridged() {
|
||||
let state = make_test_state();
|
||||
|
||||
let error = handle_command(
|
||||
&state,
|
||||
"fetch_provider_models_auto",
|
||||
Some(serde_json::json!({
|
||||
"providerId": "ollama"
|
||||
})),
|
||||
)
|
||||
.await
|
||||
.expect_err("missing provider should fail after bridge routing");
|
||||
|
||||
assert!(error.to_string().contains("Provider 不存在: ollama"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_provider_alias_config_is_bridged() {
|
||||
let state = make_test_state();
|
||||
@@ -898,123 +876,35 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn api_key_provider_write_flow_is_bridged() {
|
||||
async fn retired_api_key_provider_commands_are_not_dev_bridge_facades() {
|
||||
let state = make_test_state();
|
||||
|
||||
let created = handle_command(
|
||||
&state,
|
||||
"add_custom_api_key_provider",
|
||||
Some(serde_json::json!({
|
||||
"request": {
|
||||
"name": "SenseNova E2E",
|
||||
"type": "openai",
|
||||
"api_host": "https://api.sensenova.cn/compatible-mode/v2"
|
||||
}
|
||||
})),
|
||||
)
|
||||
.await
|
||||
.expect("custom provider should be created through bridge");
|
||||
let provider_id = created["id"].as_str().unwrap().to_string();
|
||||
|
||||
handle_command(
|
||||
&state,
|
||||
"update_api_key_provider",
|
||||
Some(serde_json::json!({
|
||||
"id": provider_id,
|
||||
"request": {
|
||||
"enabled": true,
|
||||
"custom_models": ["SenseChat-5"]
|
||||
}
|
||||
})),
|
||||
)
|
||||
.await
|
||||
.expect("custom provider should be updated through bridge");
|
||||
|
||||
let key = handle_command(
|
||||
&state,
|
||||
"add_api_key",
|
||||
Some(serde_json::json!({
|
||||
"request": {
|
||||
"provider_id": provider_id,
|
||||
"api_key": "sk-bridge-test",
|
||||
"alias": "bridge"
|
||||
}
|
||||
})),
|
||||
)
|
||||
.await
|
||||
.expect("api key should be added through bridge");
|
||||
|
||||
assert_eq!(key["provider_id"], created["id"]);
|
||||
assert_ne!(key["api_key_masked"], "sk-bridge-test");
|
||||
|
||||
let provider = handle_command(
|
||||
&state,
|
||||
for command in [
|
||||
"get_api_key_provider",
|
||||
Some(serde_json::json!({
|
||||
"id": created["id"].as_str().unwrap()
|
||||
})),
|
||||
)
|
||||
.await
|
||||
.expect("custom provider should be readable through bridge");
|
||||
|
||||
assert_eq!(provider["id"], created["id"]);
|
||||
assert_eq!(
|
||||
provider["api_host"],
|
||||
"https://api.sensenova.cn/compatible-mode/v2"
|
||||
);
|
||||
assert_eq!(provider["custom_models"][0], "SenseChat-5");
|
||||
assert_eq!(provider["api_keys"].as_array().unwrap().len(), 1);
|
||||
|
||||
let deleted = handle_command(
|
||||
&state,
|
||||
"add_custom_api_key_provider",
|
||||
"update_api_key_provider",
|
||||
"delete_custom_api_key_provider",
|
||||
Some(serde_json::json!({
|
||||
"id": created["id"].as_str().unwrap()
|
||||
})),
|
||||
)
|
||||
.await
|
||||
.expect("custom provider should be deleted through bridge");
|
||||
|
||||
assert_eq!(deleted, serde_json::json!(true));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_api_key_provider_connection_is_bridged() {
|
||||
let state = make_test_state();
|
||||
|
||||
let error = handle_command(
|
||||
&state,
|
||||
"add_api_key",
|
||||
"delete_api_key",
|
||||
"toggle_api_key",
|
||||
"update_api_key_alias",
|
||||
"get_next_api_key",
|
||||
"record_api_key_usage",
|
||||
"record_api_key_error",
|
||||
"get_provider_ui_state",
|
||||
"set_provider_ui_state",
|
||||
"update_provider_sort_orders",
|
||||
"export_api_key_providers",
|
||||
"import_api_key_providers",
|
||||
"test_api_key_provider_connection",
|
||||
Some(serde_json::json!({
|
||||
"providerId": "missing-provider"
|
||||
})),
|
||||
)
|
||||
.await
|
||||
.expect_err("missing provider should fail after bridge routing");
|
||||
|
||||
assert!(error
|
||||
.to_string()
|
||||
.contains("Provider 不存在: missing-provider"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_api_key_provider_chat_is_bridged() {
|
||||
let state = make_test_state();
|
||||
|
||||
let error = handle_command(
|
||||
&state,
|
||||
"test_api_key_provider_chat",
|
||||
Some(serde_json::json!({
|
||||
"providerId": "missing-provider",
|
||||
"prompt": "hello"
|
||||
})),
|
||||
)
|
||||
.await
|
||||
.expect_err("missing provider should fail after bridge routing");
|
||||
] {
|
||||
let error = handle_command(&state, command, Some(serde_json::json!({})))
|
||||
.await
|
||||
.expect_err("retired API Key Provider command should be unknown");
|
||||
|
||||
assert!(error
|
||||
.to_string()
|
||||
.contains("Provider 不存在: missing-provider"));
|
||||
assert!(error.to_string().contains("[DevBridge] 未知命令"));
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -54,31 +54,6 @@ pub async fn try_handle(
|
||||
crate::commands::agent_app_cmd::agent_app_uninstall(request).await?,
|
||||
)?
|
||||
}
|
||||
"agent_app_start_ui_runtime" => {
|
||||
let request = parse_nested_arg(&args_or_default(args), "request")?;
|
||||
let config = { state.server.read().await.config.clone() };
|
||||
serde_json::to_value(
|
||||
crate::commands::agent_app_cmd::agent_app_start_ui_runtime_for_dev_bridge(
|
||||
request,
|
||||
&config,
|
||||
state.db.as_ref(),
|
||||
Some(state.api_key_provider_service.as_ref()),
|
||||
)
|
||||
.await?,
|
||||
)?
|
||||
}
|
||||
"agent_app_get_ui_runtime_status" => {
|
||||
let request = parse_nested_arg(&args_or_default(args), "request")?;
|
||||
serde_json::to_value(
|
||||
crate::commands::agent_app_cmd::agent_app_get_ui_runtime_status(request).await?,
|
||||
)?
|
||||
}
|
||||
"agent_app_stop_ui_runtime" => {
|
||||
let request = parse_nested_arg(&args_or_default(args), "request")?;
|
||||
serde_json::to_value(
|
||||
crate::commands::agent_app_cmd::agent_app_stop_ui_runtime(request).await?,
|
||||
)?
|
||||
}
|
||||
"agent_app_select_directory" => {
|
||||
let request = parse_optional_nested_arg(&args_or_default(args), "request")?;
|
||||
let app_handle = require_app_handle(state)?;
|
||||
@@ -106,90 +81,6 @@ pub async fn try_handle(
|
||||
.await?,
|
||||
)?
|
||||
}
|
||||
"agent_app_runtime_start_task" => {
|
||||
let request = parse_nested_arg(&args_or_default(args), "request")?;
|
||||
let app_handle = require_app_handle(state)?;
|
||||
let aster_state = app_handle.state::<crate::agent::AsterAgentState>();
|
||||
let db = app_handle.state::<crate::database::DbConnection>();
|
||||
let api_key_provider_service =
|
||||
app_handle
|
||||
.state::<crate::commands::api_key_provider_cmd::ApiKeyProviderServiceState>();
|
||||
let logs = app_handle.state::<crate::app::LogState>();
|
||||
let config_manager = app_handle.state::<crate::config::GlobalConfigManagerState>();
|
||||
let mcp_manager = app_handle.state::<crate::mcp::McpManagerState>();
|
||||
let automation_state =
|
||||
app_handle.state::<crate::services::automation_service::AutomationServiceState>();
|
||||
serde_json::to_value(
|
||||
crate::commands::agent_app_runtime_cmd::start_task::agent_app_runtime_start_task(
|
||||
app_handle.clone(),
|
||||
aster_state,
|
||||
db,
|
||||
api_key_provider_service,
|
||||
logs,
|
||||
config_manager,
|
||||
mcp_manager,
|
||||
automation_state,
|
||||
request,
|
||||
)
|
||||
.await?,
|
||||
)?
|
||||
}
|
||||
"agent_app_runtime_cancel_task" => {
|
||||
let request = parse_nested_arg(&args_or_default(args), "request")?;
|
||||
let app_handle = require_app_handle(state)?;
|
||||
let aster_state = app_handle.state::<crate::agent::AsterAgentState>();
|
||||
serde_json::to_value(
|
||||
crate::commands::agent_app_runtime_cmd::cancel_task::agent_app_runtime_cancel_task(
|
||||
app_handle.clone(),
|
||||
aster_state,
|
||||
request,
|
||||
)
|
||||
.await?,
|
||||
)?
|
||||
}
|
||||
"agent_app_runtime_get_task" => {
|
||||
let request = parse_nested_arg(&args_or_default(args), "request")?;
|
||||
let app_handle = require_app_handle(state)?;
|
||||
let aster_state = app_handle.state::<crate::agent::AsterAgentState>();
|
||||
let db = app_handle.state::<crate::database::DbConnection>();
|
||||
let api_key_provider_service =
|
||||
app_handle
|
||||
.state::<crate::commands::api_key_provider_cmd::ApiKeyProviderServiceState>();
|
||||
let logs = app_handle.state::<crate::app::LogState>();
|
||||
let config_manager = app_handle.state::<crate::config::GlobalConfigManagerState>();
|
||||
let mcp_manager = app_handle.state::<crate::mcp::McpManagerState>();
|
||||
let automation_state =
|
||||
app_handle.state::<crate::services::automation_service::AutomationServiceState>();
|
||||
serde_json::to_value(
|
||||
crate::commands::agent_app_runtime_cmd::task_snapshot::agent_app_runtime_get_task(
|
||||
app_handle.clone(),
|
||||
aster_state,
|
||||
db,
|
||||
api_key_provider_service,
|
||||
logs,
|
||||
config_manager,
|
||||
mcp_manager,
|
||||
automation_state,
|
||||
request,
|
||||
)
|
||||
.await?,
|
||||
)?
|
||||
}
|
||||
"agent_app_runtime_submit_host_response" => {
|
||||
let request = parse_nested_arg(&args_or_default(args), "request")?;
|
||||
let app_handle = require_app_handle(state)?;
|
||||
let aster_state = app_handle.state::<crate::agent::AsterAgentState>();
|
||||
let db = app_handle.state::<crate::database::DbConnection>();
|
||||
serde_json::to_value(
|
||||
crate::commands::agent_app_runtime_cmd::host_response::agent_app_runtime_submit_host_response(
|
||||
app_handle.clone(),
|
||||
aster_state,
|
||||
db,
|
||||
request,
|
||||
)
|
||||
.await?,
|
||||
)?
|
||||
}
|
||||
_ => return Ok(None),
|
||||
};
|
||||
|
||||
|
||||
@@ -87,7 +87,6 @@ pub(super) async fn try_handle(
|
||||
| "agent_runtime_diff_file_checkpoint"
|
||||
| "agent_runtime_restore_file_checkpoint"
|
||||
| "agent_runtime_get_tool_inventory"
|
||||
| "agent_runtime_list_workspace_skill_bindings"
|
||||
| "agent_runtime_replay_request"
|
||||
| "agent_runtime_update_session"
|
||||
| "agent_runtime_delete_session"
|
||||
@@ -658,18 +657,6 @@ pub(super) async fn try_handle(
|
||||
.await?,
|
||||
)?
|
||||
}
|
||||
"agent_runtime_list_workspace_skill_bindings" => {
|
||||
let request = parse_request::<
|
||||
crate::commands::aster_agent_cmd::AgentRuntimeListWorkspaceSkillBindingsRequest,
|
||||
>(args)?;
|
||||
|
||||
serde_json::to_value(
|
||||
crate::commands::aster_agent_cmd::agent_runtime_list_workspace_skill_bindings(
|
||||
request,
|
||||
)
|
||||
.await?,
|
||||
)?
|
||||
}
|
||||
"agent_runtime_replay_request" => {
|
||||
let request = parse_request::<
|
||||
crate::commands::aster_agent_cmd::AgentRuntimeReplayRequestRequest,
|
||||
|
||||
@@ -36,15 +36,6 @@ pub(super) async fn try_handle(
|
||||
.await;
|
||||
serde_json::to_value(preview)?
|
||||
}
|
||||
"get_default_provider" => {
|
||||
let default_provider_ref = { state.server.read().await.default_provider_ref.clone() };
|
||||
let provider = default_provider_ref.read().await.clone();
|
||||
serde_json::json!(provider)
|
||||
}
|
||||
"get_endpoint_providers" => {
|
||||
let providers = { state.server.read().await.config.endpoint_providers.clone() };
|
||||
serde_json::to_value(providers)?
|
||||
}
|
||||
"get_server_diagnostics" => {
|
||||
let (status, capability_routing, response_cache, request_dedup, idempotency) = {
|
||||
let server = state.server.read().await;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user