Merge remote-tracking branch 'origin/main'
This commit is contained in:
10
.github/workflows/release.yml
vendored
10
.github/workflows/release.yml
vendored
@@ -456,21 +456,15 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload to release (Unix)
|
- name: Upload to release (Unix)
|
||||||
if: runner.os != 'Windows'
|
if: runner.os != 'Windows'
|
||||||
uses: softprops/action-gh-release@v2
|
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
run: gh release upload "${{ github.ref_name }}" dist/${{ matrix.artifact }}.tar.gz --clobber
|
||||||
tag_name: ${{ github.ref_name }}
|
|
||||||
files: dist/${{ matrix.artifact }}.tar.gz
|
|
||||||
|
|
||||||
- name: Upload to release (Windows)
|
- name: Upload to release (Windows)
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
uses: softprops/action-gh-release@v2
|
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
run: gh release upload "${{ github.ref_name }}" dist/${{ matrix.artifact }}.zip --clobber
|
||||||
tag_name: ${{ github.ref_name }}
|
|
||||||
files: dist/${{ matrix.artifact }}.zip
|
|
||||||
|
|
||||||
build-docker:
|
build-docker:
|
||||||
needs:
|
needs:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "codeg",
|
"name": "codeg",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.8.7",
|
"version": "0.8.8",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev --turbopack",
|
"dev": "next dev --turbopack",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
|
|||||||
2
src-tauri/Cargo.lock
generated
2
src-tauri/Cargo.lock
generated
@@ -853,7 +853,7 @@ checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "codeg"
|
name = "codeg"
|
||||||
version = "0.8.7"
|
version = "0.8.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"agent-client-protocol-schema",
|
"agent-client-protocol-schema",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "codeg"
|
name = "codeg"
|
||||||
version = "0.8.7"
|
version = "0.8.8"
|
||||||
description = "Agent Code Generation App"
|
description = "Agent Code Generation App"
|
||||||
authors = ["feitao"]
|
authors = ["feitao"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|||||||
@@ -3077,3 +3077,219 @@ pub async fn opencode_uninstall_plugin(
|
|||||||
) -> Result<PluginCheckSummary, AcpError> {
|
) -> Result<PluginCheckSummary, AcpError> {
|
||||||
opencode_uninstall_plugin_core(name).await
|
opencode_uninstall_plugin_core(name).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── Codex Device Code OAuth ───
|
||||||
|
|
||||||
|
const CODEX_OAUTH_ISSUER: &str = "https://auth.openai.com";
|
||||||
|
const CODEX_OAUTH_CLIENT_ID: &str = "app_EMoamEEZ73f0CkXaXp7hrann";
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct CodexDeviceCodeResponse {
|
||||||
|
pub user_code: String,
|
||||||
|
pub verification_url: String,
|
||||||
|
pub device_auth_id: String,
|
||||||
|
pub interval: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct CodexDeviceCodePollResult {
|
||||||
|
pub status: String,
|
||||||
|
pub message: Option<String>,
|
||||||
|
pub id_token: Option<String>,
|
||||||
|
pub access_token: Option<String>,
|
||||||
|
pub refresh_token: Option<String>,
|
||||||
|
pub account_id: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct DeviceCodeUserCodeResp {
|
||||||
|
device_auth_id: String,
|
||||||
|
#[serde(alias = "usercode")]
|
||||||
|
user_code: String,
|
||||||
|
#[serde(default = "default_interval", deserialize_with = "deserialize_interval")]
|
||||||
|
interval: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_interval() -> u64 {
|
||||||
|
5
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_jwt_account_id(jwt: &str) -> Option<String> {
|
||||||
|
let payload = jwt.split('.').nth(1)?;
|
||||||
|
let decoded = base64::Engine::decode(
|
||||||
|
&base64::engine::general_purpose::URL_SAFE_NO_PAD,
|
||||||
|
payload,
|
||||||
|
)
|
||||||
|
.ok()?;
|
||||||
|
let value: serde_json::Value = serde_json::from_slice(&decoded).ok()?;
|
||||||
|
value
|
||||||
|
.get("https://api.openai.com/auth")
|
||||||
|
.and_then(|auth| auth.get("chatgpt_account_id"))
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_interval<'de, D>(deserializer: D) -> Result<u64, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
use serde::de;
|
||||||
|
let value = serde_json::Value::deserialize(deserializer)?;
|
||||||
|
match &value {
|
||||||
|
serde_json::Value::Number(n) => n.as_u64().ok_or_else(|| {
|
||||||
|
de::Error::custom(format!("invalid interval number: {n}"))
|
||||||
|
}),
|
||||||
|
serde_json::Value::String(s) => s.trim().parse::<u64>().map_err(de::Error::custom),
|
||||||
|
_ => Err(de::Error::custom(format!("unexpected interval type: {value}"))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct DeviceCodeTokenResp {
|
||||||
|
authorization_code: String,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
code_challenge: String,
|
||||||
|
code_verifier: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct OAuthTokenResp {
|
||||||
|
id_token: String,
|
||||||
|
access_token: String,
|
||||||
|
refresh_token: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn codex_request_device_code_core()
|
||||||
|
-> Result<CodexDeviceCodeResponse, AcpError>
|
||||||
|
{
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
let url = format!("{CODEX_OAUTH_ISSUER}/api/accounts/deviceauth/usercode");
|
||||||
|
let body = serde_json::json!({ "client_id": CODEX_OAUTH_CLIENT_ID });
|
||||||
|
|
||||||
|
let resp = client
|
||||||
|
.post(&url)
|
||||||
|
.json(&body)
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.map_err(|e| AcpError::protocol(format!("device code request failed: {e}")))?;
|
||||||
|
|
||||||
|
if !resp.status().is_success() {
|
||||||
|
let status = resp.status();
|
||||||
|
let text = resp.text().await.unwrap_or_default();
|
||||||
|
return Err(AcpError::protocol(format!(
|
||||||
|
"device code request returned {status}: {text}"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let raw_body = resp
|
||||||
|
.text()
|
||||||
|
.await
|
||||||
|
.map_err(|e| AcpError::protocol(format!("read device code response failed: {e}")))?;
|
||||||
|
let uc: DeviceCodeUserCodeResp = serde_json::from_str(&raw_body)
|
||||||
|
.map_err(|e| AcpError::protocol(format!("parse device code response failed: {e} | body: {raw_body}")))?;
|
||||||
|
|
||||||
|
Ok(CodexDeviceCodeResponse {
|
||||||
|
user_code: uc.user_code,
|
||||||
|
verification_url: format!("{CODEX_OAUTH_ISSUER}/codex/device"),
|
||||||
|
device_auth_id: uc.device_auth_id,
|
||||||
|
interval: uc.interval,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tauri-runtime", tauri::command)]
|
||||||
|
pub async fn codex_request_device_code()
|
||||||
|
-> Result<CodexDeviceCodeResponse, AcpError>
|
||||||
|
{
|
||||||
|
codex_request_device_code_core().await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tauri-runtime", tauri::command)]
|
||||||
|
pub async fn codex_poll_device_code(
|
||||||
|
device_auth_id: String,
|
||||||
|
user_code: String,
|
||||||
|
) -> Result<CodexDeviceCodePollResult, AcpError> {
|
||||||
|
codex_poll_device_code_core(device_auth_id, user_code).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn codex_poll_device_code_core(
|
||||||
|
device_auth_id: String,
|
||||||
|
user_code: String,
|
||||||
|
) -> Result<CodexDeviceCodePollResult, AcpError> {
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
let poll_url = format!("{CODEX_OAUTH_ISSUER}/api/accounts/deviceauth/token");
|
||||||
|
let poll_body = serde_json::json!({
|
||||||
|
"device_auth_id": device_auth_id,
|
||||||
|
"user_code": user_code,
|
||||||
|
});
|
||||||
|
|
||||||
|
let resp = client
|
||||||
|
.post(&poll_url)
|
||||||
|
.json(&poll_body)
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.map_err(|e| AcpError::protocol(format!("device code poll failed: {e}")))?;
|
||||||
|
|
||||||
|
if !resp.status().is_success() {
|
||||||
|
return Ok(CodexDeviceCodePollResult {
|
||||||
|
status: "pending".into(),
|
||||||
|
message: None,
|
||||||
|
id_token: None,
|
||||||
|
access_token: None,
|
||||||
|
refresh_token: None,
|
||||||
|
account_id: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let code_resp: DeviceCodeTokenResp = resp
|
||||||
|
.json()
|
||||||
|
.await
|
||||||
|
.map_err(|e| AcpError::protocol(format!("parse poll response failed: {e}")))?;
|
||||||
|
|
||||||
|
let redirect_uri = format!("{CODEX_OAUTH_ISSUER}/deviceauth/callback");
|
||||||
|
let token_url = format!("{CODEX_OAUTH_ISSUER}/oauth/token");
|
||||||
|
|
||||||
|
let token_resp = client
|
||||||
|
.post(&token_url)
|
||||||
|
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
.body(format!(
|
||||||
|
"grant_type=authorization_code&code={}&redirect_uri={}&client_id={}&code_verifier={}",
|
||||||
|
urlencoding::encode(&code_resp.authorization_code),
|
||||||
|
urlencoding::encode(&redirect_uri),
|
||||||
|
urlencoding::encode(CODEX_OAUTH_CLIENT_ID),
|
||||||
|
urlencoding::encode(&code_resp.code_verifier),
|
||||||
|
))
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.map_err(|e| AcpError::protocol(format!("token exchange failed: {e}")))?;
|
||||||
|
|
||||||
|
if !token_resp.status().is_success() {
|
||||||
|
let status = token_resp.status();
|
||||||
|
let text = token_resp.text().await.unwrap_or_default();
|
||||||
|
return Ok(CodexDeviceCodePollResult {
|
||||||
|
status: "error".into(),
|
||||||
|
message: Some(format!("token exchange returned {status}: {text}")),
|
||||||
|
id_token: None,
|
||||||
|
access_token: None,
|
||||||
|
refresh_token: None,
|
||||||
|
account_id: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let tokens: OAuthTokenResp = token_resp
|
||||||
|
.json()
|
||||||
|
.await
|
||||||
|
.map_err(|e| AcpError::protocol(format!("parse token response failed: {e}")))?;
|
||||||
|
|
||||||
|
let account_id = extract_jwt_account_id(&tokens.id_token).unwrap_or_default();
|
||||||
|
|
||||||
|
Ok(CodexDeviceCodePollResult {
|
||||||
|
status: "success".into(),
|
||||||
|
message: None,
|
||||||
|
id_token: Some(tokens.id_token),
|
||||||
|
access_token: Some(tokens.access_token),
|
||||||
|
refresh_token: Some(tokens.refresh_token),
|
||||||
|
account_id: Some(account_id),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -393,6 +393,8 @@ mod tauri_app {
|
|||||||
acp_commands::opencode_list_plugins,
|
acp_commands::opencode_list_plugins,
|
||||||
acp_commands::opencode_install_plugins,
|
acp_commands::opencode_install_plugins,
|
||||||
acp_commands::opencode_uninstall_plugin,
|
acp_commands::opencode_uninstall_plugin,
|
||||||
|
acp_commands::codex_request_device_code,
|
||||||
|
acp_commands::codex_poll_device_code,
|
||||||
experts_commands::experts_list,
|
experts_commands::experts_list,
|
||||||
experts_commands::experts_list_for_agent,
|
experts_commands::experts_list_for_agent,
|
||||||
experts_commands::experts_get_install_status,
|
experts_commands::experts_get_install_status,
|
||||||
|
|||||||
@@ -591,3 +591,30 @@ pub async fn opencode_uninstall_plugin(
|
|||||||
.map_err(|e| AppCommandError::task_execution_failed(e.to_string()))?;
|
.map_err(|e| AppCommandError::task_execution_failed(e.to_string()))?;
|
||||||
Ok(Json(result))
|
Ok(Json(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn codex_request_device_code(
|
||||||
|
) -> Result<Json<acp_commands::CodexDeviceCodeResponse>, AppCommandError> {
|
||||||
|
let result = acp_commands::codex_request_device_code_core()
|
||||||
|
.await
|
||||||
|
.map_err(|e| AppCommandError::task_execution_failed(e.to_string()))?;
|
||||||
|
Ok(Json(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct CodexPollDeviceCodeParams {
|
||||||
|
pub device_auth_id: String,
|
||||||
|
pub user_code: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn codex_poll_device_code(
|
||||||
|
Json(params): Json<CodexPollDeviceCodeParams>,
|
||||||
|
) -> Result<Json<acp_commands::CodexDeviceCodePollResult>, AppCommandError> {
|
||||||
|
let result = acp_commands::codex_poll_device_code_core(
|
||||||
|
params.device_auth_id,
|
||||||
|
params.user_code,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|e| AppCommandError::task_execution_failed(e.to_string()))?;
|
||||||
|
Ok(Json(result))
|
||||||
|
}
|
||||||
|
|||||||
@@ -459,6 +459,14 @@ pub fn build_router(state: Arc<AppState>, token: String, static_dir: std::path::
|
|||||||
"/opencode_uninstall_plugin",
|
"/opencode_uninstall_plugin",
|
||||||
post(handlers::acp::opencode_uninstall_plugin),
|
post(handlers::acp::opencode_uninstall_plugin),
|
||||||
)
|
)
|
||||||
|
.route(
|
||||||
|
"/codex_request_device_code",
|
||||||
|
post(handlers::acp::codex_request_device_code),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/codex_poll_device_code",
|
||||||
|
post(handlers::acp::codex_poll_device_code),
|
||||||
|
)
|
||||||
// ─── Experts ───
|
// ─── Experts ───
|
||||||
.route("/experts_list", post(handlers::experts::experts_list))
|
.route("/experts_list", post(handlers::experts::experts_list))
|
||||||
.route(
|
.route(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "codeg",
|
"productName": "codeg",
|
||||||
"version": "0.8.7",
|
"version": "0.8.8",
|
||||||
"identifier": "app.codeg",
|
"identifier": "app.codeg",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "pnpm dev",
|
"beforeDevCommand": "pnpm dev",
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
CheckCircle2,
|
CheckCircle2,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
ChevronRight,
|
ChevronRight,
|
||||||
|
Copy,
|
||||||
Download,
|
Download,
|
||||||
Eye,
|
Eye,
|
||||||
EyeOff,
|
EyeOff,
|
||||||
@@ -75,6 +76,8 @@ import {
|
|||||||
acpUninstallAgent,
|
acpUninstallAgent,
|
||||||
acpUpdateAgentConfig,
|
acpUpdateAgentConfig,
|
||||||
acpUpdateAgentEnv,
|
acpUpdateAgentEnv,
|
||||||
|
codexPollDeviceCode,
|
||||||
|
codexRequestDeviceCode,
|
||||||
listModelProviders,
|
listModelProviders,
|
||||||
} from "@/lib/api"
|
} from "@/lib/api"
|
||||||
import type {
|
import type {
|
||||||
@@ -1549,6 +1552,18 @@ function inferCodexAuthMode(authJsonText: string): CodexAuthMode {
|
|||||||
return "api_key"
|
return "api_key"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasCodexChatgptTokens(authJsonText: string): boolean {
|
||||||
|
const { authObject } = parseCodexAuthJsonObject(authJsonText)
|
||||||
|
if (!authObject) return false
|
||||||
|
const tokens = authObject.tokens as Record<string, unknown> | undefined
|
||||||
|
if (tokens && typeof tokens === "object") {
|
||||||
|
return (
|
||||||
|
typeof tokens.access_token === "string" && tokens.access_token.length > 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
function extractCodexImportantValues(
|
function extractCodexImportantValues(
|
||||||
authJsonText: string,
|
authJsonText: string,
|
||||||
configTomlText: string
|
configTomlText: string
|
||||||
@@ -2685,6 +2700,17 @@ export function AcpAgentSettings() {
|
|||||||
const installStream = useAgentInstallStream()
|
const installStream = useAgentInstallStream()
|
||||||
const [streamAgentType, setStreamAgentType] = useState<AgentType | null>(null)
|
const [streamAgentType, setStreamAgentType] = useState<AgentType | null>(null)
|
||||||
const installLogEndRef = useRef<HTMLDivElement | null>(null)
|
const installLogEndRef = useRef<HTMLDivElement | null>(null)
|
||||||
|
const [codexDeviceCode, setCodexDeviceCode] = useState<{
|
||||||
|
userCode: string
|
||||||
|
verificationUrl: string
|
||||||
|
deviceAuthId: string
|
||||||
|
interval: number
|
||||||
|
} | null>(null)
|
||||||
|
const [codexLoginStatus, setCodexLoginStatus] = useState<
|
||||||
|
"idle" | "requesting" | "polling" | "success" | "error"
|
||||||
|
>("idle")
|
||||||
|
const [codexLoginError, setCodexLoginError] = useState<string | null>(null)
|
||||||
|
const codexPollCancelledRef = useRef(false)
|
||||||
|
|
||||||
const sortedAgents = useMemo(
|
const sortedAgents = useMemo(
|
||||||
() =>
|
() =>
|
||||||
@@ -3426,13 +3452,8 @@ export function AcpAgentSettings() {
|
|||||||
|
|
||||||
const selectedMissingModelProvider =
|
const selectedMissingModelProvider =
|
||||||
selectedNeedsModelProvider && selectedDraft?.modelProviderId == null
|
selectedNeedsModelProvider && selectedDraft?.modelProviderId == null
|
||||||
const selectedCodexAuthJsonText = selectedDraft?.codexAuthJsonText ?? ""
|
|
||||||
const selectedConfigText = selectedDraft?.configText ?? ""
|
const selectedConfigText = selectedDraft?.configText ?? ""
|
||||||
const selectedOpenCodeAuthJsonText = selectedDraft?.openCodeAuthJsonText ?? ""
|
const selectedOpenCodeAuthJsonText = selectedDraft?.openCodeAuthJsonText ?? ""
|
||||||
const selectedCodexAuthError = useMemo(() => {
|
|
||||||
if (selectedAgentKind !== "codex" || !locale) return null
|
|
||||||
return parseCodexAuthJsonText(selectedCodexAuthJsonText)
|
|
||||||
}, [locale, selectedAgentKind, selectedCodexAuthJsonText])
|
|
||||||
const selectedCodexReasoningEffortOption =
|
const selectedCodexReasoningEffortOption =
|
||||||
selectedAgent?.agent_type === "codex" && selectedDraft
|
selectedAgent?.agent_type === "codex" && selectedDraft
|
||||||
? (CODEX_REASONING_EFFORT_OPTIONS.find(
|
? (CODEX_REASONING_EFFORT_OPTIONS.find(
|
||||||
@@ -4593,31 +4614,6 @@ export function AcpAgentSettings() {
|
|||||||
[handleOpenCodeConfigPatch]
|
[handleOpenCodeConfigPatch]
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleCodexAuthJsonTextChange = useCallback(
|
|
||||||
(nextText: string) => {
|
|
||||||
if (!selectedAgent || selectedAgent.agent_type !== "codex") return
|
|
||||||
const important = extractCodexImportantValues(
|
|
||||||
nextText,
|
|
||||||
selectedDraft?.codexConfigTomlText ?? ""
|
|
||||||
)
|
|
||||||
updateSelectedDraft((current) => ({
|
|
||||||
...current,
|
|
||||||
codexAuthMode: inferCodexAuthMode(nextText),
|
|
||||||
codexAuthJsonText: nextText,
|
|
||||||
apiBaseUrl: important.apiBaseUrl,
|
|
||||||
apiKey: important.apiKey ?? current.apiKey,
|
|
||||||
model: important.model,
|
|
||||||
codexModelProvider: important.modelProvider,
|
|
||||||
codexProviderOptions: important.providerOptions,
|
|
||||||
codexReasoningEffort: important.reasoningEffort,
|
|
||||||
codexSupportsWebsockets: important.supportsWebsockets,
|
|
||||||
codexSkills: important.skills,
|
|
||||||
codexServiceTierFast: important.serviceTierFast,
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
[selectedAgent, selectedDraft, updateSelectedDraft]
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleCodexConfigTomlTextChange = useCallback(
|
const handleCodexConfigTomlTextChange = useCallback(
|
||||||
(nextText: string) => {
|
(nextText: string) => {
|
||||||
if (!selectedAgent || selectedAgent.agent_type !== "codex") return
|
if (!selectedAgent || selectedAgent.agent_type !== "codex") return
|
||||||
@@ -4900,6 +4896,138 @@ export function AcpAgentSettings() {
|
|||||||
[selectedAgent, selectedDraft, updateSelectedDraft]
|
[selectedAgent, selectedDraft, updateSelectedDraft]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const handleCodexDeviceLogin = useCallback(async () => {
|
||||||
|
setCodexLoginStatus("requesting")
|
||||||
|
setCodexLoginError(null)
|
||||||
|
setCodexDeviceCode(null)
|
||||||
|
codexPollCancelledRef.current = false
|
||||||
|
try {
|
||||||
|
const resp = await codexRequestDeviceCode()
|
||||||
|
setCodexDeviceCode(resp)
|
||||||
|
setCodexLoginStatus("polling")
|
||||||
|
} catch (err) {
|
||||||
|
const msg = err instanceof Error ? err.message : String(err)
|
||||||
|
setCodexLoginError(msg)
|
||||||
|
setCodexLoginStatus("error")
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const cancelCodexDeviceLogin = useCallback(() => {
|
||||||
|
codexPollCancelledRef.current = true
|
||||||
|
setCodexLoginStatus("idle")
|
||||||
|
setCodexDeviceCode(null)
|
||||||
|
setCodexLoginError(null)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (codexLoginStatus !== "polling" || !codexDeviceCode) return
|
||||||
|
codexPollCancelledRef.current = false
|
||||||
|
const pollInterval = (codexDeviceCode.interval || 5) * 1000
|
||||||
|
const deadline = Date.now() + 15 * 60 * 1000
|
||||||
|
let timer: ReturnType<typeof setTimeout> | null = null
|
||||||
|
let active = true
|
||||||
|
|
||||||
|
const poll = async () => {
|
||||||
|
if (!active || codexPollCancelledRef.current) return
|
||||||
|
if (Date.now() > deadline) {
|
||||||
|
setCodexLoginError(t("codex.loginTimeout"))
|
||||||
|
setCodexLoginStatus("error")
|
||||||
|
setCodexDeviceCode(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const result = await codexPollDeviceCode({
|
||||||
|
deviceAuthId: codexDeviceCode.deviceAuthId,
|
||||||
|
userCode: codexDeviceCode.userCode,
|
||||||
|
})
|
||||||
|
if (!active || codexPollCancelledRef.current) return
|
||||||
|
if (result.status === "success") {
|
||||||
|
setCodexLoginStatus("success")
|
||||||
|
setCodexDeviceCode(null)
|
||||||
|
const authJson = JSON.stringify(
|
||||||
|
{
|
||||||
|
auth_mode: "chatgpt",
|
||||||
|
OPENAI_API_KEY: null,
|
||||||
|
tokens: {
|
||||||
|
id_token: result.idToken,
|
||||||
|
access_token: result.accessToken,
|
||||||
|
refresh_token: result.refreshToken,
|
||||||
|
account_id: result.accountId ?? "",
|
||||||
|
},
|
||||||
|
last_refresh: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
updateSelectedDraft((current) => ({
|
||||||
|
...current,
|
||||||
|
codexAuthJsonText: authJson,
|
||||||
|
}))
|
||||||
|
const draft = drafts.codex
|
||||||
|
if (draft) {
|
||||||
|
const codexEnvText =
|
||||||
|
draft.codexAuthMode === "chatgpt_subscription"
|
||||||
|
? patchEnvText(draft.envText, {
|
||||||
|
OPENAI_API_KEY: "",
|
||||||
|
OPENAI_BASE_URL: "",
|
||||||
|
})
|
||||||
|
: draft.envText
|
||||||
|
try {
|
||||||
|
await Promise.all([
|
||||||
|
persistEnv(
|
||||||
|
"codex",
|
||||||
|
draft.enabled,
|
||||||
|
codexEnvText,
|
||||||
|
draft.modelProviderId
|
||||||
|
),
|
||||||
|
persistConfig("codex", draft.configText, {
|
||||||
|
codexAuthJsonText: authJson,
|
||||||
|
codexConfigTomlText: draft.codexConfigTomlText,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
} catch (err) {
|
||||||
|
const msg = err instanceof Error ? err.message : String(err)
|
||||||
|
toast.error(t("codex.loginSaveFailed"), {
|
||||||
|
description: msg,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (result.status === "error") {
|
||||||
|
setCodexLoginError(result.message ?? "Unknown error")
|
||||||
|
setCodexLoginStatus("error")
|
||||||
|
setCodexDeviceCode(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
timer = setTimeout(poll, pollInterval)
|
||||||
|
} catch {
|
||||||
|
if (!active || codexPollCancelledRef.current) return
|
||||||
|
timer = setTimeout(poll, pollInterval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
timer = setTimeout(poll, pollInterval)
|
||||||
|
return () => {
|
||||||
|
active = false
|
||||||
|
if (timer) clearTimeout(timer)
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
codexLoginStatus,
|
||||||
|
codexDeviceCode,
|
||||||
|
drafts.codex,
|
||||||
|
persistConfig,
|
||||||
|
persistEnv,
|
||||||
|
updateSelectedDraft,
|
||||||
|
t,
|
||||||
|
])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedAgent?.agent_type !== "codex" && codexLoginStatus !== "idle") {
|
||||||
|
cancelCodexDeviceLogin()
|
||||||
|
}
|
||||||
|
}, [selectedAgent, codexLoginStatus, cancelCodexDeviceLogin])
|
||||||
|
|
||||||
if (loadingAgents) {
|
if (loadingAgents) {
|
||||||
return (
|
return (
|
||||||
<div className="h-full flex items-center justify-center text-sm text-muted-foreground">
|
<div className="h-full flex items-center justify-center text-sm text-muted-foreground">
|
||||||
@@ -5303,6 +5431,108 @@ export function AcpAgentSettings() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{selectedDraft.codexAuthMode === "chatgpt_subscription" && (
|
||||||
|
<div className="space-y-2">
|
||||||
|
{hasCodexChatgptTokens(
|
||||||
|
selectedDraft.codexAuthJsonText
|
||||||
|
) &&
|
||||||
|
codexLoginStatus !== "polling" &&
|
||||||
|
codexLoginStatus !== "requesting" && (
|
||||||
|
<div className="flex items-center gap-1.5 text-xs text-green-600">
|
||||||
|
<CheckCircle2 className="h-3 w-3" />
|
||||||
|
{t("codex.loggedIn")}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{codexLoginStatus === "idle" && (
|
||||||
|
<Button
|
||||||
|
onClick={handleCodexDeviceLogin}
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
>
|
||||||
|
{hasCodexChatgptTokens(
|
||||||
|
selectedDraft.codexAuthJsonText
|
||||||
|
)
|
||||||
|
? t("codex.loginRelogin")
|
||||||
|
: t("codex.loginButton")}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{codexLoginStatus === "requesting" && (
|
||||||
|
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
||||||
|
<Loader2 className="h-3 w-3 animate-spin" />
|
||||||
|
{t("codex.loginRequesting")}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{codexLoginStatus === "polling" && codexDeviceCode && (
|
||||||
|
<div className="space-y-2 rounded-md border p-3">
|
||||||
|
<p className="text-xs">{t("codex.loginStep1")}</p>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="text-xs text-primary underline cursor-pointer"
|
||||||
|
onClick={() =>
|
||||||
|
openUrl(codexDeviceCode.verificationUrl)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{codexDeviceCode.verificationUrl}
|
||||||
|
</button>
|
||||||
|
<p className="text-xs mt-1">
|
||||||
|
{t("codex.loginStep2")}
|
||||||
|
</p>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<code className="rounded bg-muted px-2 py-1 text-sm font-mono font-bold tracking-widest">
|
||||||
|
{codexDeviceCode.userCode}
|
||||||
|
</code>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="ghost"
|
||||||
|
className="h-7 w-7 p-0"
|
||||||
|
onClick={() => {
|
||||||
|
navigator.clipboard.writeText(
|
||||||
|
codexDeviceCode.userCode
|
||||||
|
)
|
||||||
|
toast.success(t("codex.loginCodeCopied"))
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Copy className="h-3 w-3" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2 text-xs text-muted-foreground mt-1">
|
||||||
|
<Loader2 className="h-3 w-3 animate-spin" />
|
||||||
|
{t("codex.loginPolling")}
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
onClick={cancelCodexDeviceLogin}
|
||||||
|
>
|
||||||
|
{t("codex.loginCancel")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{codexLoginStatus === "success" && (
|
||||||
|
<div className="flex items-center gap-1.5 text-xs text-green-600">
|
||||||
|
<CheckCircle2 className="h-3 w-3" />
|
||||||
|
{t("codex.loginSuccess")}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{codexLoginStatus === "error" && (
|
||||||
|
<div className="space-y-1.5">
|
||||||
|
<p className="text-xs text-destructive">
|
||||||
|
{t("codex.loginFailed", {
|
||||||
|
message: codexLoginError ?? "Unknown error",
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
|
<Button
|
||||||
|
onClick={handleCodexDeviceLogin}
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
>
|
||||||
|
{t("codex.loginRetry")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{selectedDraft.codexAuthMode === "model_provider" && (
|
{selectedDraft.codexAuthMode === "model_provider" && (
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
<label className="text-[11px] text-muted-foreground">
|
<label className="text-[11px] text-muted-foreground">
|
||||||
@@ -5497,27 +5727,6 @@ export function AcpAgentSettings() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-1.5">
|
|
||||||
<label className="text-[11px] text-muted-foreground">
|
|
||||||
{t("codex.authJsonNative")}
|
|
||||||
</label>
|
|
||||||
<Textarea
|
|
||||||
value={selectedDraft.codexAuthJsonText}
|
|
||||||
onChange={(event) => {
|
|
||||||
handleCodexAuthJsonTextChange(event.target.value)
|
|
||||||
}}
|
|
||||||
placeholder={`{
|
|
||||||
"OPENAI_API_KEY": "sk-..."
|
|
||||||
}`}
|
|
||||||
className="min-h-28 max-h-60 font-mono text-xs"
|
|
||||||
/>
|
|
||||||
{selectedCodexAuthError && (
|
|
||||||
<div className="rounded-md border border-red-500/30 bg-red-500/5 px-2.5 py-1.5 text-[11px] text-red-400">
|
|
||||||
{selectedCodexAuthError}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
<label className="text-[11px] text-muted-foreground">
|
<label className="text-[11px] text-muted-foreground">
|
||||||
{t("codex.configTomlNative")}
|
{t("codex.configTomlNative")}
|
||||||
|
|||||||
@@ -557,7 +557,21 @@
|
|||||||
"enableFast": "تفعيل Fast",
|
"enableFast": "تفعيل Fast",
|
||||||
"enableFastAria": "تفعيل مستوى خدمة Fast لـ Codex",
|
"enableFastAria": "تفعيل مستوى خدمة Fast لـ Codex",
|
||||||
"authJsonNative": "auth.json (أصلي)",
|
"authJsonNative": "auth.json (أصلي)",
|
||||||
"configTomlNative": "config.toml (أصلي)"
|
"configTomlNative": "config.toml (أصلي)",
|
||||||
|
"loginButton": "تسجيل الدخول باستخدام ChatGPT",
|
||||||
|
"loginRequesting": "جارٍ طلب رمز تسجيل الدخول...",
|
||||||
|
"loginStep1": "افتح الرابط التالي في متصفحك:",
|
||||||
|
"loginStep2": "أدخل الرمز أدناه:",
|
||||||
|
"loginPolling": "في انتظار التفويض...",
|
||||||
|
"loginCancel": "إلغاء",
|
||||||
|
"loginSuccess": "تم تسجيل الدخول بنجاح، تم حفظ الإعدادات!",
|
||||||
|
"loginFailed": "فشل تسجيل الدخول: {message}",
|
||||||
|
"loginRetry": "إعادة المحاولة",
|
||||||
|
"loginCodeCopied": "تم نسخ الرمز",
|
||||||
|
"loggedIn": "تم تسجيل الدخول",
|
||||||
|
"loginRelogin": "إعادة تسجيل الدخول / تبديل الحساب",
|
||||||
|
"loginTimeout": "انتهت مهلة تسجيل الدخول، يرجى المحاولة مرة أخرى",
|
||||||
|
"loginSaveFailed": "تم تسجيل الدخول بنجاح ولكن فشل حفظ الإعدادات"
|
||||||
},
|
},
|
||||||
"gemini": {
|
"gemini": {
|
||||||
"authConfig": "إعداد مصادقة Gemini",
|
"authConfig": "إعداد مصادقة Gemini",
|
||||||
|
|||||||
@@ -557,7 +557,21 @@
|
|||||||
"enableFast": "Fast aktivieren",
|
"enableFast": "Fast aktivieren",
|
||||||
"enableFastAria": "Fast-Servicestufe für Codex aktivieren",
|
"enableFastAria": "Fast-Servicestufe für Codex aktivieren",
|
||||||
"authJsonNative": "auth.json (nativ)",
|
"authJsonNative": "auth.json (nativ)",
|
||||||
"configTomlNative": "config.toml (nativ)"
|
"configTomlNative": "config.toml (nativ)",
|
||||||
|
"loginButton": "Mit ChatGPT anmelden",
|
||||||
|
"loginRequesting": "Login-Code wird angefordert...",
|
||||||
|
"loginStep1": "Öffnen Sie die folgende URL in Ihrem Browser:",
|
||||||
|
"loginStep2": "Geben Sie den folgenden Code ein:",
|
||||||
|
"loginPolling": "Warte auf Autorisierung...",
|
||||||
|
"loginCancel": "Abbrechen",
|
||||||
|
"loginSuccess": "Erfolgreich angemeldet, Konfiguration gespeichert!",
|
||||||
|
"loginFailed": "Anmeldung fehlgeschlagen: {message}",
|
||||||
|
"loginRetry": "Erneut versuchen",
|
||||||
|
"loginCodeCopied": "Code kopiert",
|
||||||
|
"loggedIn": "Konto angemeldet",
|
||||||
|
"loginRelogin": "Erneut anmelden / Konto wechseln",
|
||||||
|
"loginTimeout": "Anmeldung abgelaufen, bitte erneut versuchen",
|
||||||
|
"loginSaveFailed": "Anmeldung erfolgreich, aber Konfiguration konnte nicht gespeichert werden"
|
||||||
},
|
},
|
||||||
"gemini": {
|
"gemini": {
|
||||||
"authConfig": "Gemini-Auth-Konfiguration",
|
"authConfig": "Gemini-Auth-Konfiguration",
|
||||||
|
|||||||
@@ -557,7 +557,21 @@
|
|||||||
"enableFast": "Enable Fast",
|
"enableFast": "Enable Fast",
|
||||||
"enableFastAria": "Enable Fast service tier for Codex",
|
"enableFastAria": "Enable Fast service tier for Codex",
|
||||||
"authJsonNative": "auth.json (native)",
|
"authJsonNative": "auth.json (native)",
|
||||||
"configTomlNative": "config.toml (native)"
|
"configTomlNative": "config.toml (native)",
|
||||||
|
"loginButton": "Log in with ChatGPT",
|
||||||
|
"loginRequesting": "Requesting login code...",
|
||||||
|
"loginStep1": "Open the following URL in your browser:",
|
||||||
|
"loginStep2": "Enter the code below:",
|
||||||
|
"loginPolling": "Waiting for authorization...",
|
||||||
|
"loginCancel": "Cancel",
|
||||||
|
"loginSuccess": "Logged in successfully, config saved!",
|
||||||
|
"loginFailed": "Login failed: {message}",
|
||||||
|
"loginRetry": "Retry",
|
||||||
|
"loginCodeCopied": "Code copied",
|
||||||
|
"loggedIn": "Account logged in",
|
||||||
|
"loginRelogin": "Re-login / Switch account",
|
||||||
|
"loginTimeout": "Login timed out, please try again",
|
||||||
|
"loginSaveFailed": "Login succeeded but failed to save config"
|
||||||
},
|
},
|
||||||
"gemini": {
|
"gemini": {
|
||||||
"authConfig": "Gemini Auth Config",
|
"authConfig": "Gemini Auth Config",
|
||||||
|
|||||||
@@ -557,7 +557,21 @@
|
|||||||
"enableFast": "Habilitar Fast",
|
"enableFast": "Habilitar Fast",
|
||||||
"enableFastAria": "Habilitar nivel de servicio Fast para Codex",
|
"enableFastAria": "Habilitar nivel de servicio Fast para Codex",
|
||||||
"authJsonNative": "auth.json (nativo)",
|
"authJsonNative": "auth.json (nativo)",
|
||||||
"configTomlNative": "config.toml (nativo)"
|
"configTomlNative": "config.toml (nativo)",
|
||||||
|
"loginButton": "Iniciar sesión con ChatGPT",
|
||||||
|
"loginRequesting": "Solicitando código de inicio de sesión...",
|
||||||
|
"loginStep1": "Abre la siguiente URL en tu navegador:",
|
||||||
|
"loginStep2": "Introduce el siguiente código:",
|
||||||
|
"loginPolling": "Esperando autorización...",
|
||||||
|
"loginCancel": "Cancelar",
|
||||||
|
"loginSuccess": "¡Inicio de sesión exitoso, configuración guardada!",
|
||||||
|
"loginFailed": "Error de inicio de sesión: {message}",
|
||||||
|
"loginRetry": "Reintentar",
|
||||||
|
"loginCodeCopied": "Código copiado",
|
||||||
|
"loggedIn": "Cuenta conectada",
|
||||||
|
"loginRelogin": "Reconectar / Cambiar cuenta",
|
||||||
|
"loginTimeout": "Tiempo de inicio de sesión agotado, inténtalo de nuevo",
|
||||||
|
"loginSaveFailed": "Inicio de sesión exitoso pero falló al guardar la configuración"
|
||||||
},
|
},
|
||||||
"gemini": {
|
"gemini": {
|
||||||
"authConfig": "Configuración de autenticación de Gemini",
|
"authConfig": "Configuración de autenticación de Gemini",
|
||||||
|
|||||||
@@ -557,7 +557,21 @@
|
|||||||
"enableFast": "Activer Fast",
|
"enableFast": "Activer Fast",
|
||||||
"enableFastAria": "Activer le niveau de service Fast pour Codex",
|
"enableFastAria": "Activer le niveau de service Fast pour Codex",
|
||||||
"authJsonNative": "auth.json (natif)",
|
"authJsonNative": "auth.json (natif)",
|
||||||
"configTomlNative": "config.toml (natif)"
|
"configTomlNative": "config.toml (natif)",
|
||||||
|
"loginButton": "Se connecter avec ChatGPT",
|
||||||
|
"loginRequesting": "Demande du code de connexion...",
|
||||||
|
"loginStep1": "Ouvrez l'URL suivante dans votre navigateur :",
|
||||||
|
"loginStep2": "Entrez le code ci-dessous :",
|
||||||
|
"loginPolling": "En attente d'autorisation...",
|
||||||
|
"loginCancel": "Annuler",
|
||||||
|
"loginSuccess": "Connexion réussie, configuration enregistrée !",
|
||||||
|
"loginFailed": "Échec de la connexion : {message}",
|
||||||
|
"loginRetry": "Réessayer",
|
||||||
|
"loginCodeCopied": "Code copié",
|
||||||
|
"loggedIn": "Compte connecté",
|
||||||
|
"loginRelogin": "Reconnecter / Changer de compte",
|
||||||
|
"loginTimeout": "Connexion expirée, veuillez réessayer",
|
||||||
|
"loginSaveFailed": "Connexion réussie mais échec de la sauvegarde de la configuration"
|
||||||
},
|
},
|
||||||
"gemini": {
|
"gemini": {
|
||||||
"authConfig": "Configuration d’authentification Gemini",
|
"authConfig": "Configuration d’authentification Gemini",
|
||||||
|
|||||||
@@ -557,7 +557,21 @@
|
|||||||
"enableFast": "Fast を有効化",
|
"enableFast": "Fast を有効化",
|
||||||
"enableFastAria": "Codex の Fast サービスティアを有効化",
|
"enableFastAria": "Codex の Fast サービスティアを有効化",
|
||||||
"authJsonNative": "auth.json(ネイティブ)",
|
"authJsonNative": "auth.json(ネイティブ)",
|
||||||
"configTomlNative": "config.toml(ネイティブ)"
|
"configTomlNative": "config.toml(ネイティブ)",
|
||||||
|
"loginButton": "ChatGPT でログイン",
|
||||||
|
"loginRequesting": "ログインコードを取得中...",
|
||||||
|
"loginStep1": "ブラウザで以下の URL を開いてください:",
|
||||||
|
"loginStep2": "以下のコードを入力してください:",
|
||||||
|
"loginPolling": "認証を待っています...",
|
||||||
|
"loginCancel": "キャンセル",
|
||||||
|
"loginSuccess": "ログイン成功、設定を保存しました!",
|
||||||
|
"loginFailed": "ログインに失敗しました:{message}",
|
||||||
|
"loginRetry": "再試行",
|
||||||
|
"loginCodeCopied": "コードをコピーしました",
|
||||||
|
"loggedIn": "アカウントにログイン済み",
|
||||||
|
"loginRelogin": "再ログイン / アカウント切替",
|
||||||
|
"loginTimeout": "ログインがタイムアウトしました。再試行してください",
|
||||||
|
"loginSaveFailed": "ログインは成功しましたが、設定の保存に失敗しました"
|
||||||
},
|
},
|
||||||
"gemini": {
|
"gemini": {
|
||||||
"authConfig": "Gemini 認証設定",
|
"authConfig": "Gemini 認証設定",
|
||||||
|
|||||||
@@ -557,7 +557,21 @@
|
|||||||
"enableFast": "Fast 활성화",
|
"enableFast": "Fast 활성화",
|
||||||
"enableFastAria": "Codex용 Fast 서비스 계층 활성화",
|
"enableFastAria": "Codex용 Fast 서비스 계층 활성화",
|
||||||
"authJsonNative": "auth.json (네이티브)",
|
"authJsonNative": "auth.json (네이티브)",
|
||||||
"configTomlNative": "config.toml (네이티브)"
|
"configTomlNative": "config.toml (네이티브)",
|
||||||
|
"loginButton": "ChatGPT로 로그인",
|
||||||
|
"loginRequesting": "로그인 코드 요청 중...",
|
||||||
|
"loginStep1": "브라우저에서 다음 URL을 열어주세요:",
|
||||||
|
"loginStep2": "아래 코드를 입력하세요:",
|
||||||
|
"loginPolling": "인증 대기 중...",
|
||||||
|
"loginCancel": "취소",
|
||||||
|
"loginSuccess": "로그인 성공, 설정이 저장되었습니다!",
|
||||||
|
"loginFailed": "로그인 실패: {message}",
|
||||||
|
"loginRetry": "재시도",
|
||||||
|
"loginCodeCopied": "코드가 복사되었습니다",
|
||||||
|
"loggedIn": "계정 로그인됨",
|
||||||
|
"loginRelogin": "재로그인 / 계정 전환",
|
||||||
|
"loginTimeout": "로그인 시간 초과, 다시 시도해 주세요",
|
||||||
|
"loginSaveFailed": "로그인 성공했지만 설정 저장 실패"
|
||||||
},
|
},
|
||||||
"gemini": {
|
"gemini": {
|
||||||
"authConfig": "Gemini 인증 설정",
|
"authConfig": "Gemini 인증 설정",
|
||||||
|
|||||||
@@ -557,7 +557,21 @@
|
|||||||
"enableFast": "Habilitar Fast",
|
"enableFast": "Habilitar Fast",
|
||||||
"enableFastAria": "Habilitar nível de serviço Fast para Codex",
|
"enableFastAria": "Habilitar nível de serviço Fast para Codex",
|
||||||
"authJsonNative": "auth.json (nativo)",
|
"authJsonNative": "auth.json (nativo)",
|
||||||
"configTomlNative": "config.toml (nativo)"
|
"configTomlNative": "config.toml (nativo)",
|
||||||
|
"loginButton": "Entrar com ChatGPT",
|
||||||
|
"loginRequesting": "Solicitando código de login...",
|
||||||
|
"loginStep1": "Abra a seguinte URL no seu navegador:",
|
||||||
|
"loginStep2": "Digite o código abaixo:",
|
||||||
|
"loginPolling": "Aguardando autorização...",
|
||||||
|
"loginCancel": "Cancelar",
|
||||||
|
"loginSuccess": "Login realizado com sucesso, configuração salva!",
|
||||||
|
"loginFailed": "Falha no login: {message}",
|
||||||
|
"loginRetry": "Tentar novamente",
|
||||||
|
"loginCodeCopied": "Código copiado",
|
||||||
|
"loggedIn": "Conta conectada",
|
||||||
|
"loginRelogin": "Reconectar / Trocar conta",
|
||||||
|
"loginTimeout": "Login expirou, tente novamente",
|
||||||
|
"loginSaveFailed": "Login realizado com sucesso, mas falha ao salvar configuração"
|
||||||
},
|
},
|
||||||
"gemini": {
|
"gemini": {
|
||||||
"authConfig": "Configuração de autenticação do Gemini",
|
"authConfig": "Configuração de autenticação do Gemini",
|
||||||
|
|||||||
@@ -557,7 +557,21 @@
|
|||||||
"enableFast": "启用 Fast",
|
"enableFast": "启用 Fast",
|
||||||
"enableFastAria": "Codex 启用 Fast 服务等级",
|
"enableFastAria": "Codex 启用 Fast 服务等级",
|
||||||
"authJsonNative": "auth.json(原生)",
|
"authJsonNative": "auth.json(原生)",
|
||||||
"configTomlNative": "config.toml(原生)"
|
"configTomlNative": "config.toml(原生)",
|
||||||
|
"loginButton": "使用 ChatGPT 登录",
|
||||||
|
"loginRequesting": "正在请求登录码...",
|
||||||
|
"loginStep1": "在浏览器中打开以下链接:",
|
||||||
|
"loginStep2": "输入以下代码:",
|
||||||
|
"loginPolling": "等待授权中...",
|
||||||
|
"loginCancel": "取消",
|
||||||
|
"loginSuccess": "登录成功,配置已保存!",
|
||||||
|
"loginFailed": "登录失败:{message}",
|
||||||
|
"loginRetry": "重试",
|
||||||
|
"loginCodeCopied": "已复制代码",
|
||||||
|
"loggedIn": "账号已登录",
|
||||||
|
"loginRelogin": "重新登录 / 切换账号",
|
||||||
|
"loginTimeout": "登录超时,请重试",
|
||||||
|
"loginSaveFailed": "登录成功但配置保存失败"
|
||||||
},
|
},
|
||||||
"gemini": {
|
"gemini": {
|
||||||
"authConfig": "Gemini 认证配置",
|
"authConfig": "Gemini 认证配置",
|
||||||
|
|||||||
@@ -557,7 +557,21 @@
|
|||||||
"enableFast": "啟用 Fast",
|
"enableFast": "啟用 Fast",
|
||||||
"enableFastAria": "Codex 啟用 Fast 服務等級",
|
"enableFastAria": "Codex 啟用 Fast 服務等級",
|
||||||
"authJsonNative": "auth.json(原生)",
|
"authJsonNative": "auth.json(原生)",
|
||||||
"configTomlNative": "config.toml(原生)"
|
"configTomlNative": "config.toml(原生)",
|
||||||
|
"loginButton": "使用 ChatGPT 登入",
|
||||||
|
"loginRequesting": "正在請求登入碼...",
|
||||||
|
"loginStep1": "在瀏覽器中打開以下連結:",
|
||||||
|
"loginStep2": "輸入以下代碼:",
|
||||||
|
"loginPolling": "等待授權中...",
|
||||||
|
"loginCancel": "取消",
|
||||||
|
"loginSuccess": "登入成功,配置已儲存!",
|
||||||
|
"loginFailed": "登入失敗:{message}",
|
||||||
|
"loginRetry": "重試",
|
||||||
|
"loginCodeCopied": "已複製代碼",
|
||||||
|
"loggedIn": "帳號已登入",
|
||||||
|
"loginRelogin": "重新登入 / 切換帳號",
|
||||||
|
"loginTimeout": "登入逾時,請重試",
|
||||||
|
"loginSaveFailed": "登入成功但配置儲存失敗"
|
||||||
},
|
},
|
||||||
"gemini": {
|
"gemini": {
|
||||||
"authConfig": "Gemini 認證配置",
|
"authConfig": "Gemini 認證配置",
|
||||||
|
|||||||
@@ -277,6 +277,32 @@ export async function acpReorderAgents(agentTypes: AgentType[]): Promise<void> {
|
|||||||
return getTransport().call("acp_reorder_agents", { agentTypes })
|
return getTransport().call("acp_reorder_agents", { agentTypes })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function codexRequestDeviceCode(): Promise<{
|
||||||
|
userCode: string
|
||||||
|
verificationUrl: string
|
||||||
|
deviceAuthId: string
|
||||||
|
interval: number
|
||||||
|
}> {
|
||||||
|
return getTransport().call("codex_request_device_code", {})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function codexPollDeviceCode(params: {
|
||||||
|
deviceAuthId: string
|
||||||
|
userCode: string
|
||||||
|
}): Promise<{
|
||||||
|
status: "pending" | "success" | "error"
|
||||||
|
message?: string
|
||||||
|
idToken?: string
|
||||||
|
accessToken?: string
|
||||||
|
refreshToken?: string
|
||||||
|
accountId?: string
|
||||||
|
}> {
|
||||||
|
return getTransport().call("codex_poll_device_code", {
|
||||||
|
deviceAuthId: params.deviceAuthId,
|
||||||
|
userCode: params.userCode,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export async function acpPreflight(
|
export async function acpPreflight(
|
||||||
agentType: AgentType,
|
agentType: AgentType,
|
||||||
forceRefresh?: boolean
|
forceRefresh?: boolean
|
||||||
|
|||||||
Reference in New Issue
Block a user