Release v0.90.0

This commit is contained in:
coso
2026-03-18 10:39:02 +08:00
parent 157e400c97
commit 8c2cf9f97b
225 changed files with 11935 additions and 7890 deletions
+135 -24
View File
@@ -3,6 +3,7 @@
import fs from "node:fs";
import path from "node:path";
import process from "node:process";
import agentCommandCatalog from "../src/lib/governance/agentCommandCatalog.json" with { type: "json" };
const repoRoot = path.resolve(process.cwd());
const sourceRoots = ["src"];
@@ -27,6 +28,16 @@ const ignoredDirs = new Set([
".next",
]);
const agentLegacyCommandSurfaceMonitors =
agentCommandCatalog.legacyCommandSurfaceMonitors;
const agentLegacyHelperSurfaceMonitors = (
agentCommandCatalog.legacyHelperSurfaceMonitors ?? []
).map(({ helpers, ...monitor }) => ({
...monitor,
patterns: helpers.map((helper) => `${helper}(`),
}));
const importSurfaceMonitors = [
{
id: "general-chat-root-entry",
@@ -70,6 +81,20 @@ const importSurfaceMonitors = [
targets: ["src/lib/api/agentCompat.ts"],
allowedPaths: [],
},
{
id: "agent-api-facade-entry",
classification: "dead-candidate",
description: "已删除 Agent API 门面入口",
targets: ["src/lib/api/agent.ts"],
allowedPaths: [],
},
{
id: "agent-legacy-hook-entry",
classification: "dead-candidate",
description: "旧 Agent Chat Hook 入口",
targets: ["src/components/agent/chat/hooks/useAgentChat.ts"],
allowedPaths: [],
},
{
id: "heartbeat-api-gateway",
classification: "deprecated",
@@ -91,9 +116,24 @@ const importSurfaceMonitors = [
targets: ["src/components/settings-v2/agent/assistant/index.tsx"],
allowedPaths: [],
},
{
id: "stores-root-barrel-entry",
classification: "dead-candidate",
description: "旧 stores 根 barrel 入口",
targets: ["src/stores/index.ts"],
allowedPaths: [],
},
{
id: "agent-legacy-store-entry",
classification: "dead-candidate",
description: "旧 Agent Zustand store 入口",
targets: ["src/stores/agentStore.ts"],
allowedPaths: [],
},
];
const commandSurfaceMonitors = [
...agentLegacyCommandSurfaceMonitors,
{
id: "general-chat-compat-commands",
classification: "compat",
@@ -166,6 +206,7 @@ const commandSurfaceMonitors = [
];
const frontendTextSurfaceMonitors = [
...agentLegacyHelperSurfaceMonitors,
{
id: "frontend-assistant-settings-surfaces",
classification: "deprecated",
@@ -182,6 +223,13 @@ const frontendTextSurfaceMonitors = [
],
allowedPaths: [],
},
{
id: "stores-root-barrel-imports",
classification: "deprecated",
description: "从 @/stores 根 barrel 回流遗留 store 导入",
patterns: ['from "@/stores"', "from '@/stores'"],
allowedPaths: [],
},
];
const rustTextSurfaceMonitors = [
@@ -302,6 +350,57 @@ const rustTextSurfaceMonitors = [
],
allowedPaths: [],
},
{
id: "rust-service-agent-table-query-leak",
classification: "deprecated",
description: "Tauri service 层 direct agent_sessions/agent_messages 查询回流",
patterns: [
"FROM agent_sessions s",
"FROM agent_messages m",
"JOIN agent_sessions s ON s.id = m.session_id",
],
includePathPrefixes: ["src-tauri/src/services"],
allowedPaths: [],
},
{
id: "rust-service-model-usage-table-query-leak",
classification: "deprecated",
description: "Tauri service 层 direct model_usage_stats 查询回流",
patterns: ["FROM model_usage_stats", "SELECT COUNT(*) FROM model_usage_stats"],
includePathPrefixes: ["src-tauri/src/services"],
allowedPaths: [],
},
{
id: "rust-agent-session-direct-record-access",
classification: "deprecated",
description: "Rust 上层模块 direct Agent session 记录与消息读写回流",
patterns: [
"AgentDao::get_session(",
"AgentDao::list_sessions(",
"AgentDao::get_message_count(",
"AgentDao::get_messages(",
"AgentDao::session_exists(",
"AgentDao::update_title(",
"AgentDao::update_session_time(",
"AgentDao::update_working_dir(",
"AgentDao::update_execution_strategy(",
],
allowedPaths: ["src-tauri/crates/agent/src/session_store.rs"],
},
{
id: "rust-agent-session-direct-delete",
classification: "deprecated",
description: "Rust 业务层 direct AgentDao::delete_session 回流",
patterns: ["AgentDao::delete_session("],
allowedPaths: [],
},
{
id: "rust-agent-session-direct-create",
classification: "deprecated",
description: "Rust 业务层 direct AgentDao::create_session 回流",
patterns: ["AgentDao::create_session("],
allowedPaths: ["src-tauri/crates/agent/src/session_store.rs"],
},
{
id: "rust-heartbeat-business-surfaces",
classification: "deprecated",
@@ -315,7 +414,7 @@ const rustTextSurfaceMonitors = [
"heartbeat_tool",
"RunSource::Heartbeat",
"source = 'heartbeat'",
"source: \"heartbeat\".to_string()",
'source: "heartbeat".to_string()',
],
allowedPaths: [],
},
@@ -331,7 +430,10 @@ const rustTextSurfaceMonitors = [
"show_suggestions",
"auto_select",
],
includePathPrefixes: ["src-tauri/crates/core/src/config", "src-tauri/src/config"],
includePathPrefixes: [
"src-tauri/crates/core/src/config",
"src-tauri/src/config",
],
allowedPaths: [],
},
{
@@ -339,13 +441,13 @@ const rustTextSurfaceMonitors = [
classification: "deprecated",
description: "Rust 迁移 settings 标记字符串扩散",
patterns: [
"\"migrated_api_keys_to_pool\"",
"\"migrated_provider_ids_v1\"",
"\"cleaned_legacy_api_key_credentials\"",
"\"migrated_mcp_lime_enabled\"",
"\"migrated_mcp_created_at_to_integer\"",
"\"model_registry_refresh_needed\"",
"\"model_registry_version\"",
'"migrated_api_keys_to_pool"',
'"migrated_provider_ids_v1"',
'"cleaned_legacy_api_key_credentials"',
'"migrated_mcp_lime_enabled"',
'"migrated_mcp_created_at_to_integer"',
'"model_registry_refresh_needed"',
'"model_registry_version"',
],
allowedPaths: [
"src-tauri/crates/core/src/database/migration.rs",
@@ -408,9 +510,9 @@ const rustTextSurfaceMonitors = [
classification: "deprecated",
description: "versioned migration 直接手写事务样板回流",
patterns: [
"conn.execute(\"BEGIN TRANSACTION\"",
"conn.execute(\"COMMIT\"",
"conn.execute(\"ROLLBACK\"",
'conn.execute("BEGIN TRANSACTION"',
'conn.execute("COMMIT"',
'conn.execute("ROLLBACK"',
],
includePathPrefixes: ["src-tauri/crates/core/src/database/migration_v"],
allowedPaths: [],
@@ -419,7 +521,7 @@ const rustTextSurfaceMonitors = [
id: "rust-hardcoded-projects-path-leak",
classification: "deprecated",
description: "数据库迁移硬编码 legacy projects 路径",
patterns: ["\".lime/projects\"", "join(\".lime\").join(\"projects\")"],
patterns: ['".lime/projects"', 'join(".lime").join("projects")'],
includePathPrefixes: ["src-tauri/crates/core/src/database"],
allowedPaths: [],
},
@@ -427,7 +529,7 @@ const rustTextSurfaceMonitors = [
id: "rust-hardcoded-session-files-path-leak",
classification: "deprecated",
description: "session files 硬编码 legacy sessions 路径",
patterns: ["~/.lime/sessions", "join(\".lime\").join(\"sessions\")"],
patterns: ["~/.lime/sessions", 'join(".lime").join("sessions")'],
includePathPrefixes: ["src-tauri/crates/core/src/session_files"],
allowedPaths: [],
},
@@ -435,7 +537,7 @@ const rustTextSurfaceMonitors = [
id: "rust-hardcoded-legacy-config-path-leak",
classification: "deprecated",
description: "数据库迁移硬编码 legacy config 路径",
patterns: ["~/.lime/config.json", "join(\".lime\").join(\"config.json\")"],
patterns: ["~/.lime/config.json", 'join(".lime").join("config.json")'],
includePathPrefixes: ["src-tauri/crates/core/src/database"],
allowedPaths: [],
},
@@ -443,7 +545,7 @@ const rustTextSurfaceMonitors = [
id: "rust-hardcoded-workspace-projects-path-leak",
classification: "deprecated",
description: "上层命令或桥接层硬编码 workspace projects 路径",
patterns: ["~/.lime/projects", "join(\".lime\").join(\"projects\")"],
patterns: ["~/.lime/projects", 'join(".lime").join("projects")'],
includePathPrefixes: ["src-tauri/src"],
allowedPaths: [],
},
@@ -451,7 +553,7 @@ const rustTextSurfaceMonitors = [
id: "rust-hardcoded-logger-path-leak",
classification: "deprecated",
description: "logger fallback 硬编码 legacy logs 路径",
patterns: ["~/.lime/logs", "join(\".lime\").join(\"logs\")"],
patterns: ["~/.lime/logs", 'join(".lime").join("logs")'],
includePathPrefixes: ["src-tauri/crates/core/src/logger.rs"],
allowedPaths: [],
},
@@ -459,7 +561,7 @@ const rustTextSurfaceMonitors = [
id: "rust-hardcoded-skills-path-leak",
classification: "deprecated",
description: "skills 相关模块硬编码 legacy skills 路径",
patterns: ["~/.lime/skills", "join(\".lime\").join(\"skills\")"],
patterns: ["~/.lime/skills", 'join(".lime").join("skills")'],
includePathPrefixes: ["src-tauri/src"],
allowedPaths: [],
},
@@ -469,8 +571,8 @@ const rustTextSurfaceMonitors = [
description: "memory 相关模块硬编码 legacy memory 或 AGENTS 路径",
patterns: [
"~/.lime/AGENTS.md",
"join(\".lime\").join(\"AGENTS.md\")",
"join(\".lime\").join(\"memory\")",
'join(".lime").join("AGENTS.md")',
'join(".lime").join("memory")',
".lime/memory",
],
includePathPrefixes: ["src-tauri/src"],
@@ -892,7 +994,10 @@ function evaluateTextCountMonitor(monitor, runtimeSources, testSources) {
const counts = monitor.occurrences
.map((rule) => ({
...rule,
count: countOccurrences(file.rawSourceCode ?? file.sourceCode, rule.pattern),
count: countOccurrences(
file.rawSourceCode ?? file.sourceCode,
rule.pattern,
),
}))
.filter((rule) => rule.count > 0);
@@ -1032,8 +1137,10 @@ function printTextCountReport(result) {
}
const { runtimeSources, testSources } = collectSources();
const { runtimeSources: frontendRuntimeTextSources, testSources: frontendTestTextSources } =
collectTextSources(sourceRoots, sourceExtensions);
const {
runtimeSources: frontendRuntimeTextSources,
testSources: frontendTestTextSources,
} = collectTextSources(sourceRoots, sourceExtensions);
const { runtimeSources: rustRuntimeSources, testSources: rustTestSources } =
collectTextSources(rustSourceRoots, rustSourceExtensions);
const importResults = importSurfaceMonitors.map((monitor) =>
@@ -1043,7 +1150,11 @@ const commandResults = commandSurfaceMonitors.map((monitor) =>
evaluateCommandMonitor(monitor, runtimeSources, testSources),
);
const frontendTextResults = frontendTextSurfaceMonitors.map((monitor) =>
evaluateTextMonitor(monitor, frontendRuntimeTextSources, frontendTestTextSources),
evaluateTextMonitor(
monitor,
frontendRuntimeTextSources,
frontendTestTextSources,
),
);
const rustTextResults = rustTextSurfaceMonitors.map((monitor) =>
evaluateTextMonitor(monitor, rustRuntimeSources, rustTestSources),
+107
View File
@@ -0,0 +1,107 @@
import { spawn } from "node:child_process";
import path from "node:path";
import { fileURLToPath } from "node:url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const rootDir = path.resolve(__dirname, "..");
const args = process.argv.slice(2);
const mode = args.shift() || "trace";
let openDevtools = false;
let headless = false;
const extraArgs = [];
for (const arg of args) {
if (arg === "--open-devtools") {
openDevtools = true;
continue;
}
if (arg === "--headless") {
headless = true;
continue;
}
extraArgs.push(arg);
}
const supportedModes = new Map([
["trace", ["trace"]],
["console", ["console"]],
["trace-console", ["trace", "console"]],
]);
const profileModes = supportedModes.get(mode);
if (!profileModes) {
console.error(`[profile] 不支持的模式: ${mode}`);
process.exit(1);
}
const env = {
...process.env,
CARGO_TARGET_DIR: process.env.CARGO_TARGET_DIR || "target",
LIME_PROFILE: profileModes.join(","),
RUST_BACKTRACE: process.env.RUST_BACKTRACE || "1",
RUST_LOG: process.env.RUST_LOG || "info",
};
if (openDevtools) {
env.LIME_OPEN_WEBVIEW_DEVTOOLS = "1";
}
if (profileModes.includes("console")) {
const existingRustFlags = env.RUSTFLAGS?.trim() || "";
env.RUSTFLAGS = existingRustFlags.includes("--cfg tokio_unstable")
? existingRustFlags
: [existingRustFlags, "--cfg tokio_unstable"].filter(Boolean).join(" ");
env.TOKIO_CONSOLE_BIND = process.env.TOKIO_CONSOLE_BIND || "127.0.0.1:6669";
}
const tauriArgs = ["exec", "--", "tauri", "dev"];
if (headless) {
tauriArgs.push("--config", "src-tauri/tauri.conf.headless.json");
}
const tauriFeatures = [];
if (profileModes.includes("trace")) {
tauriFeatures.push("dev-profiling");
}
if (profileModes.includes("console")) {
tauriFeatures.push("tokio-console");
}
if (tauriFeatures.length > 0) {
tauriArgs.push("--features", tauriFeatures.join(","));
}
tauriArgs.push(...extraArgs);
console.log("[profile] 仅用于开发诊断,release/生产构建默认不会启用这些能力。");
if (profileModes.includes("trace")) {
console.log(
"[profile] 已启用 trace 导出,关闭应用后可把生成的 JSON 导入 https://ui.perfetto.dev/",
);
}
if (profileModes.includes("console")) {
console.log(
`[profile] 已启用 Tokio Console 遥测,另开终端运行: tokio-console ${env.TOKIO_CONSOLE_BIND}`,
);
}
const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
const child = spawn(npmCommand, tauriArgs, {
cwd: rootDir,
env,
stdio: "inherit",
});
child.on("exit", (code, signal) => {
if (signal) {
process.kill(process.pid, signal);
return;
}
process.exit(code ?? 0);
});