feat: 路由不再开机自动接管 + 保存分组即写直连配置 + 官方登录完整备份 (v1.5.16)
- 启动不再自动 router.start():只想用其它功能的用户不再被接管 - 新增 applyDirectToCodex():保存 responses 中转分组即把直连配置写进 config.toml+auth.json(非本地路由),重启 Codex 即用;本地路由降级为 '启用本地路由(高级)'显式选项 - chat 协议转换分组(DeepSeek/GLM/Kimi)保存时只提示需启用路由,不自动起 - 新增 snapshotOfficialLogin():首次接管前完整快照官方 auth.json(含OAuth tokens)+config.toml(router.json加密存+落盘副本);restoreDirect 优先用它 100%还原官方登录态,并清理 td-direct 残段 - setActive 按 isRunning 分流:路由在跑走本地路由秒切,否则写直连需重启 - 隔离测试:登录态21/21 + Mac custom回归9/9 全绿 - 版本 1.5.15 → 1.5.16 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<!-- codex-memory:start -->
|
||||
## 项目记忆(Codex 插件市场自动维护 · 回答前据此 · 勿手动编辑本段)
|
||||
**当前状态**:用户想重开 Claude Code 会话并延续之前记录。已说明可在原项目目录用 `claude --continue`/`-c` 接最近会话,用 `claude --resume`/`-r` 或 `/resume` 手动选择历史会话;会话按项目目录保存。
|
||||
**当前状态**:当前仅确认助手位于项目目录 `/Users/mac/Documents/备份数据/ProjectDown/codex-market-app`,尚未开始具体代码任务。可继续处理代码、查历史记忆或恢复 Claude/Codex 会话。
|
||||
**已定决策**:
|
||||
- 指定历史会话用 claude --resume
|
||||
- 最近会话用 claude --continue
|
||||
@@ -15,10 +15,10 @@
|
||||
- 同时发布热更和完整包
|
||||
- 非显式生图请求剥离gen-image声明
|
||||
**下一步**:
|
||||
- 进入原项目目录
|
||||
- 运行恢复命令继续会话
|
||||
**关键文件**:~/.claude/projects/.../*.jsonl
|
||||
- 等待用户指定具体任务
|
||||
**关键文件**:/Users/mac/Documents/备份数据/ProjectDown/codex-market-app
|
||||
**历史话题(需其中细节时用下面命令检索)**:
|
||||
- 2026-07-16 项目会话恢复
|
||||
- 2026-07-16 Claude Code 会话恢复
|
||||
- 2026-07-16 今日新闻搜索交接
|
||||
- 2026-07-16 发布1.5.14完整包
|
||||
@@ -30,6 +30,5 @@
|
||||
- 2026-07-15 生成魔兽世界宣传海报
|
||||
- 2026-07-15 Codex skill软链改造
|
||||
- 2026-07-14 列出本地 skills
|
||||
- 2026-07-14 Claude CLI 中转配置排查
|
||||
> 回答涉及本段未列出的历史细节前,先运行 `rg -n "关键词" /Users/mac/.codex/memory/31c67c70b625.md` 查证再答。
|
||||
<!-- codex-memory:end -->
|
||||
|
||||
+10
-21
@@ -3,7 +3,16 @@
|
||||
|
||||
> 核心记忆已写进本项目 `AGENTS.md`(Codex 每次会话自动读)。本文件是更详细的近期快照,供需要时参考;关键信息不依赖本文件。
|
||||
|
||||
## 最近会话 · 2026-07-16 10:37 · Claude Code 会话恢复
|
||||
## 最近会话 · 2026-07-16 14:32 · 项目会话恢复
|
||||
|
||||
当前仅确认助手位于项目目录 `/Users/mac/Documents/备份数据/ProjectDown/codex-market-app`,尚未开始具体代码任务。可继续处理代码、查历史记忆或恢复 Claude/Codex 会话。
|
||||
|
||||
**下一步**:
|
||||
- 等待用户指定具体任务
|
||||
|
||||
**涉及文件**:/Users/mac/Documents/备份数据/ProjectDown/codex-market-app
|
||||
|
||||
## 更早会话 · 2026-07-16 10:37 · Claude Code 会话恢复
|
||||
|
||||
用户想重开 Claude Code 会话并延续之前记录。已说明可在原项目目录用 `claude --continue`/`-c` 接最近会话,用 `claude --resume`/`-r` 或 `/resume` 手动选择历史会话;会话按项目目录保存。
|
||||
|
||||
@@ -49,23 +58,3 @@
|
||||
- 若仍403检查所选生图分组权限
|
||||
|
||||
**涉及文件**:electron/lib/router.js、package.json、package-lock.json、scripts/publish-hot.js、electron-builder.yml
|
||||
|
||||
## 更早会话 · 2026-07-16 01:57 · 生图 skill 与聊天分组冲突
|
||||
|
||||
已定位:安装生图 skill 后,普通聊天请求会携带生图相关上下文,当前聊天分组未开图片权限而被 /responses 返回 403。推荐方案是普通聊天不暴露生图 skill,显式 $gen-image 时由本地拦截并直接调用 gen-image 脚本与生图分组。
|
||||
|
||||
**已确定的决策**:
|
||||
- Grok 视频不是流式,是异步轮询
|
||||
- Grok 失败曾因成品视频直连超时
|
||||
- 已修 Grok 轮询与代理下载逻辑
|
||||
- 生图 403 来自聊天分组权限
|
||||
- 推荐 $gen-image 本地直执行
|
||||
|
||||
**下一步**:
|
||||
- 实现 $gen-image 本地命令拦截
|
||||
- 普通聊天禁用生图 skill 注入
|
||||
- 调用 gen_image.py 走生图 .env
|
||||
- 返回生成图片路径或预览
|
||||
- 必要时加错误提示与日志
|
||||
|
||||
**涉及文件**:/Users/mac/Library/Containers/com.tencent.qq/Data/Downloads/GROK_VIDEO_API_CN.md、/Users/mac/Documents/备份数据/ProjectDown/electron/lib/canvas.js、/Users/mac/Documents/备份数据/ProjectDown/hot/1.5.13/electron/lib/canvas.js、/var/folders/xk/qrv10_rx12jg91fn322xlwr00000gn/T/codex-clipboard-f13f4be1-4b94-4878-927c-2a80e0e67b47.png
|
||||
|
||||
+169
-12
@@ -12,7 +12,8 @@ const translate = require("./translate");
|
||||
|
||||
const CODEX_HOME = path.join(os.homedir(), ".codex");
|
||||
const CLAUDE_HOME = path.join(os.homedir(), ".claude");
|
||||
const PROVIDER_ID = "local-router"; // config.toml 里的 provider 名
|
||||
const PROVIDER_ID = "local-router"; // config.toml 里的 provider 名(走本地路由进程时用)
|
||||
const DIRECT_PROVIDER_ID = "td-direct"; // config.toml 里的 provider 名(直连中转、不经路由时用)
|
||||
|
||||
function cfgPath() {
|
||||
return path.join(app.getPath("userData"), "router.json");
|
||||
@@ -46,7 +47,14 @@ function load() {
|
||||
try {
|
||||
const raw = JSON.parse(fs.readFileSync(cfgPath(), "utf-8"));
|
||||
raw.routerKey = raw.routerKey ? dec(raw.routerKey) : defaultConfig().routerKey;
|
||||
raw.groups = (raw.groups || []).map((g) => ({ ...g, key: dec(g.key) }));
|
||||
raw.groups = (raw.groups || []).map((g) => {
|
||||
const out = { ...g, key: dec(g.key) };
|
||||
// officialBackup.authJson 含 OAuth tokens,落盘时加密,读时解密
|
||||
if (out.officialBackup && out.officialBackup.authJson) {
|
||||
out.officialBackup = { ...out.officialBackup, authJson: dec(out.officialBackup.authJson) };
|
||||
}
|
||||
return out;
|
||||
});
|
||||
config = { ...defaultConfig(), ...raw };
|
||||
} catch {
|
||||
config = defaultConfig();
|
||||
@@ -58,7 +66,13 @@ function save() {
|
||||
const out = {
|
||||
...config,
|
||||
routerKey: enc(config.routerKey),
|
||||
groups: config.groups.map((g) => ({ ...g, key: enc(g.key) })),
|
||||
groups: config.groups.map((g) => {
|
||||
const o = { ...g, key: enc(g.key) };
|
||||
if (o.officialBackup && o.officialBackup.authJson) {
|
||||
o.officialBackup = { ...o.officialBackup, authJson: enc(o.officialBackup.authJson) };
|
||||
}
|
||||
return o;
|
||||
}),
|
||||
};
|
||||
const p = cfgPath();
|
||||
fs.mkdirSync(path.dirname(p), { recursive: true });
|
||||
@@ -139,6 +153,36 @@ function backupConfig(text) {
|
||||
fs.writeFileSync(path.join(dir, "config.toml." + Date.now() + ".bak"), text || "");
|
||||
} catch {}
|
||||
}
|
||||
// 首次接管 Codex 配置前,把官方登录信息(含 OAuth tokens 的 auth.json + 当前 config.toml)
|
||||
// 完整快照进默认分组 def.officialBackup,并落一份磁盘副本。幂等:只在第一次真正执行。
|
||||
// 保证任何时候「切回默认」都能 100% 还原回官方登录态。
|
||||
function snapshotOfficialLogin(def) {
|
||||
if (!def) return;
|
||||
if (def.officialBackup && def.officialBackup.done) return; // 幂等守卫
|
||||
let configToml = "";
|
||||
let authJson = "";
|
||||
try { configToml = fs.readFileSync(path.join(CODEX_HOME, "config.toml"), "utf-8"); } catch {}
|
||||
try { authJson = fs.readFileSync(path.join(CODEX_HOME, "auth.json"), "utf-8"); } catch {}
|
||||
// 磁盘副本(灾备):仅当不存在时写,避免二次接管覆盖原始官方备份
|
||||
try {
|
||||
const dir = path.join(CODEX_HOME, "backups_state", "router", "official-login");
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
const cfgOrig = path.join(dir, "config.toml.orig");
|
||||
const authOrig = path.join(dir, "auth.json.orig");
|
||||
if (configToml && !fs.existsSync(cfgOrig)) fs.writeFileSync(cfgOrig, configToml, "utf-8");
|
||||
if (authJson && !fs.existsSync(authOrig)) {
|
||||
fs.writeFileSync(authOrig, authJson, "utf-8");
|
||||
try { fs.chmodSync(authOrig, 0o600); } catch {}
|
||||
}
|
||||
} catch {}
|
||||
def.officialBackup = {
|
||||
done: true,
|
||||
ts: Date.now(),
|
||||
configToml,
|
||||
authJson,
|
||||
hadTokens: isLoggedIn(),
|
||||
};
|
||||
}
|
||||
// 首次使用时,把当前(原始)Codex 配置快照为不可删除的「默认」分组。
|
||||
// 铁律:默认分组必须代表「我的账号 · 直连」的登录态,绝不能是接入中转的状态,
|
||||
// 否则「切回默认」会把 Codex 重新指向中转,用户就再也复原不回自己账号了。
|
||||
@@ -147,6 +191,7 @@ function ensureDefault() {
|
||||
const loggedIn = isLoggedIn();
|
||||
const existing = config.groups.find((g) => g.isDefault);
|
||||
if (existing) {
|
||||
snapshotOfficialLogin(existing); // 幂等:确保已装用户也补上官方登录完整备份
|
||||
// 自愈:已用自己账号登录,但历史版本把默认分组错误地快照成了中转 provider →
|
||||
// 纠正为「纯登录态」,保证「切回默认」永远能干净复原自己账号。
|
||||
if (loggedIn && (existing.modelProvider || existing.providerBlock || existing.authKey)) {
|
||||
@@ -169,6 +214,7 @@ function ensureDefault() {
|
||||
authKey: "",
|
||||
origModel: currentRootModel(), // 记住原始根级 model(新版桌面登录态通常无 → ""),复原时精确还原
|
||||
});
|
||||
snapshotOfficialLogin(config.groups[0]);
|
||||
if (!config.activeId) config.activeId = "default";
|
||||
save();
|
||||
return;
|
||||
@@ -186,6 +232,7 @@ function ensureDefault() {
|
||||
authKey: readAuthKey(),
|
||||
origModel: currentRootModel(),
|
||||
});
|
||||
snapshotOfficialLogin(config.groups[0]);
|
||||
if (!config.activeId) config.activeId = "default";
|
||||
save();
|
||||
}
|
||||
@@ -246,8 +293,9 @@ function restoreDirect() {
|
||||
const cfgFile = path.join(CODEX_HOME, "config.toml");
|
||||
let text = readConfigToml();
|
||||
backupConfig(text);
|
||||
// 先移除本地路由段(无论哪种复原都要清掉)
|
||||
// 先移除本地路由段 + 直连中转段(无论哪种复原都要清掉,避免残留污染)
|
||||
text = removeTomlSection(text, `model_providers.${PROVIDER_ID}`);
|
||||
text = removeTomlSection(text, `model_providers.${DIRECT_PROVIDER_ID}`);
|
||||
if (def.modelProvider) {
|
||||
// 有原始直连 provider(纯 API key 用户,如 Mac 的 custom):设根键 + 追加原始段,避免重复键。
|
||||
// 此分支完全保持旧行为(不动根级 model 行),确保已在用的 Mac 环境零回归。
|
||||
@@ -267,6 +315,14 @@ function restoreDirect() {
|
||||
text = text.replace(/\n{3,}/g, "\n\n");
|
||||
fs.writeFileSync(cfgFile, text, "utf-8");
|
||||
const af = path.join(CODEX_HOME, "auth.json");
|
||||
// 优先用官方登录完整备份整体还原(含 OAuth tokens)→ 100% 复原官方登录态
|
||||
if (def.officialBackup && def.officialBackup.done && def.officialBackup.hadTokens && def.officialBackup.authJson) {
|
||||
try {
|
||||
fs.writeFileSync(af, def.officialBackup.authJson, "utf-8");
|
||||
fs.chmodSync(af, 0o600);
|
||||
return;
|
||||
} catch {}
|
||||
}
|
||||
try {
|
||||
const a = JSON.parse(fs.readFileSync(af, "utf-8"));
|
||||
// 登录态:清空 OPENAI_API_KEY、保留 tokens → Codex 用 OAuth 直连;纯 key 用户则还原原始 key
|
||||
@@ -727,7 +783,32 @@ function upsertGroup(g) {
|
||||
}
|
||||
save();
|
||||
try { syncGenImage(); } catch {}
|
||||
return status();
|
||||
// 保存中转分组后:把它的直连中转配置写进 Codex config.toml(不经路由、不起进程),
|
||||
// 并设为激活分组。用户重启 Codex 即用当前分组。只有显式「启用本地路由」才走路由进程。
|
||||
const st = status();
|
||||
if (cur && !cur.isDefault) {
|
||||
// 路由进程正在跑(用户处于「启用路由」高级模式)→ 保持走本地路由,不改成直连
|
||||
if (isRunning()) {
|
||||
st._directSkipped = true;
|
||||
st._directReason = "本地路由运行中,保存的分组走路由生效(切分组无需重启)";
|
||||
} else if (!cur.baseUrl || !cur.key) {
|
||||
st._directSkipped = true;
|
||||
st._directReason = "该分组缺少 API 地址或密钥,未写入 Codex 配置";
|
||||
} else if (cur.mode === "chat") {
|
||||
st._directSkipped = true;
|
||||
st._directNeedRouter = true;
|
||||
st._directReason = "该分组为 Chat 协议转换模式,需启用本地路由才能接入 Codex";
|
||||
} else {
|
||||
try {
|
||||
const r = applyDirectToCodex(cur.id);
|
||||
if (r.ok) { st._directApplied = true; st.activeId = config.activeId; }
|
||||
else { st._directSkipped = true; st._directReason = r.error || "写入失败"; }
|
||||
} catch (e) {
|
||||
st._directError = String((e && e.message) || e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return st;
|
||||
}
|
||||
function deleteGroup(id) {
|
||||
load();
|
||||
@@ -744,24 +825,34 @@ function setActive(id) {
|
||||
const target = config.groups.find((g) => g.id === id);
|
||||
if (!target) return status();
|
||||
const before = currentModelProvider();
|
||||
const wasTakenOver = before === PROVIDER_ID || before === DIRECT_PROVIDER_ID;
|
||||
let needsRestart = false;
|
||||
let mode = "proxy";
|
||||
if (target.isDefault) {
|
||||
// 切回默认 = 还原原始配置,Codex 直连自己账号(需重启 Codex)
|
||||
restoreDirect();
|
||||
mode = "direct";
|
||||
needsRestart = before === PROVIDER_ID; // 之前在走路由 → 需重启
|
||||
} else {
|
||||
// 切到中转分组:确保 config.toml 指向本地路由
|
||||
needsRestart = wasTakenOver; // 之前在走路由或直连中转 → 需重启
|
||||
} else if (isRunning()) {
|
||||
// 路由进程在跑(高级模式):确保 config.toml 指向本地路由,秒切无需重启
|
||||
mode = "proxy";
|
||||
if (before !== PROVIDER_ID) {
|
||||
applyToCodex();
|
||||
needsRestart = true; // 从直连切到路由 → 需重启一次
|
||||
needsRestart = true; // 从直连/默认切到路由 → 需重启一次
|
||||
}
|
||||
// 若分组指定了模型:写模型目录窗口 + 设 config 模型,让 Codex 按真实窗口压缩
|
||||
if (target.model) {
|
||||
applyModelCatalog(target);
|
||||
setConfigModel(target.model);
|
||||
}
|
||||
} else {
|
||||
// 默认模式:直连中转,改 config.toml 指向该分组地址(必然需重启 Codex)
|
||||
mode = "direct";
|
||||
const r = applyDirectToCodex(id);
|
||||
// applyDirectToCodex 内部已 setActive + save;chat/缺 key 则不写配置
|
||||
needsRestart = !!r.ok && !r.restored;
|
||||
config.activeId = id;
|
||||
save();
|
||||
return { ...status(), _needsRestart: needsRestart, _mode: mode, _directSkipped: r.skipped, _directReason: r.error };
|
||||
}
|
||||
config.activeId = id;
|
||||
save();
|
||||
@@ -837,8 +928,73 @@ function applyToCodex() {
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
// —— 自动接入 Claude Code —— 写 ~/.claude/settings.json 的 env:ANTHROPIC_BASE_URL + ANTHROPIC_AUTH_TOKEN
|
||||
// Claude Code 会把 settings.json 里 env 的键当环境变量注入每个会话,从而走本地路由。
|
||||
// —— 直连中转写配置(不经本地路由,不起进程)——
|
||||
// 把指定分组的中转 base_url/key 直接写进 config.toml + auth.json,Codex 直连中转站。
|
||||
// 用户保存分组即调用;重启 Codex 生效。chat 模式分组需协议转换、无法直连 → 拒绝。
|
||||
function applyDirectToCodex(groupId) {
|
||||
load();
|
||||
const g = config.groups.find((x) => x.id === (groupId || config.activeId));
|
||||
if (!g) return { ok: false, error: "分组不存在" };
|
||||
if (g.isDefault) {
|
||||
restoreDirect(); // 默认分组语义 = 回到官方登录直连
|
||||
config.activeId = g.id;
|
||||
save();
|
||||
return { ok: true, restored: true };
|
||||
}
|
||||
if (!g.baseUrl || !g.key) {
|
||||
return { ok: false, skipped: true, error: "该分组缺少 API 地址或密钥,未写入 Codex 配置" };
|
||||
}
|
||||
if (g.mode === "chat") {
|
||||
return { ok: false, skipped: true, needRouter: true, error: "该分组为 Chat 协议转换模式,直连 Codex 不支持,需启用本地路由" };
|
||||
}
|
||||
|
||||
// 接管前先确保官方登录已完整备份(幂等)
|
||||
const def = config.groups.find((x) => x.isDefault);
|
||||
if (def) snapshotOfficialLogin(def);
|
||||
|
||||
const cfgFile = path.join(CODEX_HOME, "config.toml");
|
||||
let text = "";
|
||||
try { text = fs.readFileSync(cfgFile, "utf-8"); } catch {}
|
||||
backupConfig(text);
|
||||
|
||||
// 清理其他接管痕迹:本地路由段、上次直连段、默认分组原始 provider 段
|
||||
text = removeTomlSection(text, `model_providers.${PROVIDER_ID}`);
|
||||
text = removeTomlSection(text, `model_providers.${DIRECT_PROVIDER_ID}`);
|
||||
if (def && def.modelProvider && def.modelProvider !== DIRECT_PROVIDER_ID) {
|
||||
text = removeTomlSection(text, `model_providers.${def.modelProvider}`);
|
||||
}
|
||||
// 替换/插入根键 → 指向直连 provider
|
||||
if (/^\s*model_provider\s*=/m.test(text)) {
|
||||
text = text.replace(/^\s*model_provider\s*=.*$/m, `model_provider = "${DIRECT_PROVIDER_ID}"`);
|
||||
} else {
|
||||
text = `model_provider = "${DIRECT_PROVIDER_ID}"\n` + text;
|
||||
}
|
||||
// 追加 [model_providers.td-direct] 段:base_url = 分组真实中转地址(非 127.0.0.1)
|
||||
const base = g.baseUrl.replace(/\/+$/, "");
|
||||
const block = `[model_providers.${DIRECT_PROVIDER_ID}]\nname = "${(g.name || "中转直连").replace(/"/g, "")}"\nbase_url = "${base}"\nwire_api = "responses"\nrequires_openai_auth = true\n`;
|
||||
text = text.trimEnd() + "\n\n" + block;
|
||||
text = text.replace(/\n{3,}/g, "\n\n");
|
||||
fs.writeFileSync(cfgFile, text, "utf-8");
|
||||
|
||||
// auth.json 写分组真实 key(Codex 直连中转站时用它作 Bearer)
|
||||
const authFile = path.join(CODEX_HOME, "auth.json");
|
||||
let auth = {};
|
||||
try { auth = JSON.parse(fs.readFileSync(authFile, "utf-8")); } catch {}
|
||||
auth.OPENAI_API_KEY = g.key;
|
||||
try {
|
||||
fs.writeFileSync(authFile, JSON.stringify(auth, null, 2), "utf-8");
|
||||
fs.chmodSync(authFile, 0o600);
|
||||
} catch {}
|
||||
|
||||
// 分组指定模型 → 写模型目录窗口 + 设根级 model
|
||||
if (g.model) {
|
||||
applyModelCatalog(g);
|
||||
setConfigModel(g.model);
|
||||
}
|
||||
config.activeId = g.id;
|
||||
save();
|
||||
return { ok: true, direct: true, group: g.name };
|
||||
}
|
||||
function claudeSettingsPath() {
|
||||
return path.join(CLAUDE_HOME, "settings.json");
|
||||
}
|
||||
@@ -932,6 +1088,7 @@ module.exports = {
|
||||
setActive,
|
||||
setPort,
|
||||
applyToCodex,
|
||||
applyDirectToCodex,
|
||||
codexSnippet,
|
||||
applyToClaudeCode,
|
||||
restoreClaudeCode,
|
||||
|
||||
+10
-6
@@ -213,15 +213,13 @@ app.whenReady().then(async () => {
|
||||
if (mainWindow && !mainWindow.isDestroyed()) mainWindow.webContents.send("memory-changed");
|
||||
},
|
||||
});
|
||||
// 有分组就自动启动本地路由
|
||||
// 启动时不再自动接管/启动本地路由:用户可能只想用其它功能。
|
||||
// 保存分组已直接写 Codex 直连配置;只有用户显式「启用本地路由」才 start() 路由进程。
|
||||
// 仅刷新一次托盘菜单以反映当前分组/状态(不 start)。
|
||||
try {
|
||||
const st = router.status();
|
||||
if (st.groups.length) {
|
||||
await router.start();
|
||||
rebuildTrayMenu();
|
||||
}
|
||||
} catch (e) {
|
||||
log(`本地路由启动失败:${e.message || e}`);
|
||||
log(`刷新托盘失败:${e.message || e}`);
|
||||
}
|
||||
try {
|
||||
const r = migrateLegacySkills();
|
||||
@@ -1303,6 +1301,12 @@ ipcMain.handle("router-apply-codex", async () => {
|
||||
rebuildTrayMenu();
|
||||
return { ...r, status: router.status() };
|
||||
});
|
||||
// 把指定分组的直连中转配置写进 Codex(不经路由、不起进程)
|
||||
ipcMain.handle("router-apply-direct", async (_e, { id } = {}) => {
|
||||
const r = router.applyDirectToCodex(id);
|
||||
rebuildTrayMenu();
|
||||
return { ...r, status: router.status() };
|
||||
});
|
||||
ipcMain.handle("router-snippet", async () => router.codexSnippet());
|
||||
ipcMain.handle("router-apply-claude", async () => {
|
||||
const r = router.applyToClaudeCode();
|
||||
|
||||
@@ -71,6 +71,7 @@ contextBridge.exposeInMainWorld("api", {
|
||||
routerStart: () => ipcRenderer.invoke("router-start"),
|
||||
routerStop: () => ipcRenderer.invoke("router-stop"),
|
||||
routerApplyCodex: () => ipcRenderer.invoke("router-apply-codex"),
|
||||
routerApplyDirect: (id) => ipcRenderer.invoke("router-apply-direct", { id }),
|
||||
routerApplyClaude: () => ipcRenderer.invoke("router-apply-claude"),
|
||||
routerRestoreClaude: () => ipcRenderer.invoke("router-restore-claude"),
|
||||
routerSnippet: () => ipcRenderer.invoke("router-snippet"),
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "codex-market",
|
||||
"version": "1.5.15",
|
||||
"version": "1.5.16",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "codex-market",
|
||||
"version": "1.5.15",
|
||||
"version": "1.5.16",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"electron-updater": "^6.8.9",
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "codex-market",
|
||||
"version": "1.5.15",
|
||||
"version": "1.5.16",
|
||||
"description": "TD工具箱 / 技能商店 / 备份工具 —— 跨平台桌面客户端",
|
||||
"author": "Codex Market",
|
||||
"license": "MIT",
|
||||
"main": "electron/bootstrap.js",
|
||||
"hotContentVersion": "1.5.15",
|
||||
"hotContentVersion": "1.5.16",
|
||||
"scripts": {
|
||||
"start": "electron .",
|
||||
"dist": "electron-builder",
|
||||
|
||||
+13
-3
@@ -1414,9 +1414,19 @@ $("#grp-save").addEventListener("click", async () => {
|
||||
// 编辑留空时不传 key 字段,避免覆盖
|
||||
if (grpEditing && !key) delete group.key;
|
||||
try {
|
||||
renderRouter(await apic.routerUpsertGroup(group));
|
||||
const st = await apic.routerUpsertGroup(group);
|
||||
renderRouter(st);
|
||||
closeGrpModal();
|
||||
// 保存后根据是否已把直连中转配置写进 Codex 给出对应提示
|
||||
if (st._directApplied) {
|
||||
toast("已保存并写入 Codex 配置 ✅ 重启 Codex 即用当前分组", "ok");
|
||||
} else if (st._directError) {
|
||||
toast("已保存,但写入 Codex 配置失败:" + st._directError, "err");
|
||||
} else if (st._directSkipped) {
|
||||
toast("已保存分组。" + (st._directReason || "该分组需启用本地路由或补全密钥后才能接入 Codex"), "warn");
|
||||
} else {
|
||||
toast("已保存分组 ✅", "ok");
|
||||
}
|
||||
} catch (e) {
|
||||
toast("保存失败:" + (e.message || e), "err");
|
||||
}
|
||||
@@ -1473,13 +1483,13 @@ $("#router-save-port").addEventListener("click", async () => {
|
||||
}
|
||||
});
|
||||
$("#router-apply").addEventListener("click", async (ev) => {
|
||||
if (!confirm("将修改 Codex 的 config.toml,指向本地路由(会自动备份)。\n注意:启用后账号切换请统一在本软件切,不要再用 cc-switch,以免冲突。\n\n确认?")) return;
|
||||
if (!confirm("【高级】启用本地路由:将把 Codex 的 config.toml 改为指向本地路由并启动路由进程(会自动备份)。\n\n普通使用无需此项——保存分组时已把中转配置直接写进 Codex,重启即用。\n仅当你需要「切分组免重启」或使用 Chat 协议转换分组(如 DeepSeek/GLM/Kimi)时才需要启用路由。\n\n注意:启用后账号切换请统一在本软件切,不要再用 cc-switch,以免冲突。\n\n确认启用?")) return;
|
||||
const btn = ev.currentTarget;
|
||||
busy(btn, true, "配置中…");
|
||||
try {
|
||||
const r = await apic.routerApplyCodex();
|
||||
renderRouter(r.status);
|
||||
toast("已配置到 Codex ✅ 重启 Codex 后即可通过本地路由,之后切分组无需重启", "ok");
|
||||
toast("已启用本地路由 ✅ 重启 Codex 后即可通过路由,之后切分组无需重启", "ok");
|
||||
} catch (e) {
|
||||
toast("配置失败:" + (e.message || e), "err");
|
||||
} finally {
|
||||
|
||||
+1
-1
@@ -300,7 +300,7 @@
|
||||
<div><code id="router-addr">—</code> <span class="pc-badge" id="router-state">未启动</span></div>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<button id="router-apply" class="btn primary">一键配置到 Codex</button>
|
||||
<button id="router-apply" class="btn primary">启用本地路由(高级)</button>
|
||||
<button id="router-apply-claude" class="btn">接入 Claude Code</button>
|
||||
<button id="router-toggle" class="btn">启动</button>
|
||||
</div>
|
||||
|
||||
@@ -217,6 +217,7 @@ h3 { margin: 0; font-size: 16px; }
|
||||
max-width: 80%; font-size: 13px; }
|
||||
.toast.ok { border-color: var(--ok); }
|
||||
.toast.err { border-color: var(--danger); }
|
||||
.toast.warn { border-color: #f5a623; }
|
||||
|
||||
/* 卡片内按钮忙碌态 */
|
||||
.installing { pointer-events: none; opacity: .7; }
|
||||
|
||||
Reference in New Issue
Block a user