Skip to content

Commit 1455c45

Browse files
SonAIengineclaude
andcommitted
fix: CLI 윈도우 인증 토큰 전달 — 프론트 쿠키 → URL param → Rust session
문제: CLI 별도 윈도우에서 메인 윈도우 쿠키 접근 불가 → XGEN API 401 해결: - 사이드바 AI CLI 버튼: document.cookie에서 access_token 추출 - open_cli_window: token을 AppState에 저장 + URL query param으로 전달 - cli.html: URL param에서 token 읽기 - cli_send_message/cli_list_providers: session에 저장된 token fallback Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent bf5ce71 commit 1455c45

4 files changed

Lines changed: 43 additions & 19 deletions

File tree

scripts/patch-sidebar-cli.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,13 @@ content = content.replace(
5353
const openCliWindow = async () => {
5454
try {
5555
const { invoke } = await import('@tauri-apps/api/core');
56-
await invoke('open_cli_window');
56+
// Pass auth token from cookie to CLI window
57+
const getCookie = (name) => {
58+
const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
59+
return match ? match[2] : null;
60+
};
61+
const token = getCookie('access_token') || undefined;
62+
await invoke('open_cli_window', { xgenToken: token });
5763
} catch (e) {
5864
console.error('Failed to open CLI window:', e);
5965
}

src-cli/cli.html

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -202,18 +202,10 @@
202202
let streamingText = '';
203203
let currentStreamEl = null;
204204

205-
// Get auth token from main window via Tauri
206-
// We'll pass it from the cookie or localStorage
205+
// Get auth token from URL query param (passed by open_cli_window)
207206
function getToken() {
208-
try {
209-
// Try to get from cookie-like storage
210-
const cookies = document.cookie.split(';').reduce((acc, c) => {
211-
const [k, v] = c.trim().split('=');
212-
acc[k] = v;
213-
return acc;
214-
}, {});
215-
return cookies['access_token'] || null;
216-
} catch { return null; }
207+
const params = new URLSearchParams(window.location.search);
208+
return params.get('token') || null;
217209
}
218210

219211
// Load providers

src-tauri/src/commands/cli.rs

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,36 @@ use crate::services::{LlmClient, XgenApiClient};
1414
use crate::services::llm_client::ChatMessage;
1515
use crate::state::AppState;
1616

17-
/// Open AI CLI in a separate window
17+
/// Open AI CLI in a separate window with auth token
1818
#[tauri::command]
19-
pub async fn open_cli_window(app: AppHandle) -> Result<()> {
19+
pub async fn open_cli_window(
20+
app: AppHandle,
21+
state: tauri::State<'_, Arc<AppState>>,
22+
xgen_token: Option<String>,
23+
) -> Result<()> {
24+
// Store token in CLI session for later use
25+
if let Some(token) = &xgen_token {
26+
let mut session = state.cli_session.write().await;
27+
session.xgen_token = Some(token.clone());
28+
}
29+
2030
// If window already exists, focus it
2131
if let Some(window) = app.get_webview_window("cli") {
2232
let _ = window.set_focus();
2333
return Ok(());
2434
}
2535

26-
// Create new CLI window
36+
// Pass token as query param so cli.html can use it
37+
let url = if let Some(token) = &xgen_token {
38+
format!("cli.html?token={}", token)
39+
} else {
40+
"cli.html".to_string()
41+
};
42+
2743
let _window = WebviewWindowBuilder::new(
2844
&app,
2945
"cli",
30-
tauri::WebviewUrl::App("cli.html".into()),
46+
tauri::WebviewUrl::App(url.into()),
3147
)
3248
.title("XGEN AI CLI")
3349
.inner_size(700.0, 500.0)
@@ -72,8 +88,12 @@ pub async fn cli_send_message(
7288
let base_url = state.get_server_url().await
7389
.unwrap_or_else(|| "https://xgen.x2bee.com".to_string());
7490

75-
// Use token from frontend auth (passed from cookie)
76-
let xgen_api = XgenApiClient::new(base_url, xgen_token);
91+
// Use token: prefer passed token, fallback to session stored token
92+
let token = xgen_token.or_else(|| {
93+
let session = state.cli_session.try_read().ok();
94+
session.and_then(|s| s.xgen_token.clone())
95+
});
96+
let xgen_api = XgenApiClient::new(base_url, token);
7797

7898
// Create LLM client from XGEN backend config with optional provider/model
7999
let llm = LlmClient::from_xgen(
@@ -158,7 +178,11 @@ pub async fn cli_list_providers(
158178
) -> Result<Value> {
159179
let base_url = state.get_server_url().await
160180
.unwrap_or_else(|| "https://xgen.x2bee.com".to_string());
161-
let xgen_api = XgenApiClient::new(base_url, xgen_token);
181+
let token = xgen_token.or_else(|| {
182+
let session = state.cli_session.try_read().ok();
183+
session.and_then(|s| s.xgen_token.clone())
184+
});
185+
let xgen_api = XgenApiClient::new(base_url, token);
162186
let providers = xgen_api.list_available_providers().await?;
163187
Ok(serde_json::to_value(providers).unwrap_or_default())
164188
}

src-tauri/src/state/app_state.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,15 @@ pub struct AppState {
3434
pub struct CliSession {
3535
pub session_id: String,
3636
pub messages: Vec<ChatMessage>,
37+
pub xgen_token: Option<String>,
3738
}
3839

3940
impl CliSession {
4041
pub fn new() -> Self {
4142
Self {
4243
session_id: uuid::Uuid::new_v4().to_string(),
4344
messages: Vec::new(),
45+
xgen_token: None,
4446
}
4547
}
4648

0 commit comments

Comments
 (0)