Ð޸´ȫÏîĿÉó²éÎÊÌⲢ·¢²¼ v1.1.9

¸ÄÓñ¾»ú¶ÀÁ¢ CA£¬»ָ´ CLI TLS УÑ飬±£»¤ƾ¾ÝÓëÅäÖò¢·¢±£´档
Ð޸´ Cursor Êý¾ݡ¢»Ự·¾¶¡¢´úÀíÉúÃüÖÜÆڡ¢ģÐÍÇëÇóÓë Skills Îļþ²Ù×÷¡£
Ϊ¶ÀÁ¢ Tab ·þÎñÔö¼ӿͻ§¶˼øȨ£¬Éý¼¶ÒÀÀµÓ빤¾ßÁ´£¬ÍêÉƷ¢²¼ԪÐÅϢͬ²½¡£

Ñé֤£ºÁ½¸ö Go ģ¿éµIJâÊÔ/vet¡¢¹ؼüģ¿é race¡¢ǰ¶˲âÊÔ/Éú²ú¹¹½¨/Éó¼ơ¢
Go ¿ɴï©¶´ɨÃèÓë macOS/Linux ¿ÉÐÐģ¿齻²æ±àÒë¾ùͨ¹ý¡£
Windows ¸ôÀëÆô¶¯ͨ¹ý£»δÍê³ɵĵã»÷ðÑ̺ÍԭÉúƽ̨Ñé֤¼ûÉó²鱨¸档
This commit is contained in:
FuDie0915
2026-11-10 09:59:34 +08:00
parent db73cf5a39
commit 188225fae6
98 changed files with 3324 additions and 930 deletions

View File

