mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-08 02:17:46 +08:00
feat(codex): enroll agent identity from session token
This commit is contained in:
@@ -643,20 +643,53 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<JsonImportInput
|
||||
v-else
|
||||
v-model="importText"
|
||||
:disabled="importing"
|
||||
:reset-key="importInputResetKey"
|
||||
:drop-title="importDropTitle"
|
||||
:drop-hint="importDropHint"
|
||||
:manual-placeholder="importManualPlaceholder"
|
||||
:manual-description="importManualDescription"
|
||||
:paste-toggle-text="importPasteToggleText"
|
||||
:file-toggle-text="importFileToggleText"
|
||||
textarea-class="min-h-[200px] text-xs font-mono break-all !rounded-xl"
|
||||
@error="handleImportInputError"
|
||||
/>
|
||||
<template v-else>
|
||||
<div
|
||||
v-if="isCodexProvider"
|
||||
class="rounded-lg border border-border bg-muted/20 px-3 py-2.5"
|
||||
>
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<div class="min-w-0">
|
||||
<p class="text-xs font-medium">
|
||||
{{ legacyT('使用 Session Token 创建 Agent Identity(实验)') }}
|
||||
</p>
|
||||
<p class="mt-0.5 text-[11px] text-muted-foreground">
|
||||
{{ legacyT('仅用于一次性注册,成功后不会保存 Token。') }}
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
v-model="useSessionTokenAgentIdentity"
|
||||
:disabled="importing"
|
||||
:aria-label="legacyT('使用 Session Token 创建 Agent Identity(实验)')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Textarea
|
||||
v-if="isCodexSessionTokenAgentIdentityImport"
|
||||
v-model="importText"
|
||||
:disabled="importing"
|
||||
:placeholder="legacyT('粘贴 ChatGPT Session Token(JWT)')"
|
||||
class="min-h-[200px] text-xs font-mono break-all !rounded-xl"
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
/>
|
||||
|
||||
<JsonImportInput
|
||||
v-else
|
||||
v-model="importText"
|
||||
:disabled="importing"
|
||||
:reset-key="importInputResetKey"
|
||||
:drop-title="importDropTitle"
|
||||
:drop-hint="importDropHint"
|
||||
:manual-placeholder="importManualPlaceholder"
|
||||
:manual-description="importManualDescription"
|
||||
:paste-toggle-text="importPasteToggleText"
|
||||
:file-toggle-text="importFileToggleText"
|
||||
textarea-class="min-h-[200px] text-xs font-mono break-all !rounded-xl"
|
||||
@error="handleImportInputError"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<div
|
||||
v-if="importTask && !isWindsurfEmailPasswordImport"
|
||||
@@ -740,7 +773,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch, onBeforeUnmount } from 'vue'
|
||||
import { Dialog, Button, Textarea, Popover, PopoverTrigger, PopoverContent } from '@/components/ui'
|
||||
import { Dialog, Button, Textarea, Popover, PopoverTrigger, PopoverContent, Switch } from '@/components/ui'
|
||||
import {
|
||||
ComboboxAnchor,
|
||||
ComboboxContent,
|
||||
@@ -941,6 +974,7 @@ const windsurfImportMethod = ref<WindsurfImportMethod>('email_password')
|
||||
const windsurfEmail = ref('')
|
||||
const windsurfPassword = ref('')
|
||||
const windsurfAccountName = ref('')
|
||||
const useSessionTokenAgentIdentity = ref(false)
|
||||
|
||||
const isOpen = computed(() => props.open)
|
||||
|
||||
@@ -1039,7 +1073,7 @@ const importManualPlaceholder = computed(() => (
|
||||
? legacyT('粘贴 Grok sso/session token,支持每行一个;或粘贴包含 token、sso_token、access_token、plan_type、pool_tier 的 JSON')
|
||||
: isWindsurfProvider.value
|
||||
? legacyT('粘贴 show-auth-token Token、API key 或 JSON 内容')
|
||||
: legacyT('粘贴 Refresh Token / Access Token 或 JSON 内容')
|
||||
: legacyT('粘贴 Refresh Token / Access Token / Agent Identity JSON 内容')
|
||||
))
|
||||
const importManualDescription = computed(() => (
|
||||
isGrokProvider.value
|
||||
@@ -1065,6 +1099,9 @@ const proxyUsageDescription = computed(() => {
|
||||
const isWindsurfEmailPasswordImport = computed(() =>
|
||||
isWindsurfProvider.value && windsurfImportMethod.value === 'email_password'
|
||||
)
|
||||
const isCodexSessionTokenAgentIdentityImport = computed(() =>
|
||||
isCodexProvider.value && useSessionTokenAgentIdentity.value
|
||||
)
|
||||
|
||||
const importButtonText = computed(() => {
|
||||
if (importing.value) {
|
||||
@@ -1072,6 +1109,9 @@ const importButtonText = computed(() => {
|
||||
? (isEnglishLocale() ? `Importing ${importTask.value.progress_percent}%` : `导入中 ${importTask.value.progress_percent}%`)
|
||||
: legacyT('导入中...')
|
||||
}
|
||||
if (isCodexSessionTokenAgentIdentityImport.value) {
|
||||
return legacyT('创建并导入 Agent Identity')
|
||||
}
|
||||
return isWindsurfEmailPasswordImport.value ? legacyT('登录并导入') : importButtonLabel.value
|
||||
})
|
||||
|
||||
@@ -1328,6 +1368,7 @@ function resetForm() {
|
||||
windsurfEmail.value = ''
|
||||
windsurfPassword.value = ''
|
||||
windsurfAccountName.value = ''
|
||||
useSessionTokenAgentIdentity.value = false
|
||||
proxyPopoverOpen.value = false
|
||||
selectedProxyNodeId.value = ''
|
||||
mode.value = defaultMode.value
|
||||
@@ -1782,6 +1823,26 @@ async function handleImport() {
|
||||
return
|
||||
}
|
||||
|
||||
if (isCodexSessionTokenAgentIdentityImport.value) {
|
||||
importing.value = true
|
||||
try {
|
||||
const result = await importProviderRefreshToken(props.providerId, {
|
||||
session_token: inputText,
|
||||
create_agent_identity_from_session_token: true,
|
||||
proxy_node_id: selectedProxyNodeId.value || undefined,
|
||||
})
|
||||
success(getOAuthSuccessMessage('导入', result))
|
||||
emit('saved')
|
||||
handleClose()
|
||||
} catch (err: unknown) {
|
||||
const errorMessage = localizedApiError(err, '创建 Agent Identity 失败')
|
||||
showError(errorMessage, legacyT('错误'))
|
||||
} finally {
|
||||
importing.value = false
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const normalizedCredentials = normalizeBatchImportCredentials(inputText)
|
||||
if (!normalizedCredentials.ok) {
|
||||
showError(legacyT(normalizedCredentials.message), legacyT('格式错误'))
|
||||
|
||||
+49
@@ -82,10 +82,30 @@ vi.mock('@/components/ui', async () => {
|
||||
},
|
||||
})
|
||||
|
||||
const Switch = defineComponent({
|
||||
name: 'SwitchStub',
|
||||
props: {
|
||||
modelValue: Boolean,
|
||||
disabled: Boolean,
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
setup(props, { attrs, emit }) {
|
||||
return () => h('button', {
|
||||
...attrs,
|
||||
type: 'button',
|
||||
role: 'switch',
|
||||
'aria-checked': String(props.modelValue),
|
||||
disabled: props.disabled,
|
||||
onClick: () => emit('update:modelValue', !props.modelValue),
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
Dialog,
|
||||
Button,
|
||||
Textarea,
|
||||
Switch,
|
||||
Popover: passthrough('PopoverStub'),
|
||||
PopoverTrigger: passthrough('PopoverTriggerStub'),
|
||||
PopoverContent: passthrough('PopoverContentStub'),
|
||||
@@ -418,6 +438,35 @@ describe('OAuthAccountDialog Grok import', () => {
|
||||
expect(endpointMocks.importProviderRefreshToken).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('creates Codex Agent Identity from a Session Token only when enabled', async () => {
|
||||
const root = mountDialog('codex')
|
||||
await settle()
|
||||
|
||||
getButton(root, '导入授权')?.click()
|
||||
await settle()
|
||||
|
||||
const sessionTokenSwitch = root.querySelector<HTMLButtonElement>('button[role="switch"]')
|
||||
expect(sessionTokenSwitch).toBeTruthy()
|
||||
expect(sessionTokenSwitch?.getAttribute('aria-checked')).toBe('false')
|
||||
sessionTokenSwitch?.click()
|
||||
await settle()
|
||||
|
||||
const textarea = getImportTextarea(root)
|
||||
textarea.value = 'session-token-for-test-only'
|
||||
textarea.dispatchEvent(new Event('input'))
|
||||
await settle()
|
||||
|
||||
getExactButton(root, '创建并导入 Agent Identity')?.click()
|
||||
await settle()
|
||||
|
||||
expect(endpointMocks.importProviderRefreshToken).toHaveBeenCalledWith('provider-1', {
|
||||
session_token: 'session-token-for-test-only',
|
||||
create_agent_identity_from_session_token: true,
|
||||
proxy_node_id: undefined,
|
||||
})
|
||||
expect(endpointMocks.startBatchImportOAuthTask).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('sends a complete sub2api Agent Identity export through batch import', async () => {
|
||||
const root = mountDialog('codex')
|
||||
await settle()
|
||||
|
||||
Reference in New Issue
Block a user