feat(ai): 添加 Codex CLI 命令解析和环境变量处理功能

- 实现了对 Codex CLI 命令的解析,支持路径解析和环境变量设置
- 增加了对命令环境的处理,确保正确的执行路径
- 添加了相关的单元测试,确保功能的正确性
This commit is contained in:
二丫讲梵 authored and GitHub committed 2026-06-25 16:47:43 +08:00
1 parent d96448b5a5
commit c2aeaaa8f7
6 files changed
+360 -12

No files matched your search

@@ -18,6 +18,7 @@ import DatabaseIcon from "@/components/icons/DatabaseIcon.vue";
import { useQueryStore } from "@/stores/queryStore";
import { useToast } from "@/composables/useToast";
import { buildAiContext, runAgentStream, type AiAction } from "@/lib/ai";
import { formatAiModelOption } from "@/lib/aiModelPresentation";
import type { AgentEvent } from "@/lib/tauri";
import { buildAiAgentPlan } from "@/lib/aiAgentPlan";
import { buildAiAgentStepItems, type AiAgentStepItem, type AiAgentStepTone } from "@/lib/aiAgentStepPresentation";
@@ -136,6 +137,14 @@ function displayModelName(modelId: string) {
return modelOptions.value.find((model) => model.id === modelId)?.displayName || modelId;
}
function modelOptionPresentation(modelId: string, label = displayModelName(modelId)) {
return formatAiModelOption(label, modelId);
}
function modelOptionSecondary(modelId: string, label = displayModelName(modelId)) {
return modelOptionPresentation(modelId, label).secondary;
}
/** Deferred context compaction info; applied after stream ends to avoid shifting assistantIdx. */
const pendingCompaction = ref<{ summary: string; compactedMessages: number } | null>(null);
@@ -1300,7 +1309,7 @@ const messageRenderer = computed(() => {
:display-name="displayModelName"
trigger-class="min-w-0 w-auto max-w-[220px] shrink justify-end rounded-full border px-2 py-0.5 text-[11px] text-muted-foreground hover:bg-muted hover:text-foreground"
content-class="w-72"
item-class="text-xs px-2"
item-class="h-auto min-h-8 px-2 py-1.5 text-xs"
@update:model-value="handleModelSelect"
@update:open="(open: boolean) => open && fetchModelOptions()"
>
@@ -1308,9 +1317,9 @@ const messageRenderer = computed(() => {
<span class="min-w-0 truncate">{{ loading ? t("ai.loadingModels") : label }}</span>
</template>
<template #option-label="{ option, label }">
<span class="flex min-w-0 flex-col">
<span class="truncate">{{ label }}</span>
<span v-if="label !== option" class="truncate text-[11px] text-muted-foreground">{{ option }}</span>
<span class="flex min-w-0 flex-col leading-tight">
<span class="truncate">{{ modelOptionPresentation(option, label).primary }}</span>
<span v-if="modelOptionSecondary(option, label)" class="mt-0.5 truncate text-[11px] text-muted-foreground">{{ modelOptionSecondary(option, label) }}</span>
</span>
</template>
</SearchableSelect>
@@ -37,6 +37,7 @@ import {
type CustomTheme,
} from "@/stores/settingsStore";
import { loadEditorTheme, editorFontTheme } from "@/lib/editorThemes";
import { formatAiModelOption } from "@/lib/aiModelPresentation";
import ThemeCustomizerDialog from "./ThemeCustomizerDialog.vue";
import { isTauriRuntime } from "@/lib/tauriRuntime";
import { useTheme } from "@/composables/useTheme";
@@ -1594,6 +1595,14 @@ function displayAiModelName(modelId: string): string {
return aiModelOptions.value.find((model) => model.id === modelId)?.displayName || modelId;
}
function aiModelOptionPresentation(modelId: string, label = displayAiModelName(modelId)) {
return formatAiModelOption(label, modelId);
}
function aiModelOptionSecondary(modelId: string, label = displayAiModelName(modelId)) {
return aiModelOptionPresentation(modelId, label).secondary;
}
async function aiRefreshModels() {
if (aiModelLoading.value) return;
if (!aiModelListSupported.value) {
@@ -2985,6 +2994,7 @@ watch(
:display-name="displayAiModelName"
trigger-class="h-8 min-w-[104px] max-w-[150px] shrink-0 border border-border bg-background px-2 text-xs shadow-none hover:bg-muted/50"
content-class="w-72"
item-class="h-auto min-h-8 py-1.5"
@update:model-value="aiSelectModel"
@update:open="onAiModelListOpen"
>
@@ -2992,9 +3002,9 @@ watch(
<span class="truncate">{{ loading ? t("ai.loadingModels") : t("ai.browseModels") }}</span>
</template>
<template #option-label="{ option, label }">
<span class="flex min-w-0 flex-col">
<span class="truncate">{{ label }}</span>
<span v-if="label !== option" class="truncate text-[11px] text-muted-foreground">{{ option }}</span>
<span class="flex min-w-0 flex-col leading-tight">
<span class="truncate">{{ aiModelOptionPresentation(option, label).primary }}</span>
<span v-if="aiModelOptionSecondary(option, label)" class="mt-0.5 truncate text-[11px] text-muted-foreground">{{ aiModelOptionSecondary(option, label) }}</span>
</span>
</template>
</SearchableSelect>