feat: restore Codex CLI backend

Add back Codex CLI alongside Claude Code CLI and API backends.
All three backends are now available in the settings dropdown.

Change-Id: If93e0e93b724bf0dc18308d54f8d39b47c55deb4
This commit is contained in:
wujunchen 2026-04-26 10:11:59 +08:00
parent 84095fa0eb
commit c8a13b5553
4 changed files with 64 additions and 44 deletions

View file

@ -36,6 +36,7 @@
|------|------|
| **API / Provider**(默认) | 直连 LLM API支持 20+ provider |
| **Claude Code CLI** | 通过本地 `claude -p` 调用,复用 Claude Code 登录态 |
| **Codex CLI** | 通过本地 `codex exec` 调用,复用 Codex 登录态 |
### API / Provider 模式
@ -52,16 +53,16 @@
Model ID 支持 `provider/model` 写法(如 `anthropic/claude-sonnet-4-6`),匹配当前 preset 时自动剥离前缀。
### Claude Code CLI 模式
### CLI 模式Claude Code / Codex
切换后端为 **Claude Code CLI**,插件通过 `claude -p --output-format json` 调用本地 Claude Code
切换后端为 **Claude Code CLI****Codex CLI**,插件通过本地 CLI 调用 LLM
Obsidian 从 GUI 启动时不继承 shell `PATH`,需要在设置中填写 `claude` 的绝对路径:
Obsidian 从 GUI 启动时不继承 shell `PATH`,需要在设置中填写 CLI 的绝对路径:
```bash
# 查看你的 claude 路径
which claude
# 例如:/Users/you/.claude/local/claude
# 查看路径
which claude # Claude Code
which codex # Codex
```
在插件设置的 **CLI 路径** 中填入该路径,点击 **Test** 验证。

54
main.js

File diff suppressed because one or more lines are too long

33
main.ts
View file

@ -3,7 +3,7 @@ import { MarkdownView, Notice, Plugin, requestUrl, TFile } from 'obsidian';
import { findLineForAnchor } from './src/anchor';
import { serializeCacheFile, shouldConfirmRegenerate, touchCacheEntry } from './src/cache';
import { activeIndexAfterCardDelete, removeCardAt, updateCardAt } from './src/cards';
import { resolveCliPath, summarizeViaClaudeCode } from './src/cli';
import { resolveCliPath, summarizeViaClaudeCode, summarizeViaCodex } from './src/cli';
import {
classifyGenerationError,
type GenerationJob,
@ -57,18 +57,25 @@ async function summarizeDocument(
) {
const { system, user } = buildPrompts(content, settings);
let cards: RawCard[];
if (settings.backend === 'claude-code') {
cards = await summarizeViaClaudeCode(system, user, settings, job);
} else {
const useStreaming = supportsStreaming(settings);
const abortController = useStreaming ? new AbortController() : null;
if (abortController) {
job.onCancel(() => abortController.abort());
}
if (useStreaming) {
cards = await summarizeViaApiStreaming(system, user, settings, onStreamProgress, abortController!.signal);
} else {
cards = await summarizeViaApi(requestUrl, system, user, settings);
switch (settings.backend) {
case 'claude-code':
cards = await summarizeViaClaudeCode(system, user, settings, job);
break;
case 'codex':
cards = await summarizeViaCodex(system, user, settings, job);
break;
default: {
const useStreaming = supportsStreaming(settings);
const abortController = useStreaming ? new AbortController() : null;
if (abortController) {
job.onCancel(() => abortController.abort());
}
if (useStreaming) {
cards = await summarizeViaApiStreaming(system, user, settings, onStreamProgress, abortController!.signal);
} else {
cards = await summarizeViaApi(requestUrl, system, user, settings);
}
break;
}
}
const resolved: ResolvedCard[] = cards.map((c) => ({

View file

@ -23,6 +23,11 @@ async function testBackend(settings: PluginSettings) {
const { stdout } = await runCli(cmd, ['--version'], '', 10000);
return `claude @ ${cmd}\n${stdout.trim()}`;
}
if (settings.backend === 'codex') {
const cmd = resolveCliPath('codex', settings.cliPath);
const { stdout } = await runCli(cmd, ['--version'], '', 10000);
return `codex @ ${cmd}\n${stdout.trim()}`;
}
return testApiBackend(requestUrl, settings);
}
@ -61,6 +66,7 @@ export class ParallelReaderSettingTab extends PluginSettingTab {
d
.addOption('api', 'API / Provider')
.addOption('claude-code', 'Claude Code CLI')
.addOption('codex', 'Codex CLI')
.setValue(this.plugin.settings.backend)
.onChange(async (v) => {
this.plugin.settings.backend = v;
@ -72,7 +78,7 @@ export class ParallelReaderSettingTab extends PluginSettingTab {
}),
);
const isCliBacked = this.plugin.settings.backend === 'claude-code';
const isCliBacked = this.plugin.settings.backend === 'claude-code' || this.plugin.settings.backend === 'codex';
if (isCliBacked) {
new Setting(containerEl)