diff --git a/apps/web/src/components/add-provider/index.vue b/apps/web/src/components/add-provider/index.vue index b3d47bc8..0ef4ee9e 100644 --- a/apps/web/src/components/add-provider/index.vue +++ b/apps/web/src/components/add-provider/index.vue @@ -46,7 +46,7 @@ @@ -69,12 +69,13 @@
- {{ $t('provider.oauth.createHint') }} + {{ $t(form.values.client_type === 'github-copilot' ? 'provider.oauth.githubCreateHint' : 'provider.oauth.openaiCreateHint') }}
@@ -188,12 +189,13 @@ const { mutateAsync: createProviderMutation, isLoading } = useMutation({ mutation: async (data: Record) => { const config: Record = {} if (data.base_url) config.base_url = data.base_url - if (data.api_key) config.api_key = data.api_key + if (typeof data.api_key === 'string' && data.api_key.trim() !== '' && data.client_type !== 'github-copilot') { + config.api_key = data.api_key.trim() + } const payload = { name: data.name, client_type: data.client_type, config, - metadata: { additionalProp1: {} }, } const { data: result } = await postProviders({ body: payload as ProvidersCreateRequest, throwOnError: true }) if (data.auto_import && result?.id) { @@ -221,18 +223,25 @@ const { mutateAsync: createProviderMutation, isLoading } = useMutation({ const providerSchema = toTypedSchema(z.object({ api_key: z.string().optional(), - base_url: z.string().min(1), + base_url: z.string().optional(), name: z.string().min(1), client_type: z.string().min(1), auto_import: z.boolean().optional(), }).superRefine((value, ctx) => { - if (value.client_type !== 'openai-codex' && !value.api_key?.trim()) { + if (!['openai-codex', 'github-copilot'].includes(value.client_type) && !value.api_key?.trim()) { ctx.addIssue({ code: z.ZodIssueCode.custom, path: ['api_key'], message: 'API key is required', }) } + if (value.client_type !== 'github-copilot' && !value.base_url?.trim()) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: ['base_url'], + message: 'Base URL is required', + }) + } })) const form = useForm({ @@ -240,14 +249,16 @@ const form = useForm({ initialValues: { auto_import: false, client_type: 'openai-completions', - }, + } }) watch(() => form.values.client_type, (clientType) => { - if (clientType !== 'openai-codex') return - if (!form.values.base_url) { + if (clientType === 'openai-codex' && !form.values.base_url) { form.setFieldValue('base_url', 'https://chatgpt.com/backend-api') } + if (clientType === 'github-copilot') { + form.setFieldValue('base_url', '') + } }) const createProvider = form.handleSubmit(async (value) => { diff --git a/apps/web/src/constants/client-types.ts b/apps/web/src/constants/client-types.ts index 57111650..25793aef 100644 --- a/apps/web/src/constants/client-types.ts +++ b/apps/web/src/constants/client-types.ts @@ -20,6 +20,11 @@ export const CLIENT_TYPE_META: Record = { label: 'OpenAI Codex', hint: 'Codex API (OAuth, coding-optimized)', }, + 'github-copilot': { + value: 'github-copilot', + label: 'GitHub Copilot', + hint: 'Device OAuth with GitHub account', + }, 'anthropic-messages': { value: 'anthropic-messages', label: 'Anthropic Messages', diff --git a/apps/web/src/i18n/locales/en.json b/apps/web/src/i18n/locales/en.json index 6df0ccc5..e0fa4755 100644 --- a/apps/web/src/i18n/locales/en.json +++ b/apps/web/src/i18n/locales/en.json @@ -292,20 +292,35 @@ "enable": "Enable", "enableHint": "Only models from enabled providers appear in the available model list", "oauth": { - "title": "OpenAI OAuth", - "description": "Authorize this provider with your ChatGPT account for Codex-compatible OpenAI access.", - "createHint": "Save the provider first, then authorize it from the provider details panel.", + "openaiTitle": "OpenAI OAuth", + "openaiDescription": "Authorize this provider with your ChatGPT account for Codex-compatible OpenAI access.", + "openaiCreateHint": "Save the provider first, then authorize it from the provider details panel.", + "githubTitle": "GitHub Copilot OAuth", + "githubDescription": "Connect the current Memoh account with GitHub Copilot.", + "githubDeviceTitle": "GitHub Copilot Device Authorization", + "githubDeviceDescription": "Start device authorization, open GitHub's verification page, and enter the user code shown below.", + "githubCreateHint": "Save the provider first, then start device authorization from the provider details panel.", + "githubDeviceHint": "Open the verification URL below and enter this user code to authorize the current Memoh account.", "authorize": "Authorize", + "deviceAuthorize": "Start Device Authorization", "authorizeFailed": "Failed to start authorization", "authorizeSuccess": "Authorization successful", "revoke": "Revoke", "revokeFailed": "Failed to revoke authorization", "revokeSuccess": "Authorization revoked", + "copyFailed": "Failed to copy device code", + "connectedAccount": "Connected Account", "callback": "Callback URL", + "deviceVerificationUri": "Verification URL", + "deviceUserCode": "User Code", + "deviceExpiresAt": "Expires At", "statusFailed": "Failed to load OAuth status", "status": { "checking": "Checking authorization status...", "authorized": "Authorized", + "authorizedCurrent": "Current account connected", + "oauthing": "OAuthing...", + "pendingDevice": "Waiting for device authorization to complete...", "expired": "Authorization expired. Re-authorize to continue.", "missing": "Not authorized yet.", "notConfigured": "This provider is not configured for OAuth." diff --git a/apps/web/src/i18n/locales/zh.json b/apps/web/src/i18n/locales/zh.json index c1b55e69..342e8d88 100644 --- a/apps/web/src/i18n/locales/zh.json +++ b/apps/web/src/i18n/locales/zh.json @@ -288,20 +288,35 @@ "enable": "启用", "enableHint": "只有启用的供应商的模型才会出现在可用模型列表中", "oauth": { - "title": "OpenAI OAuth", - "description": "使用你的 ChatGPT 账号为该提供商授权,以启用 Codex 兼容的 OpenAI 访问。", - "createHint": "请先保存提供商,再到详情面板完成授权。", + "openaiTitle": "OpenAI OAuth", + "openaiDescription": "使用你的 ChatGPT 账号为该提供商授权,以启用 Codex 兼容的 OpenAI 访问。", + "openaiCreateHint": "请先保存提供商,再到详情面板完成授权。", + "githubTitle": "GitHub Copilot OAuth", + "githubDescription": "为当前 Memoh 账号连接 GitHub Copilot。", + "githubDeviceTitle": "GitHub Copilot Device Authorization", + "githubDeviceDescription": "启动设备授权后,打开 GitHub 验证页面并输入下方显示的用户代码。", + "githubCreateHint": "请先保存提供商,再到详情面板启动设备授权。", + "githubDeviceHint": "打开下方验证地址,并输入这个用户代码,为当前 Memoh 账号完成授权。", "authorize": "授权", + "deviceAuthorize": "启动设备授权", "authorizeFailed": "启动授权失败", "authorizeSuccess": "授权成功", "revoke": "撤销授权", "revokeFailed": "撤销授权失败", "revokeSuccess": "授权已撤销", + "copyFailed": "复制设备代码失败", + "connectedAccount": "已连接账号", "callback": "回调地址", + "deviceVerificationUri": "验证地址", + "deviceUserCode": "用户代码", + "deviceExpiresAt": "过期时间", "statusFailed": "加载 OAuth 状态失败", "status": { "checking": "正在检查授权状态...", "authorized": "已授权", + "authorizedCurrent": "当前账号已连接", + "oauthing": "授权中...", + "pendingDevice": "正在等待设备授权完成...", "expired": "授权已过期,请重新授权。", "missing": "尚未授权。", "notConfigured": "当前提供商未正确配置 OAuth。" diff --git a/apps/web/src/pages/providers/components/provider-form.vue b/apps/web/src/pages/providers/components/provider-form.vue index 9fd97240..6f2a52b9 100644 --- a/apps/web/src/pages/providers/components/provider-form.vue +++ b/apps/web/src/pages/providers/components/provider-form.vue @@ -21,7 +21,7 @@
@@ -42,7 +42,10 @@
-
+
- {{ $t('provider.oauth.title') }} + {{ $t(form.values.client_type === 'github-copilot' ? 'provider.oauth.githubDeviceTitle' : 'provider.oauth.openaiTitle') }}
- {{ $t('provider.oauth.description') }} + {{ $t(form.values.client_type === 'github-copilot' ? 'provider.oauth.githubDeviceDescription' : 'provider.oauth.openaiDescription') }}
+