fix(providers): inform user of clipboard copy and remove copilot auth retry on timeout (#11160)

Signed-off-by: Abhijay Jain <Abhijay007j@gmail.com>
Co-authored-by: Lifei Zhou <lifei@squareup.com>
This commit is contained in:
Abhijay Jain
2026-08-21 07:07:11 +00:00
committed by GitHub
parent 384b6ccfd3
commit 4078158c01
33 changed files with 670 additions and 73 deletions
@@ -35,6 +35,19 @@ pub enum GooseSessionUpdate {
MessageUsage(MessageUsageUpdate),
}
/// Dedicated provider notification for OAuth device-code flow.
/// Sent during provider authentication when the ACP client supports
/// `goose.customNotifications` — avoids a fake empty session ID.
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcNotification)]
#[notification(method = "_goose/unstable/providers/authentication/device-code")]
#[serde(rename_all = "camelCase")]
pub struct ProviderDeviceCodeNotification {
pub provider_id: String,
pub user_code: String,
pub verification_uri: String,
pub expires_in: u64,
}
impl Default for GooseSessionUpdate {
fn default() -> Self {
GooseSessionUpdate::UsageUpdate(SessionUsageUpdate::default())
@@ -142,7 +155,10 @@ where
/// notification, define the struct above (with `JsonRpcNotification` +
/// `Default`) and add one line below.
pub fn custom_notification_schemas(generator: &mut SchemaGenerator) -> Vec<CustomMethodSchema> {
vec![notification_schema::<GooseSessionNotification>(generator)]
vec![
notification_schema::<GooseSessionNotification>(generator),
notification_schema::<ProviderDeviceCodeNotification>(generator),
]
}
#[cfg(test)]
+4
View File
@@ -570,6 +570,10 @@
{
"method": "_goose/unstable/session/update",
"paramsType": "GooseSessionNotification_unstable"
},
{
"method": "_goose/unstable/providers/authentication/device-code",
"paramsType": "ProviderDeviceCodeNotification_unstable"
}
],
"agentRequests": [
+36
View File
@@ -6916,6 +6916,33 @@
],
"description": "Per-message token usage/cost/timing, keyed by the message id used for\nchunk matching. Sent live after a turn's messages and on replay."
},
"ProviderDeviceCodeNotification_unstable": {
"type": "object",
"properties": {
"providerId": {
"type": "string"
},
"userCode": {
"type": "string"
},
"verificationUri": {
"type": "string"
},
"expiresIn": {
"type": "integer",
"minimum": 0
}
},
"required": [
"providerId",
"userCode",
"verificationUri",
"expiresIn"
],
"description": "Dedicated provider notification for OAuth device-code flow.\nSent during provider authentication when the ACP client supports\n`goose.customNotifications` — avoids a fake empty session ID.",
"x-side": "agent",
"x-method": "_goose/unstable/providers/authentication/device-code"
},
"RequestRecipeParams_unstable": {
"type": "object",
"properties": {
@@ -8697,6 +8724,15 @@
],
"description": "Params for _goose/unstable/session/update",
"title": "GooseSessionNotification_unstable"
},
{
"allOf": [
{
"$ref": "#/$defs/ProviderDeviceCodeNotification_unstable"
}
],
"description": "Params for _goose/unstable/providers/authentication/device-code",
"title": "ProviderDeviceCodeNotification_unstable"
}
]
},
+34 -2
View File
@@ -1075,10 +1075,42 @@ impl GooseAcpAgent {
.create_with_default_model(Vec::new())
.await
.internal_err_ctx("Failed to initialize provider")?;
provider
.configure_oauth()
if self.supports_goose_custom_notifications() {
let client_cx = self.client_cx.get().cloned();
let provider_id = req.provider_id.clone();
let announce: Box<dyn Fn(String, String, u64) + Send + Sync> =
Box::new(move |user_code, verification_uri, expires_in| {
let _ = arboard::Clipboard::new()
.ok()
.and_then(|mut cb| cb.set_text(&user_code).ok());
if let Err(e) = webbrowser::open(&verification_uri) {
tracing::warn!("Failed to open browser: {}", e);
}
if let Some(ref cx) = client_cx {
let notification = ProviderDeviceCodeNotification {
provider_id: provider_id.clone(),
user_code,
verification_uri,
expires_in,
};
if let Err(e) = cx.send_notification(notification) {
tracing::warn!("Failed to send device code notification: {}", e);
}
}
});
crate::providers::oauth_device_flow::with_device_code_announce(
announce,
provider.configure_oauth(),
)
.await
.internal_err_ctx("Failed to authenticate provider")?;
} else {
provider
.configure_oauth()
.await
.internal_err_ctx("Failed to authenticate provider")?;
}
}
Config::global().invalidate_secrets_cache();
+2 -13
View File
@@ -5,7 +5,7 @@ use crate::providers::openai_compatible::{
handle_status, stream_openai_compat, stream_responses_compat,
};
use crate::providers::private_file::write_private_file;
use anyhow::{anyhow, Result};
use anyhow::Result;
use async_trait::async_trait;
use axum::http;
use chrono::{DateTime, Utc};
@@ -414,17 +414,6 @@ impl GithubCopilotProvider {
Ok(info)
}
async fn get_access_token(&self) -> Result<String> {
for attempt in 0..3 {
tracing::trace!("attempt {} to get access token", attempt + 1);
match self.login().await {
Ok(token) => return Ok(token),
Err(err) => tracing::warn!("failed to get access token: {}", err),
}
}
Err(anyhow!("failed to get access token after 3 attempts"))
}
async fn login(&self) -> Result<String> {
let cfg = DeviceFlowConfig {
device_auth_url: Some(&self.urls.device_code_url),
@@ -731,7 +720,7 @@ impl Provider for GithubCopilotProvider {
}
let token = self
.get_access_token()
.login()
.await
.map_err(|e| ProviderError::Authentication(format!("OAuth flow failed: {}", e)))?;
@@ -11,6 +11,23 @@ use reqwest::header::HeaderMap;
use reqwest::Client;
use serde::{Deserialize, Serialize};
tokio::task_local! {
/// When set, called instead of the default CLI announce when a device code
/// is obtained. Args: (user_code, verification_uri, expires_in_secs).
/// Set by the ACP server to forward the code to the desktop UI.
static DEVICE_CODE_ANNOUNCE: Box<dyn Fn(String, String, u64) + Send + Sync>;
}
pub async fn with_device_code_announce<F, T>(
announce: Box<dyn Fn(String, String, u64) + Send + Sync>,
fut: F,
) -> T
where
F: std::future::Future<Output = T>,
{
DEVICE_CODE_ANNOUNCE.scope(announce, fut).await
}
/// Fallback poll interval when the server omits `interval` (RFC 8628 §3.2).
const DEFAULT_POLL_INTERVAL_SECS: u64 = 5;
@@ -320,19 +337,32 @@ async fn send_request<T: Serialize + ?Sized>(
}
fn announce_user_action(device: &DeviceCodeResponse) {
if let Ok(mut clipboard) = arboard::Clipboard::new() {
if let Err(e) = clipboard.set_text(&device.user_code) {
tracing::warn!("Failed to copy verification code to clipboard: {}", e);
}
let verify_url = device.verification_url().to_string();
if DEVICE_CODE_ANNOUNCE
.try_with(|f| {
let expires_in = device
.expires_in
.unwrap_or(DEFAULT_DEVICE_CODE_LIFETIME_SECS);
f(device.user_code.clone(), verify_url.clone(), expires_in)
})
.is_ok()
{
return;
}
let verify_url = device.verification_url();
if let Err(e) = webbrowser::open(verify_url) {
let copied = arboard::Clipboard::new()
.ok()
.and_then(|mut cb| cb.set_text(&device.user_code).ok())
.is_some();
if let Err(e) = webbrowser::open(&verify_url) {
tracing::warn!("Failed to open browser: {}", e);
}
// stderr keeps stdout clean for CLI workflows parsing provider output.
let clipboard_hint = if copied { " (copied to clipboard)" } else { "" };
eprintln!(
"Please visit {} and enter code {}",
verify_url, device.user_code
"Please visit {} and enter code {}{}",
verify_url, device.user_code, clipboard_hint
);
}
@@ -39,6 +39,7 @@ function callbacks(): GooseAcpCallbacks {
values: { name: 'Ada' },
}),
unstable_sessionUpdate: vi.fn(),
unstable_providerDeviceCode: vi.fn(),
};
}
+2
View File
@@ -5,6 +5,7 @@ import packageJson from '../../package.json';
import { GOOSE_SERVE_EXITED_USER_MESSAGE } from '../gooseServeLeaseRegistry';
import {
handleAcpGooseSessionNotification,
handleAcpProviderDeviceCodeNotification,
handleAcpSessionNotification,
} from './chatNotifications';
import { requestAcpElicitation } from './elicitationRequests';
@@ -221,6 +222,7 @@ function createClientCallbacks(): GooseAcpCallbacks {
unstable_sessionRecipeRequestParams: requestAcpRecipeParams,
sessionUpdate: handleAcpSessionNotification,
unstable_sessionUpdate: handleAcpGooseSessionNotification,
unstable_providerDeviceCode: handleAcpProviderDeviceCodeNotification,
};
}
+11 -1
View File
@@ -1,4 +1,7 @@
import type { GooseSessionNotification_unstable } from '@aaif/goose-sdk';
import type {
GooseSessionNotification_unstable,
ProviderDeviceCodeNotification_unstable,
} from '@aaif/goose-sdk';
import type { SessionNotification } from '@agentclientprotocol/sdk';
import { AppEvents } from '../constants/events';
import { maybeHandlePlatformEvent } from '../utils/platform_events';
@@ -49,3 +52,10 @@ export function handleAcpGooseSessionNotification(
acpChatSessionActions.applyAcpGooseSessionNotification(notification);
return Promise.resolve();
}
export function handleAcpProviderDeviceCodeNotification(
notification: ProviderDeviceCodeNotification_unstable
): Promise<void> {
window.dispatchEvent(new CustomEvent('goose:device-code', { detail: notification }));
return Promise.resolve();
}
+11 -1
View File
@@ -10,13 +10,15 @@ import {
GOOSE_EXT_NOTIFICATIONS,
GooseExtClient,
type GooseSessionNotification_unstable,
type ProviderDeviceCodeNotification_unstable,
type RecipeParamsResponse_unstable,
type RequestRecipeParams_unstable,
zGooseSessionNotification_unstable,
zProviderDeviceCodeNotification_unstable,
zRequestRecipeParams_unstable,
} from '@aaif/goose-sdk';
const [gooseSessionUpdate] = GOOSE_EXT_NOTIFICATIONS;
const [gooseSessionUpdate, providerDeviceCode] = GOOSE_EXT_NOTIFICATIONS;
const [gooseRecipeParamsRequest] = GOOSE_EXT_AGENT_REQUESTS;
export type GooseAcpCallbacks = Required<
@@ -26,6 +28,9 @@ export type GooseAcpCallbacks = Required<
request: RequestRecipeParams_unstable
) => Promise<RecipeParamsResponse_unstable>;
unstable_sessionUpdate: (notification: GooseSessionNotification_unstable) => Promise<void>;
unstable_providerDeviceCode: (
notification: ProviderDeviceCodeNotification_unstable
) => Promise<void>;
};
export type GooseAcpClient = {
@@ -52,6 +57,11 @@ export function connectGooseAcpClient(
)
.onNotification(gooseSessionUpdate.method, zGooseSessionNotification_unstable, (context) =>
callbacks.unstable_sessionUpdate(context.params)
)
.onNotification(
providerDeviceCode.method,
zProviderDeviceCodeNotification_unstable,
(context) => callbacks.unstable_providerDeviceCode(context.params)
);
const connection = app.connect(stream);
@@ -1,5 +1,6 @@
import { useState } from 'react';
import { acpAuthenticateProvider } from '../../acp/providers';
import { useProviderDeviceCode } from '../../hooks/useProviderDeviceCode';
import type { ProviderDetails } from '../../types/providers';
import DefaultProviderSetupForm, {
ConfigInput,
@@ -23,7 +24,7 @@ const i18n = defineMessages({
deviceCodeFlowHint: {
id: 'providerConfigForm.deviceCodeFlowHint',
defaultMessage:
'A browser window will open and the verification code will be copied to your clipboard. Paste it in the browser to complete sign-in.',
'A browser window will open. The verification code will appear here so you can enter it to complete sign-in.',
},
signingIn: {
id: 'providerConfigForm.signingIn',
@@ -45,6 +46,18 @@ const i18n = defineMessages({
id: 'providerConfigForm.continue',
defaultMessage: 'Continue',
},
deviceCodeVisit: {
id: 'providerConfigForm.deviceCodeVisit',
defaultMessage: 'Visit',
},
deviceCodeAndEnter: {
id: 'providerConfigForm.deviceCodeAndEnter',
defaultMessage: 'and enter:',
},
deviceCodeCopy: {
id: 'providerConfigForm.deviceCodeCopy',
defaultMessage: 'Copy',
},
});
function parseLinks(text: string) {
@@ -78,9 +91,11 @@ function OAuthForm({
}) {
const intl = useIntl();
const [isLoading, setIsLoading] = useState(false);
const { deviceCode, clearDeviceCode } = useProviderDeviceCode(provider.name);
const handleLogin = async () => {
setIsLoading(true);
clearDeviceCode();
try {
await acpAuthenticateProvider(provider.name);
await onConfigured(provider.name);
@@ -106,11 +121,42 @@ function OAuthForm({
? intl.formatMessage(i18n.signingIn)
: intl.formatMessage(i18n.signInWith, { providerName: provider.metadata.display_name })}
</Button>
<p className="text-xs text-text-muted text-center">
{isDeviceCodeFlow
? intl.formatMessage(i18n.deviceCodeFlowHint)
: intl.formatMessage(i18n.browserWindowOpen)}
</p>
{isDeviceCodeFlow && isLoading && deviceCode ? (
<div className="flex flex-col items-center gap-2 w-full">
<p className="text-xs text-text-muted text-center">
{intl.formatMessage(i18n.deviceCodeVisit)}{' '}
<a
href="#"
onClick={(e) => {
e.preventDefault();
window.electron.openExternal(deviceCode.verificationUri);
}}
className="underline"
>
{deviceCode.verificationUri}
</a>{' '}
{intl.formatMessage(i18n.deviceCodeAndEnter)}
</p>
<div className="flex items-center gap-2">
<code className="text-lg font-mono tracking-widest bg-background-muted px-3 py-1 rounded">
{deviceCode.userCode}
</code>
<button
type="button"
onClick={() => navigator.clipboard.writeText(deviceCode.userCode)}
className="text-xs text-text-muted hover:text-text-default underline"
>
{intl.formatMessage(i18n.deviceCodeCopy)}
</button>
</div>
</div>
) : (
<p className="text-xs text-text-muted text-center">
{isDeviceCodeFlow
? intl.formatMessage(i18n.deviceCodeFlowHint)
: intl.formatMessage(i18n.browserWindowOpen)}
</p>
)}
</div>
);
}
@@ -25,6 +25,7 @@ import { AlertTriangle, LogIn } from 'lucide-react';
import type { ProviderDetails } from '../../../../types/providers';
import { Button } from '../../../../components/ui/button';
import { errorMessage } from '../../../../utils/conversionUtils';
import { useProviderDeviceCode } from '../../../../hooks/useProviderDeviceCode';
import AcpReadinessPanel from '../AcpReadinessPanel';
import { defineMessages, useIntl } from '../../../../i18n';
import HuggingFaceSignInPrompt from '../../auth/HuggingFaceSignInPrompt';
@@ -94,7 +95,7 @@ const i18n = defineMessages({
deviceCodeFlowHint: {
id: 'providerConfigurationModal.deviceCodeFlowHint',
defaultMessage:
'A browser window will open and the verification code will be copied to your clipboard. Paste it in the browser to complete sign-in.',
'A browser window will open. The verification code will appear here so you can enter it to complete sign-in.',
},
externalSetupIntro: {
id: 'providerConfigurationModal.externalSetupIntro',
@@ -125,6 +126,18 @@ const i18n = defineMessages({
defaultMessage:
'Sign in to use Hugging Face Inference Providers without manually entering an API token.',
},
deviceCodeVisit: {
id: 'providerConfigurationModal.deviceCodeVisit',
defaultMessage: 'Visit',
},
deviceCodeAndEnter: {
id: 'providerConfigurationModal.deviceCodeAndEnter',
defaultMessage: 'and enter:',
},
deviceCodeCopy: {
id: 'providerConfigurationModal.deviceCodeCopy',
defaultMessage: 'Copy',
},
});
/** Render a setup step string, turning `backtick` spans into <code> and newlines into <br/>. */
@@ -176,6 +189,7 @@ export default function ProviderConfigurationModal({
const [isActiveProvider, setIsActiveProvider] = useState(false);
const [error, setError] = useState<string | null>(null);
const [isOAuthLoading, setIsOAuthLoading] = useState(false);
const { deviceCode, clearDeviceCode } = useProviderDeviceCode(provider.name);
let primaryParameters = provider.metadata.config_keys.filter((param) => param.primary);
if (primaryParameters.length === 0) {
@@ -213,6 +227,7 @@ export default function ProviderConfigurationModal({
const handleOAuthLogin = async () => {
setIsOAuthLoading(true);
clearDeviceCode();
setError(null);
try {
if (hasConfig) {
@@ -390,11 +405,42 @@ export default function ProviderConfigurationModal({
providerName: provider.metadata.display_name,
})}
</Button>
<p className="text-sm text-text-secondary text-center">
{hasDeviceCodeFlow
? intl.formatMessage(i18n.deviceCodeFlowHint)
: intl.formatMessage(i18n.browserWindowHint)}
</p>
{hasDeviceCodeFlow && isOAuthLoading && deviceCode ? (
<div className="flex flex-col items-center gap-2">
<p className="text-sm text-text-secondary text-center">
{intl.formatMessage(i18n.deviceCodeVisit)}{' '}
<a
href="#"
onClick={(e) => {
e.preventDefault();
window.electron.openExternal(deviceCode.verificationUri);
}}
className="underline"
>
{deviceCode.verificationUri}
</a>{' '}
{intl.formatMessage(i18n.deviceCodeAndEnter)}
</p>
<div className="flex items-center gap-2">
<code className="text-xl font-mono tracking-widest bg-background-muted px-4 py-2 rounded">
{deviceCode.userCode}
</code>
<button
type="button"
onClick={() => navigator.clipboard.writeText(deviceCode.userCode)}
className="text-xs text-text-muted hover:text-text-default underline"
>
{intl.formatMessage(i18n.deviceCodeCopy)}
</button>
</div>
</div>
) : (
<p className="text-sm text-text-secondary text-center">
{hasDeviceCodeFlow
? intl.formatMessage(i18n.deviceCodeFlowHint)
: intl.formatMessage(i18n.browserWindowHint)}
</p>
)}
</div>
)}
@@ -0,0 +1,32 @@
import { act, renderHook } from '@testing-library/react';
import { describe, expect, it } from 'vitest';
import { useProviderDeviceCode } from './useProviderDeviceCode';
function dispatchDeviceCode(providerId: string, userCode: string) {
window.dispatchEvent(
new CustomEvent('goose:device-code', {
detail: {
providerId,
userCode,
verificationUri: 'https://example.com/device',
expiresIn: 300,
},
})
);
}
describe('useProviderDeviceCode', () => {
it('only accepts device codes for the requested provider', () => {
const { result } = renderHook(() => useProviderDeviceCode('github_copilot'));
act(() => {
dispatchDeviceCode('kimicode', 'KIMI-CODE');
});
expect(result.current.deviceCode).toBeNull();
act(() => {
dispatchDeviceCode('github_copilot', 'COPILOT-CODE');
});
expect(result.current.deviceCode?.userCode).toBe('COPILOT-CODE');
});
});
@@ -0,0 +1,24 @@
import { useEffect, useState } from 'react';
import type { ProviderDeviceCodeNotification_unstable } from '@aaif/goose-sdk';
export function useProviderDeviceCode(providerId: string) {
const [deviceCode, setDeviceCode] = useState<ProviderDeviceCodeNotification_unstable | null>(
null
);
useEffect(() => {
const handler = (event: Event) => {
const detail = (event as CustomEvent<ProviderDeviceCodeNotification_unstable>).detail;
if (detail.providerId === providerId) {
setDeviceCode(detail);
}
};
window.addEventListener('goose:device-code', handler);
return () => window.removeEventListener('goose:device-code', handler);
}, [providerId]);
return {
deviceCode: deviceCode?.providerId === providerId ? deviceCode : null,
clearDeviceCode: () => setDeviceCode(null),
};
}
+20 -2
View File
@@ -2742,7 +2742,7 @@
"defaultMessage": "Weiter"
},
"providerConfigForm.deviceCodeFlowHint": {
"defaultMessage": "Es öffnet sich ein Browserfenster und der Bestätigungscode wird in Ihre Zwischenablage kopiert. Fügen Sie ihn im Browser ein, um die Anmeldung abzuschließen."
"defaultMessage": "Ein Browserfenster wird geöffnet. Der Bestätigungscode wird hier angezeigt, damit Sie ihn eingeben und die Anmeldung abschließen können."
},
"providerConfigForm.noApiKey": {
"defaultMessage": "Sie haben keinen API-Schlüssel?"
@@ -2781,7 +2781,7 @@
"defaultMessage": "Dadurch wird die aktuelle Anbieterkonfiguration dauerhaft gelöscht."
},
"providerConfigurationModal.deviceCodeFlowHint": {
"defaultMessage": "Es öffnet sich ein Browserfenster und der Bestätigungscode wird in Ihre Zwischenablage kopiert. Fügen Sie ihn im Browser ein, um die Anmeldung abzuschließen."
"defaultMessage": "Ein Browserfenster wird geöffnet. Der Bestätigungscode wird hier angezeigt, damit Sie ihn eingeben und die Anmeldung abschließen können."
},
"providerConfigurationModal.errorCheckingConfig": {
"defaultMessage": "Beim Überprüfen dieser Anbieterkonfiguration ist ein Fehler aufgetreten."
@@ -4666,5 +4666,23 @@
},
"acpReadinessPanel.connectionNotChecked": {
"defaultMessage": "Prüfen Sie Ihre Kontoverbindung, bevor Sie fortfahren."
},
"providerConfigForm.deviceCodeAndEnter": {
"defaultMessage": "und geben Sie ein:"
},
"providerConfigForm.deviceCodeCopy": {
"defaultMessage": "Kopieren"
},
"providerConfigForm.deviceCodeVisit": {
"defaultMessage": "Besuchen Sie"
},
"providerConfigurationModal.deviceCodeAndEnter": {
"defaultMessage": "und geben Sie ein:"
},
"providerConfigurationModal.deviceCodeCopy": {
"defaultMessage": "Kopieren"
},
"providerConfigurationModal.deviceCodeVisit": {
"defaultMessage": "Besuchen Sie"
}
}
+20 -2
View File
@@ -2768,8 +2768,17 @@
"providerConfigForm.continue": {
"defaultMessage": "Continue"
},
"providerConfigForm.deviceCodeAndEnter": {
"defaultMessage": "and enter:"
},
"providerConfigForm.deviceCodeCopy": {
"defaultMessage": "Copy"
},
"providerConfigForm.deviceCodeFlowHint": {
"defaultMessage": "A browser window will open and the verification code will be copied to your clipboard. Paste it in the browser to complete sign-in."
"defaultMessage": "A browser window will open. The verification code will appear here so you can enter it to complete sign-in."
},
"providerConfigForm.deviceCodeVisit": {
"defaultMessage": "Visit"
},
"providerConfigForm.noApiKey": {
"defaultMessage": "Don't have an API key?"
@@ -2810,8 +2819,17 @@
"providerConfigurationModal.deleteConfirmation": {
"defaultMessage": "This will permanently delete the current provider configuration."
},
"providerConfigurationModal.deviceCodeAndEnter": {
"defaultMessage": "and enter:"
},
"providerConfigurationModal.deviceCodeCopy": {
"defaultMessage": "Copy"
},
"providerConfigurationModal.deviceCodeFlowHint": {
"defaultMessage": "A browser window will open and the verification code will be copied to your clipboard. Paste it in the browser to complete sign-in."
"defaultMessage": "A browser window will open. The verification code will appear here so you can enter it to complete sign-in."
},
"providerConfigurationModal.deviceCodeVisit": {
"defaultMessage": "Visit"
},
"providerConfigurationModal.errorCheckingConfig": {
"defaultMessage": "There was an error checking this provider configuration."
+20 -2
View File
@@ -2742,7 +2742,7 @@
"defaultMessage": "Continuar"
},
"providerConfigForm.deviceCodeFlowHint": {
"defaultMessage": "Se abrirá una ventana del navegador y el código de verificación se copiará en tu portapapeles. Pégalo en el navegador para completar el inicio de sesión."
"defaultMessage": "Se abrirá una ventana del navegador. El código de verificación aparecerá aquí para que puedas introducirlo y completar el inicio de sesión."
},
"providerConfigForm.noApiKey": {
"defaultMessage": "¿No tienes una clave de API?"
@@ -2781,7 +2781,7 @@
"defaultMessage": "Esto eliminará permanentemente la configuración actual del proveedor."
},
"providerConfigurationModal.deviceCodeFlowHint": {
"defaultMessage": "Se abrirá una ventana del navegador y el código de verificación se copiará en tu portapapeles. Pégalo en el navegador para completar el inicio de sesión."
"defaultMessage": "Se abrirá una ventana del navegador. El código de verificación aparecerá aquí para que puedas introducirlo y completar el inicio de sesión."
},
"providerConfigurationModal.errorCheckingConfig": {
"defaultMessage": "Hubo un error al comprobar la configuración de este proveedor."
@@ -4666,5 +4666,23 @@
},
"acpReadinessPanel.connectionNotChecked": {
"defaultMessage": "Comprueba la conexión de tu cuenta antes de continuar."
},
"providerConfigForm.deviceCodeAndEnter": {
"defaultMessage": "e ingresa:"
},
"providerConfigForm.deviceCodeCopy": {
"defaultMessage": "Copiar"
},
"providerConfigForm.deviceCodeVisit": {
"defaultMessage": "Visita"
},
"providerConfigurationModal.deviceCodeAndEnter": {
"defaultMessage": "e ingresa:"
},
"providerConfigurationModal.deviceCodeCopy": {
"defaultMessage": "Copiar"
},
"providerConfigurationModal.deviceCodeVisit": {
"defaultMessage": "Visita"
}
}
+20 -2
View File
@@ -2742,7 +2742,7 @@
"defaultMessage": "Continuer"
},
"providerConfigForm.deviceCodeFlowHint": {
"defaultMessage": "Une fenêtre de navigateur s'ouvrira et le code de vérification sera copié dans votre presse-papiers. Collez-le dans le navigateur pour terminer la connexion."
"defaultMessage": "Une fenêtre de navigateur souvrira. Le code de vérification apparaîtra ici afin que vous puissiez le saisir pour terminer la connexion."
},
"providerConfigForm.noApiKey": {
"defaultMessage": "Vous n'avez pas de clé API ?"
@@ -2781,7 +2781,7 @@
"defaultMessage": "Cette action supprimera définitivement la configuration actuelle du fournisseur."
},
"providerConfigurationModal.deviceCodeFlowHint": {
"defaultMessage": "Une fenêtre de navigateur s'ouvrira et le code de vérification sera copié dans votre presse-papiers. Collez-le dans le navigateur pour terminer la connexion."
"defaultMessage": "Une fenêtre de navigateur souvrira. Le code de vérification apparaîtra ici afin que vous puissiez le saisir pour terminer la connexion."
},
"providerConfigurationModal.errorCheckingConfig": {
"defaultMessage": "Une erreur s'est produite lors de la vérification de la configuration de ce fournisseur."
@@ -4666,5 +4666,23 @@
},
"acpReadinessPanel.connectionNotChecked": {
"defaultMessage": "Vérifiez la connexion de votre compte avant de continuer."
},
"providerConfigForm.deviceCodeAndEnter": {
"defaultMessage": "et entrez :"
},
"providerConfigForm.deviceCodeCopy": {
"defaultMessage": "Copier"
},
"providerConfigForm.deviceCodeVisit": {
"defaultMessage": "Visitez"
},
"providerConfigurationModal.deviceCodeAndEnter": {
"defaultMessage": "et entrez :"
},
"providerConfigurationModal.deviceCodeCopy": {
"defaultMessage": "Copier"
},
"providerConfigurationModal.deviceCodeVisit": {
"defaultMessage": "Visitez"
}
}
+20 -2
View File
@@ -2742,7 +2742,7 @@
"defaultMessage": "जारी रखें"
},
"providerConfigForm.deviceCodeFlowHint": {
"defaultMessage": "एक ब्राउज़र विंडो खुलेगी और सत्यापन कोड आपके क्लिपबोर्ड पर कॉपी हो जाएगा। साइन-इन पूरा करने के लिए इसे ब्राउज़र में पेस्ट करें।"
"defaultMessage": "एक ब्राउज़र विंडो खुलेगी सत्यापन कोड यहाँ दिखाई देगा, ताकि आप उसे दर्ज करके साइन-इन पूरा कर सकें।"
},
"providerConfigForm.noApiKey": {
"defaultMessage": "क्या आपके पास API कुंजी नहीं है?"
@@ -2781,7 +2781,7 @@
"defaultMessage": "यह वर्तमान प्रदाता कॉन्फ़िगरेशन को स्थायी रूप से हटा देगा।"
},
"providerConfigurationModal.deviceCodeFlowHint": {
"defaultMessage": "एक ब्राउज़र विंडो खुलेगी और सत्यापन कोड आपके क्लिपबोर्ड पर कॉपी हो जाएगा। साइन-इन पूरा करने के लिए इसे ब्राउज़र में पेस्ट करें।"
"defaultMessage": "एक ब्राउज़र विंडो खुलेगी सत्यापन कोड यहाँ दिखाई देगा, ताकि आप उसे दर्ज करके साइन-इन पूरा कर सकें।"
},
"providerConfigurationModal.errorCheckingConfig": {
"defaultMessage": "इस प्रदाता कॉन्फ़िगरेशन की जाँच करने में त्रुटि हुई।"
@@ -4666,5 +4666,23 @@
},
"acpReadinessPanel.connectionNotChecked": {
"defaultMessage": "आगे बढ़ने से पहले अपने खाते का कनेक्शन जाँचें।"
},
"providerConfigForm.deviceCodeAndEnter": {
"defaultMessage": "और दर्ज करें:"
},
"providerConfigForm.deviceCodeCopy": {
"defaultMessage": "कॉपी करें"
},
"providerConfigForm.deviceCodeVisit": {
"defaultMessage": "जाएं"
},
"providerConfigurationModal.deviceCodeAndEnter": {
"defaultMessage": "और दर्ज करें:"
},
"providerConfigurationModal.deviceCodeCopy": {
"defaultMessage": "कॉपी करें"
},
"providerConfigurationModal.deviceCodeVisit": {
"defaultMessage": "जाएं"
}
}
+20 -2
View File
@@ -2742,7 +2742,7 @@
"defaultMessage": "Lanjutkan"
},
"providerConfigForm.deviceCodeFlowHint": {
"defaultMessage": "Jendela browser akan terbuka dan kode verifikasi akan disalin ke clipboard Anda. Tempelkan di browser untuk menyelesaikan proses masuk."
"defaultMessage": "Jendela browser akan terbuka. Kode verifikasi akan muncul di sini agar Anda dapat memasukkannya untuk menyelesaikan proses masuk."
},
"providerConfigForm.noApiKey": {
"defaultMessage": "Tidak punya kunci API?"
@@ -2781,7 +2781,7 @@
"defaultMessage": "Tindakan ini akan menghapus konfigurasi penyedia saat ini secara permanen."
},
"providerConfigurationModal.deviceCodeFlowHint": {
"defaultMessage": "Jendela browser akan terbuka dan kode verifikasi akan disalin ke clipboard Anda. Tempelkan di browser untuk menyelesaikan proses masuk."
"defaultMessage": "Jendela browser akan terbuka. Kode verifikasi akan muncul di sini agar Anda dapat memasukkannya untuk menyelesaikan proses masuk."
},
"providerConfigurationModal.errorCheckingConfig": {
"defaultMessage": "Terjadi kesalahan saat memeriksa konfigurasi penyedia ini."
@@ -4666,5 +4666,23 @@
},
"acpReadinessPanel.connectionNotChecked": {
"defaultMessage": "Periksa koneksi akun Anda sebelum melanjutkan."
},
"providerConfigForm.deviceCodeAndEnter": {
"defaultMessage": "dan masukkan:"
},
"providerConfigForm.deviceCodeCopy": {
"defaultMessage": "Salin"
},
"providerConfigForm.deviceCodeVisit": {
"defaultMessage": "Kunjungi"
},
"providerConfigurationModal.deviceCodeAndEnter": {
"defaultMessage": "dan masukkan:"
},
"providerConfigurationModal.deviceCodeCopy": {
"defaultMessage": "Salin"
},
"providerConfigurationModal.deviceCodeVisit": {
"defaultMessage": "Kunjungi"
}
}
+20 -2
View File
@@ -2742,7 +2742,7 @@
"defaultMessage": "Continua"
},
"providerConfigForm.deviceCodeFlowHint": {
"defaultMessage": "Si aprirà una finestra del browser e il codice di verifica verrà copiato negli appunti. Incollalo nel browser per completare l'accesso."
"defaultMessage": "Si aprirà una finestra del browser. Il codice di verifica verrà visualizzato qui, così potrai inserirlo per completare l'accesso."
},
"providerConfigForm.noApiKey": {
"defaultMessage": "Non hai una chiave API?"
@@ -2781,7 +2781,7 @@
"defaultMessage": "Questa operazione eliminerà definitivamente la configurazione attuale del provider."
},
"providerConfigurationModal.deviceCodeFlowHint": {
"defaultMessage": "Si aprirà una finestra del browser e il codice di verifica verrà copiato negli appunti. Incollalo nel browser per completare l'accesso."
"defaultMessage": "Si aprirà una finestra del browser. Il codice di verifica verrà visualizzato qui, così potrai inserirlo per completare l'accesso."
},
"providerConfigurationModal.errorCheckingConfig": {
"defaultMessage": "Si è verificato un errore durante la verifica della configurazione di questo provider."
@@ -4666,5 +4666,23 @@
},
"acpReadinessPanel.connectionNotChecked": {
"defaultMessage": "Verifica la connessione del tuo account prima di continuare."
},
"providerConfigForm.deviceCodeAndEnter": {
"defaultMessage": "e inserisci:"
},
"providerConfigForm.deviceCodeCopy": {
"defaultMessage": "Copia"
},
"providerConfigForm.deviceCodeVisit": {
"defaultMessage": "Visita"
},
"providerConfigurationModal.deviceCodeAndEnter": {
"defaultMessage": "e inserisci:"
},
"providerConfigurationModal.deviceCodeCopy": {
"defaultMessage": "Copia"
},
"providerConfigurationModal.deviceCodeVisit": {
"defaultMessage": "Visita"
}
}
+20 -2
View File
@@ -2742,7 +2742,7 @@
"defaultMessage": "続行"
},
"providerConfigForm.deviceCodeFlowHint": {
"defaultMessage": "ブラウザーウィンドウが開き確認コードがクリップボードにコピーされます。ブラウザーに貼り付けてサインインを完了してください。"
"defaultMessage": "ブラウザーウィンドウが開きます。確認コードがここに表示されるので、入力してサインインを完了してください。"
},
"providerConfigForm.noApiKey": {
"defaultMessage": "APIキーをお持ちでないですか?"
@@ -2781,7 +2781,7 @@
"defaultMessage": "現在のプロバイダー設定は完全に削除されます。"
},
"providerConfigurationModal.deviceCodeFlowHint": {
"defaultMessage": "ブラウザーウィンドウが開き確認コードがクリップボードにコピーされます。ブラウザーに貼り付けてサインインを完了してください。"
"defaultMessage": "ブラウザーウィンドウが開きます。確認コードがここに表示されるので、入力してサインインを完了してください。"
},
"providerConfigurationModal.errorCheckingConfig": {
"defaultMessage": "このプロバイダー設定の確認中にエラーが発生しました。"
@@ -4666,5 +4666,23 @@
},
"acpReadinessPanel.connectionNotChecked": {
"defaultMessage": "続行する前にアカウントの接続を確認してください。"
},
"providerConfigForm.deviceCodeAndEnter": {
"defaultMessage": "コードを入力してください:"
},
"providerConfigForm.deviceCodeCopy": {
"defaultMessage": "コピー"
},
"providerConfigForm.deviceCodeVisit": {
"defaultMessage": "次のURLにアクセスし、"
},
"providerConfigurationModal.deviceCodeAndEnter": {
"defaultMessage": "コードを入力してください:"
},
"providerConfigurationModal.deviceCodeCopy": {
"defaultMessage": "コピー"
},
"providerConfigurationModal.deviceCodeVisit": {
"defaultMessage": "次のURLにアクセスし、"
}
}
+20 -2
View File
@@ -2742,7 +2742,7 @@
"defaultMessage": "계속"
},
"providerConfigForm.deviceCodeFlowHint": {
"defaultMessage": "브라우저 창이 열리고 인증 코드가 클립보드에 복사됩니다. 로그인을 완료하려면 브라우저에 붙여넣으세요."
"defaultMessage": "브라우저 창이 열립니다. 인증 코드가 여기에 표시되면 입력하여 로그인을 완료하세요."
},
"providerConfigForm.noApiKey": {
"defaultMessage": "API 키가 없나요?"
@@ -2781,7 +2781,7 @@
"defaultMessage": "현재 제공업체 구성이 영구적으로 삭제됩니다."
},
"providerConfigurationModal.deviceCodeFlowHint": {
"defaultMessage": "브라우저 창이 열리고 인증 코드가 클립보드에 복사됩니다. 로그인을 완료하려면 브라우저에 붙여넣으세요."
"defaultMessage": "브라우저 창이 열립니다. 인증 코드가 여기에 표시되면 입력하여 로그인을 완료하세요."
},
"providerConfigurationModal.errorCheckingConfig": {
"defaultMessage": "이 제공업체 구성을 확인하는 동안 오류가 발생했습니다."
@@ -4666,5 +4666,23 @@
},
"acpReadinessPanel.connectionNotChecked": {
"defaultMessage": "계속하기 전에 계정 연결을 확인하세요."
},
"providerConfigForm.deviceCodeAndEnter": {
"defaultMessage": "코드를 입력하세요:"
},
"providerConfigForm.deviceCodeCopy": {
"defaultMessage": "복사"
},
"providerConfigForm.deviceCodeVisit": {
"defaultMessage": "방문하여"
},
"providerConfigurationModal.deviceCodeAndEnter": {
"defaultMessage": "코드를 입력하세요:"
},
"providerConfigurationModal.deviceCodeCopy": {
"defaultMessage": "복사"
},
"providerConfigurationModal.deviceCodeVisit": {
"defaultMessage": "방문하여"
}
}
+20 -2
View File
@@ -2742,7 +2742,7 @@
"defaultMessage": "Teruskan"
},
"providerConfigForm.deviceCodeFlowHint": {
"defaultMessage": "Tetingkap pelayar akan dibuka dan kod pengesahan akan disalin ke papan keratan anda. Tampalkannya dalam pelayar untuk melengkapkan log masuk."
"defaultMessage": "Tetingkap pelayar akan dibuka. Kod pengesahan akan dipaparkan di sini supaya anda boleh memasukkannya untuk melengkapkan log masuk."
},
"providerConfigForm.noApiKey": {
"defaultMessage": "Tidak mempunyai kunci API?"
@@ -2781,7 +2781,7 @@
"defaultMessage": "Ini akan memadamkan konfigurasi penyedia semasa secara kekal."
},
"providerConfigurationModal.deviceCodeFlowHint": {
"defaultMessage": "Tetingkap pelayar akan dibuka dan kod pengesahan akan disalin ke papan keratan anda. Tampalkannya dalam pelayar untuk melengkapkan log masuk."
"defaultMessage": "Tetingkap pelayar akan dibuka. Kod pengesahan akan dipaparkan di sini supaya anda boleh memasukkannya untuk melengkapkan log masuk."
},
"providerConfigurationModal.errorCheckingConfig": {
"defaultMessage": "Terdapat ralat semasa menyemak konfigurasi penyedia ini."
@@ -4666,5 +4666,23 @@
},
"acpReadinessPanel.connectionNotChecked": {
"defaultMessage": "Semak sambungan akaun anda sebelum meneruskan."
},
"providerConfigForm.deviceCodeAndEnter": {
"defaultMessage": "dan masukkan:"
},
"providerConfigForm.deviceCodeCopy": {
"defaultMessage": "Salin"
},
"providerConfigForm.deviceCodeVisit": {
"defaultMessage": "Lawati"
},
"providerConfigurationModal.deviceCodeAndEnter": {
"defaultMessage": "dan masukkan:"
},
"providerConfigurationModal.deviceCodeCopy": {
"defaultMessage": "Salin"
},
"providerConfigurationModal.deviceCodeVisit": {
"defaultMessage": "Lawati"
}
}
+20 -2
View File
@@ -2742,7 +2742,7 @@
"defaultMessage": "Continuar"
},
"providerConfigForm.deviceCodeFlowHint": {
"defaultMessage": "Será aberta uma janela do navegador e o código de verificação será copiado para a área de transferência. Cole-o no navegador para concluir o início de sessão."
"defaultMessage": "Será aberta uma janela do navegador. O código de verificação aparecerá aqui para que possa introduzi-lo e concluir o início de sessão."
},
"providerConfigForm.noApiKey": {
"defaultMessage": "Não tem uma chave de API?"
@@ -2781,7 +2781,7 @@
"defaultMessage": "Isto irá eliminar permanentemente a configuração atual do fornecedor."
},
"providerConfigurationModal.deviceCodeFlowHint": {
"defaultMessage": "Será aberta uma janela do navegador e o código de verificação será copiado para a área de transferência. Cole-o no navegador para concluir o início de sessão."
"defaultMessage": "Será aberta uma janela do navegador. O código de verificação aparecerá aqui para que possa introduzi-lo e concluir o início de sessão."
},
"providerConfigurationModal.errorCheckingConfig": {
"defaultMessage": "Ocorreu um erro ao verificar a configuração deste fornecedor."
@@ -4666,5 +4666,23 @@
},
"acpReadinessPanel.connectionNotChecked": {
"defaultMessage": "Verifique a ligação da sua conta antes de continuar."
},
"providerConfigForm.deviceCodeAndEnter": {
"defaultMessage": "e insira:"
},
"providerConfigForm.deviceCodeCopy": {
"defaultMessage": "Copiar"
},
"providerConfigForm.deviceCodeVisit": {
"defaultMessage": "Acesse"
},
"providerConfigurationModal.deviceCodeAndEnter": {
"defaultMessage": "e insira:"
},
"providerConfigurationModal.deviceCodeCopy": {
"defaultMessage": "Copiar"
},
"providerConfigurationModal.deviceCodeVisit": {
"defaultMessage": "Acesse"
}
}
+20 -2
View File
@@ -2742,7 +2742,7 @@
"defaultMessage": "Продолжить"
},
"providerConfigForm.deviceCodeFlowHint": {
"defaultMessage": "Откроется окно браузера, а код подтверждения будет скопирован в буфер обмена. Вставьте его в браузере, чтобы завершить вход."
"defaultMessage": "Откроется окно браузера. Код подтверждения появится здесь — введите его, чтобы завершить вход."
},
"providerConfigForm.noApiKey": {
"defaultMessage": "Нет API-ключа?"
@@ -2781,7 +2781,7 @@
"defaultMessage": "Это навсегда удалит текущую конфигурацию провайдера."
},
"providerConfigurationModal.deviceCodeFlowHint": {
"defaultMessage": "Откроется окно браузера, а код подтверждения будет скопирован в буфер обмена. Вставьте его в браузере, чтобы завершить вход."
"defaultMessage": "Откроется окно браузера. Код подтверждения появится здесь — введите его, чтобы завершить вход."
},
"providerConfigurationModal.errorCheckingConfig": {
"defaultMessage": "При проверке конфигурации этого провайдера произошла ошибка."
@@ -4666,5 +4666,23 @@
},
"acpReadinessPanel.connectionNotChecked": {
"defaultMessage": "Проверьте подключение учетной записи, прежде чем продолжить."
},
"providerConfigForm.deviceCodeAndEnter": {
"defaultMessage": "и введите:"
},
"providerConfigForm.deviceCodeCopy": {
"defaultMessage": "Копировать"
},
"providerConfigForm.deviceCodeVisit": {
"defaultMessage": "Перейдите на"
},
"providerConfigurationModal.deviceCodeAndEnter": {
"defaultMessage": "и введите:"
},
"providerConfigurationModal.deviceCodeCopy": {
"defaultMessage": "Копировать"
},
"providerConfigurationModal.deviceCodeVisit": {
"defaultMessage": "Перейдите на"
}
}
+20 -2
View File
@@ -2742,7 +2742,7 @@
"defaultMessage": "Devam et"
},
"providerConfigForm.deviceCodeFlowHint": {
"defaultMessage": "Bir tarayıcı penceresi açılacak ve doğrulama kodu panonuza kopyalanacaktır. Oturum açmayı tamamlamak için tarayıcıya yapıştırın."
"defaultMessage": "Bir tarayıcı penceresi açılacak. Doğrulama kodu burada görünecek; oturum açmayı tamamlamak için kodu girin."
},
"providerConfigForm.noApiKey": {
"defaultMessage": "API anahtarınız yok mu?"
@@ -2781,7 +2781,7 @@
"defaultMessage": "Bu, mevcut sağlayıcı yapılandırmasını kalıcı olarak silecektir."
},
"providerConfigurationModal.deviceCodeFlowHint": {
"defaultMessage": "Bir tarayıcı penceresi açılacak ve doğrulama kodu panonuza kopyalanacaktır. Oturum açmayı tamamlamak için tarayıcıya yapıştırın."
"defaultMessage": "Bir tarayıcı penceresi açılacak. Doğrulama kodu burada görünecek; oturum açmayı tamamlamak için kodu girin."
},
"providerConfigurationModal.errorCheckingConfig": {
"defaultMessage": "Bu sağlayıcı yapılandırması kontrol edilirken bir hata oluştu."
@@ -4666,5 +4666,23 @@
},
"acpReadinessPanel.connectionNotChecked": {
"defaultMessage": "Devam etmeden önce hesap bağlantınızı kontrol edin."
},
"providerConfigForm.deviceCodeAndEnter": {
"defaultMessage": "ve girin:"
},
"providerConfigForm.deviceCodeCopy": {
"defaultMessage": "Kopyala"
},
"providerConfigForm.deviceCodeVisit": {
"defaultMessage": "Ziyaret edin"
},
"providerConfigurationModal.deviceCodeAndEnter": {
"defaultMessage": "ve girin:"
},
"providerConfigurationModal.deviceCodeCopy": {
"defaultMessage": "Kopyala"
},
"providerConfigurationModal.deviceCodeVisit": {
"defaultMessage": "Ziyaret edin"
}
}
+20 -2
View File
@@ -2742,7 +2742,7 @@
"defaultMessage": "Tiếp tục"
},
"providerConfigForm.deviceCodeFlowHint": {
"defaultMessage": "Một cửa sổ trình duyệt sẽ mở ra và mã xác minh sẽ được sao chép vào bộ nhớ tạm của bạn. Dán mã vào trình duyệt để hoàn tất đăng nhập."
"defaultMessage": "Một cửa sổ trình duyệt sẽ mở ra. Mã xác minh sẽ xuất hiện tại đây để bạn nhập và hoàn tất đăng nhập."
},
"providerConfigForm.noApiKey": {
"defaultMessage": "Bạn chưa có khóa API?"
@@ -2781,7 +2781,7 @@
"defaultMessage": "Thao tác này sẽ xóa vĩnh viễn cấu hình nhà cung cấp hiện tại."
},
"providerConfigurationModal.deviceCodeFlowHint": {
"defaultMessage": "Một cửa sổ trình duyệt sẽ mở ra và mã xác minh sẽ được sao chép vào bộ nhớ tạm của bạn. Dán mã vào trình duyệt để hoàn tất đăng nhập."
"defaultMessage": "Một cửa sổ trình duyệt sẽ mở ra. Mã xác minh sẽ xuất hiện tại đây để bạn nhập và hoàn tất đăng nhập."
},
"providerConfigurationModal.errorCheckingConfig": {
"defaultMessage": "Đã xảy ra lỗi khi kiểm tra cấu hình nhà cung cấp này."
@@ -4666,5 +4666,23 @@
},
"acpReadinessPanel.connectionNotChecked": {
"defaultMessage": "Hãy kiểm tra kết nối tài khoản trước khi tiếp tục."
},
"providerConfigForm.deviceCodeAndEnter": {
"defaultMessage": "và nhập:"
},
"providerConfigForm.deviceCodeCopy": {
"defaultMessage": "Sao chép"
},
"providerConfigForm.deviceCodeVisit": {
"defaultMessage": "Truy cập"
},
"providerConfigurationModal.deviceCodeAndEnter": {
"defaultMessage": "và nhập:"
},
"providerConfigurationModal.deviceCodeCopy": {
"defaultMessage": "Sao chép"
},
"providerConfigurationModal.deviceCodeVisit": {
"defaultMessage": "Truy cập"
}
}
+20 -2
View File
@@ -2742,7 +2742,7 @@
"defaultMessage": "继续"
},
"providerConfigForm.deviceCodeFlowHint": {
"defaultMessage": "将打开浏览器窗口,验证码将复制到剪贴板。把它粘贴到浏览器即可完成登录。"
"defaultMessage": "将打开浏览器窗口。验证码会显示在此处,输入验证码即可完成登录。"
},
"providerConfigForm.noApiKey": {
"defaultMessage": "没有 API 密钥?"
@@ -2781,7 +2781,7 @@
"defaultMessage": "这将永久删除当前的提供商配置。"
},
"providerConfigurationModal.deviceCodeFlowHint": {
"defaultMessage": "将打开浏览器窗口,验证码将复制到剪贴板。把它粘贴到浏览器即可完成登录。"
"defaultMessage": "将打开浏览器窗口。验证码会显示在此处,输入验证码即可完成登录。"
},
"providerConfigurationModal.errorCheckingConfig": {
"defaultMessage": "检查此提供商配置时出错。"
@@ -4666,5 +4666,23 @@
},
"acpReadinessPanel.connectionNotChecked": {
"defaultMessage": "请先检查账户连接,然后再继续。"
},
"providerConfigForm.deviceCodeAndEnter": {
"defaultMessage": "并输入:"
},
"providerConfigForm.deviceCodeCopy": {
"defaultMessage": "复制"
},
"providerConfigForm.deviceCodeVisit": {
"defaultMessage": "访问"
},
"providerConfigurationModal.deviceCodeAndEnter": {
"defaultMessage": "并输入:"
},
"providerConfigurationModal.deviceCodeCopy": {
"defaultMessage": "复制"
},
"providerConfigurationModal.deviceCodeVisit": {
"defaultMessage": "访问"
}
}
+20 -2
View File
@@ -2742,7 +2742,7 @@
"defaultMessage": "繼續"
},
"providerConfigForm.deviceCodeFlowHint": {
"defaultMessage": "將開啟瀏覽器視窗,且驗證碼會複製到您的剪貼簿。將其貼到瀏覽器中以完成登入。"
"defaultMessage": "將開啟瀏覽器視窗驗證碼會顯示在此處,輸入驗證碼即可完成登入。"
},
"providerConfigForm.noApiKey": {
"defaultMessage": "沒有 API 金鑰嗎?"
@@ -2781,7 +2781,7 @@
"defaultMessage": "這將永久刪除目前的提供者組態。"
},
"providerConfigurationModal.deviceCodeFlowHint": {
"defaultMessage": "將開啟瀏覽器視窗,且驗證碼會複製到您的剪貼簿。將其貼到瀏覽器中以完成登入。"
"defaultMessage": "將開啟瀏覽器視窗驗證碼會顯示在此處,輸入驗證碼即可完成登入。"
},
"providerConfigurationModal.errorCheckingConfig": {
"defaultMessage": "檢查此提供者組態時發生錯誤。"
@@ -4666,5 +4666,23 @@
},
"acpReadinessPanel.connectionNotChecked": {
"defaultMessage": "請先檢查帳戶連線,然後再繼續。"
},
"providerConfigForm.deviceCodeAndEnter": {
"defaultMessage": "並輸入:"
},
"providerConfigForm.deviceCodeCopy": {
"defaultMessage": "複製"
},
"providerConfigForm.deviceCodeVisit": {
"defaultMessage": "前往"
},
"providerConfigurationModal.deviceCodeAndEnter": {
"defaultMessage": "並輸入:"
},
"providerConfigurationModal.deviceCodeCopy": {
"defaultMessage": "複製"
},
"providerConfigurationModal.deviceCodeVisit": {
"defaultMessage": "前往"
}
}
File diff suppressed because one or more lines are too long
+13 -1
View File
@@ -2626,6 +2626,18 @@ export type MessageUsageUpdate = {
usage: MessageUsageData;
};
/**
* Dedicated provider notification for OAuth device-code flow.
* Sent during provider authentication when the ACP client supports
* `goose.customNotifications` avoids a fake empty session ID.
*/
export type ProviderDeviceCodeNotification_unstable = {
providerId: string;
userCode: string;
verificationUri: string;
expiresIn: number;
};
export type RequestRecipeParams_unstable = {
sessionId: string;
parameters: Array<RecipeParameterDto>;
@@ -2663,7 +2675,7 @@ export type ExtResponse = {
export type ExtNotification = {
method: string;
params?: GooseSessionNotification_unstable | {
params?: GooseSessionNotification_unstable | ProviderDeviceCodeNotification_unstable | {
[key: string]: unknown;
} | null;
};
+16 -1
View File
@@ -2163,6 +2163,18 @@ export const zGooseSessionNotification_unstable = z.object({
update: zGooseSessionUpdate
});
/**
* Dedicated provider notification for OAuth device-code flow.
* Sent during provider authentication when the ACP client supports
* `goose.customNotifications` avoids a fake empty session ID.
*/
export const zProviderDeviceCodeNotification_unstable = z.object({
providerId: z.string(),
userCode: z.string(),
verificationUri: z.string(),
expiresIn: z.int().gte(0)
});
export const zRequestRecipeParams_unstable = z.object({
sessionId: z.string(),
parameters: z.array(zRecipeParameterDto),
@@ -2397,7 +2409,10 @@ export const zExtResponse = z.union([
export const zExtNotification = z.object({
method: z.string(),
params: z.union([
zGooseSessionNotification_unstable,
z.union([
zGooseSessionNotification_unstable,
zProviderDeviceCodeNotification_unstable
]),
z.record(z.string(), z.unknown())
]).nullish()
});