@@ -1,3 +1,4 @@
import { apiLogEntry } from "@/utils/configSafety.js";
import {
GetState,
LoadUserConfig,
@@ -32,19 +33,8 @@ import { Call } from "@wailsio/runtime";
const API_LOG_PREFIX = "[clientApi]";
const PROXY_SERVICE_NAME = "cursor/internal/bridge.ProxyService";
function logSuccess(name, payload, result) {
console.log(`${API_LOG_PREFIX} ${name} response`, {
payload,
result,
});
}
function logError(name, payload, error) {
console.error(`${API_LOG_PREFIX} ${name} error`, {
payload,
error,
});
}
function logSuccess(name) { console.info(API_LOG_PREFIX, apiLogEntry(name)); }
function logError(name) { console.error(API_LOG_PREFIX, apiLogEntry(name, true)); }
function withApiLogging(name, payload, runner) {
return Promise.resolve()

View File

@@ -1,3 +1,4 @@
import { safePreferences, editorConflict } from "@/utils/configSafety.js";
import { computed, reactive, watchSyncEffect } from "vue";
import { Events } from "@wailsio/runtime";
import dayjs from "dayjs";
@@ -580,7 +581,9 @@ function loadCachedState() {
if (!parsed || typeof parsed !== "object") {
return {};
}
return parsed;
const safe = safePreferences(parsed);
window.localStorage.setItem(APP_STATE_STORAGE_KEY, JSON.stringify(safe));
return safe;
} catch (_error) {
return {};
}
@@ -591,6 +594,8 @@ function normalizeConfig(source) {
const routing = raw.routing && typeof raw.routing === "object" ? raw.routing : {};
const homeMetrics = raw.homeMetrics && typeof raw.homeMetrics === "object" ? raw.homeMetrics : {};
return {
revision: asString(raw.revision || raw.configRevision),
cliHttpsListenAddr: asString(raw.cliHttpsListenAddr || raw.configCLIHTTPSListenAddr),
log: asBoolean(raw.log),
providerStreamIdleTimeout: asPositiveInteger(raw.providerStreamIdleTimeout),
backendListenAddr: asString(raw.configBackendListenAddr) || asString(raw.backendListenAddr),
@@ -649,6 +654,8 @@ function applyHomeMetrics(raw) {
function buildConfigPayload(source = appState) {
const normalized = normalizeConfig(source);
return {
revision: normalized.revision,
cliHttpsListenAddr: normalized.cliHttpsListenAddr,
log: normalized.log,
providerStreamIdleTimeout: normalized.providerStreamIdleTimeout,
backendListenAddr: normalized.backendListenAddr,
@@ -664,19 +671,12 @@ function buildConfigPayload(source = appState) {
// sanitizePayloadForCache 剥离敏感字段(apiKey/tabServerToken),避免 localStorage 明文落盘。
// 缓存仅用于启动瞬间兜底展示,真实配置始终以后端返回为准。
function sanitizePayloadForCache(payload) {
return {
...payload,
modelAdapters: (payload.modelAdapters || []).map(({ apiKey, ...adapter }) => ({
...adapter,
apiKey: "",
})),
tabServerToken: "",
};
}
function sanitizePayloadForCache(payload) { return safePreferences(payload); }
function applyConfigToState(config, { modelAdaptersOnly = false } = {}) {
const normalized = normalizeConfig(config);
appState.configRevision = normalized.revision;
appState.configCLIHTTPSListenAddr = normalized.cliHttpsListenAddr;
if (modelAdaptersOnly) {
appState.modelAdapters = normalized.modelAdapters;
return normalized;
@@ -802,6 +802,8 @@ const cachedConfig = normalizeConfig(cachedState);
export const appState = reactive({
appVersion: "",
configRevision: "",
configCLIHTTPSListenAddr: "",
modelAdapters: cachedConfig.modelAdapters,
modelAdapterTestResults: {},
configBackendListenAddr: cachedConfig.backendListenAddr,
@@ -845,29 +847,7 @@ watchSyncEffect(() => {
try {
window.localStorage.setItem(
APP_STATE_STORAGE_KEY,
JSON.stringify({
...sanitizePayloadForCache(buildConfigPayload()),
serviceRunning: appState.serviceRunning,
backendRunning: appState.backendRunning,
proxyRunning: appState.proxyRunning,
serviceLastError: appState.serviceLastError,
serviceListenAddr: appState.serviceListenAddr,
configBackendListenAddr: appState.configBackendListenAddr,
configProxyListenAddr: appState.configProxyListenAddr,
backendListenAddr: appState.backendListenAddr,
proxyListenAddr: appState.proxyListenAddr,
tabServerBaseURL: appState.tabServerBaseURL,
tabServerToken: "",
cursorSettingsApplied: appState.cursorSettingsApplied,
netProxySource: appState.netProxySource,
netProxyActive: appState.netProxyActive,
netProxyUsingSystem: appState.netProxyUsingSystem,
netProxyUsingEnv: appState.netProxyUsingEnv,
netProxyHttp: appState.netProxyHttp,
netProxyHttps: appState.netProxyHttps,
netProxyPacIgnored: appState.netProxyPacIgnored,
netProxyDescription: appState.netProxyDescription,
}),
JSON.stringify(sanitizePayloadForCache(buildConfigPayload())),
);
} catch (_error) {
// ignore local persistence failures
@@ -978,7 +958,9 @@ export async function runModelAdapterTest(adapter) {
}
export async function persistUserConfig() {
const expected = appState.configRevision;
const currentConfig = await loadPersistedUserConfig();
if (currentConfig.revision !== expected) return {ok:false,error:"CONFIG_CONFLICT: 配置已更改,请重新加载;草稿已保留"};
return persistConfigPayload({
...currentConfig,
modelAdapters: normalizeModelAdapters(appState.modelAdapters),
@@ -1077,8 +1059,9 @@ export async function reloadUserConfig(options = {}) {
return config;
}
export async function saveModelAdapterAt(index, adapter) {
export async function saveModelAdapterAt(index, adapter, expectedRevision, originalID) {
const currentConfig = await loadPersistedUserConfig();
if (editorConflict(currentConfig, expectedRevision, index, originalID)) return { ok:false, error:"CONFIG_CONFLICT: 配置已在其他窗口更改,请刷新版本后再保存;草稿已保留" };
const nextAdapters = normalizeModelAdapters(currentConfig.modelAdapters);
const nextAdapter = normalizeModelAdapter(adapter);
const targetIndex = index >= 0 && index < nextAdapters.length ? index : nextAdapters.length;
@@ -1311,7 +1294,7 @@ export async function openModelConfigWindow() {
}
export async function openModelEditorWindow(index, adapter) {
const adapterJSON = JSON.stringify(normalizeModelAdapter(adapter));
const adapterJSON = JSON.stringify({ ...normalizeModelAdapter(adapter), __revision: appState.configRevision, __originalID: adapter?.id || "" });
await openModelEditor(index, adapterJSON);
}

View File

@@ -0,0 +1,21 @@
// Cache an explicit preference allowlist; model and authentication data stay in memory.
export function safePreferences(payload = {}) {
const metrics = payload.homeMetrics || {};
return {
homeMetrics: {
includeCacheWriteInHitRate: metrics.includeCacheWriteInHitRate === true,
persistentMode: metrics.persistentMode !== false,
refreshIntervalSeconds: Number(metrics.refreshIntervalSeconds) || 60,
},
};
}
export function apiLogEntry(operation, failed = false) {
return { operation, status: failed ? "failed" : "completed" };
}
export function editorConflict(current, expectedRevision, index, originalID) {
if (expectedRevision !== undefined && current.revision !== expectedRevision) return true;
if (index >= 0 && (!current.modelAdapters[index] || (originalID && current.modelAdapters[index].id !== originalID))) return true;
return false;
}

View File

@@ -20,6 +20,7 @@ import {
OPENAI_ENDPOINT_RESPONSES,
OPENAI_EXTRA_PARAMS_DEFAULT_JSON,
runModelAdapterTest,
reloadUserConfig,
saveModelAdapterAt,
toUserError,
validateModelAdapters,
@@ -53,6 +54,8 @@ const openAIEndpointOptions = [
];
const editorIndex = ref(-1);
const editorRevision = ref("");
const originalID = ref("");
const draft = reactive(createEmptyModelAdapter());
const errorMessage = ref("");
const loading = ref(true);
@@ -142,6 +145,8 @@ async function loadContext() {
const ctx = await getModelEditorContext();
editorIndex.value = typeof ctx.index === "number" ? ctx.index : -1;
const parsed = JSON.parse(ctx.adapterJSON || "{}");
editorRevision.value = parsed.__revision || appState.configRevision;
originalID.value = parsed.__originalID || parsed.id || "";
Object.assign(draft, normalizeModelAdapter(parsed));
if (!draft.type) {
draft.type = "openai";
@@ -163,7 +168,7 @@ async function persistDraft() {
return { ok: false, error: singleCheck, adapter: null };
}
const result = await saveModelAdapterAt(editorIndex.value, adapter);
const result = await saveModelAdapterAt(editorIndex.value, adapter, editorRevision.value, originalID.value);
if (!result.ok) {
errorMessage.value = result.error;
return { ok: false, error: result.error, adapter: null };
@@ -175,6 +180,8 @@ async function persistDraft() {
if (result.adapter) {
Object.assign(draft, normalizeModelAdapter(result.adapter));
}
editorRevision.value = appState.configRevision;
originalID.value = result.adapter?.id || originalID.value;
errorMessage.value = "";
return {
ok: true,
@@ -183,6 +190,19 @@ async function persistDraft() {
};
}
async function refreshEditorRevision() {
try {
const latest = await reloadUserConfig();
if (editorIndex.value >= 0) {
const index = latest.modelAdapters.findIndex(item => item.id === originalID.value);
if (index < 0) { errorMessage.value = "原模型已被修改或删除,草稿仍保留。请先核对其他窗口中的修改。"; return; }
editorIndex.value = index;
}
editorRevision.value = latest.revision;
errorMessage.value = "版本已刷新,草稿保持不变。请核对后保存。";
} catch (error) { errorMessage.value = toUserError(error); }
}
async function handleSave() {
const result = await persistDraft();
if (!result.ok) {
@@ -542,6 +562,7 @@ onMounted(async () => {
>
{{ errorMessage }}
</div>
<Button v-if="errorMessage.includes('CONFIG_CONFLICT')" @click="refreshEditorRevision">刷新版本,保留草稿</Button>
</div>
</div>
</div>