feat(omo): integrate Oh My OpenCode profile management (#972)

* feat(omo): integrate Oh My OpenCode profile management into Provider system

Adds full-stack OMO support: backend config read/write/import, OMO-specific
provider CRUD with exclusive switching, frontend profile editor with
agent/category/model configuration, global config management, and i18n support.

* feat(omo): add model/variant dropdowns from enabled providers

Replace model text inputs with Select dropdowns sourced from enabled
OpenCode providers, add thinking-level variant selection, and prevent
auto-enabling newly added OMO providers.

* fix(omo): use standard provider action styles for OMO switch button

* fix(omo): replace hardcoded isZh strings with proper i18n t() calls
This commit is contained in:
Dex Miller
2026-02-09 12:18:20 +08:00
committed by GitHub
parent 5502c74a79
commit ed47480fc7
35 changed files with 4304 additions and 737 deletions
+1 -13
View File
@@ -17,7 +17,6 @@ import { UniversalProviderPanel } from "@/components/universal";
import { providerPresets } from "@/config/claudeProviderPresets";
import { codexProviderPresets } from "@/config/codexProviderPresets";
import { geminiProviderPresets } from "@/config/geminiProviderPresets";
// Note: opencodeProviderPresets is loaded via ProviderForm, not needed here
import type { UniversalProviderPreset } from "@/config/universalProviderPresets";
interface AddProviderDialogProps {
@@ -36,7 +35,6 @@ export function AddProviderDialog({
onSubmit,
}: AddProviderDialogProps) {
const { t } = useTranslation();
// OpenCode doesn't support universal providers
const showUniversalTab = appId !== "opencode";
const [activeTab, setActiveTab] = useState<"app-specific" | "universal">(
"app-specific",
@@ -45,7 +43,6 @@ export function AddProviderDialog({
const [selectedUniversalPreset, setSelectedUniversalPreset] =
useState<UniversalProviderPreset | null>(null);
// Handle universal provider save
const handleUniversalProviderSave = useCallback(
async (provider: UniversalProvider) => {
try {
@@ -73,7 +70,6 @@ export function AddProviderDialog({
[t, onOpenChange],
);
// Close universal form and return to main dialog
const handleUniversalFormClose = useCallback(() => {
setUniversalFormOpen(false);
setSelectedUniversalPreset(null);
@@ -86,7 +82,6 @@ export function AddProviderDialog({
unknown
>;
// 构造基础提交数据
const providerData: Omit<Provider, "id"> & { providerKey?: string } = {
name: values.name.trim(),
notes: values.notes?.trim() || undefined,
@@ -98,7 +93,6 @@ export function AddProviderDialog({
...(values.meta ? { meta: values.meta } : {}),
};
// OpenCode: pass providerKey for ID generation
if (appId === "opencode" && values.providerKey) {
providerData.providerKey = values.providerKey;
}
@@ -107,8 +101,7 @@ export function AddProviderDialog({
providerData.meta?.custom_endpoints &&
Object.keys(providerData.meta.custom_endpoints).length > 0;
if (!hasCustomEndpoints) {
// 收集端点候选(仅在缺少自定义端点时兜底)
if (!hasCustomEndpoints && values.presetCategory !== "omo") {
const urlSet = new Set<string>();
const addUrl = (rawUrl?: string) => {
@@ -163,7 +156,6 @@ export function AddProviderDialog({
}
}
}
// Note: OpenCode doesn't use endpointCandidates - it handles endpoints internally
}
if (appId === "claude") {
@@ -187,7 +179,6 @@ export function AddProviderDialog({
addUrl(env.GOOGLE_GEMINI_BASE_URL);
}
} else if (appId === "opencode") {
// OpenCode uses options.baseURL
const options = parsedConfig.options as
| Record<string, any>
| undefined;
@@ -221,7 +212,6 @@ export function AddProviderDialog({
[appId, onSubmit, onOpenChange],
);
// 动态 footer:根据当前 Tab 显示不同按钮
const footer =
!showUniversalTab || activeTab === "app-specific" ? (
<>
@@ -296,7 +286,6 @@ export function AddProviderDialog({
</TabsContent>
</Tabs>
) : (
// OpenCode: directly show form without tabs
<ProviderForm
appId={appId}
submitLabel={t("common.add")}
@@ -306,7 +295,6 @@ export function AddProviderDialog({
/>
)}
{/* Universal Provider Form Modal */}
{showUniversalTab && (
<UniversalProviderFormModal
isOpen={universalFormOpen}