Merge branch 'feat/back-to-front' into feat/front-end
This commit is contained in:
@ -8,7 +8,7 @@ import * as vscode from "vscode";
|
|||||||
type Environment = "dev" | "test" | "prod";
|
type Environment = "dev" | "test" | "prod";
|
||||||
|
|
||||||
/** 当前环境 - 修改这里切换环境 */
|
/** 当前环境 - 修改这里切换环境 */
|
||||||
const CURRENT_ENV: Environment = "test";
|
const CURRENT_ENV: Environment = "dev";
|
||||||
|
|
||||||
/** 服务等级类型 */
|
/** 服务等级类型 */
|
||||||
export type ServiceTier = "lite" | "syntaxic" | "max" | "auto";
|
export type ServiceTier = "lite" | "syntaxic" | "max" | "auto";
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import {
|
|||||||
handleReplaceInFile,
|
handleReplaceInFile,
|
||||||
handleUserAnswer,
|
handleUserAnswer,
|
||||||
abortCurrentDialog,
|
abortCurrentDialog,
|
||||||
|
handleOptimizePrompt,
|
||||||
handlePlanAction,
|
handlePlanAction,
|
||||||
getCurrentTaskId,
|
getCurrentTaskId,
|
||||||
setLastTaskId,
|
setLastTaskId,
|
||||||
@ -328,6 +329,17 @@ export async function showICHelperPanel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "optimizePrompt":
|
||||||
|
if (typeof message.prompt === "string") {
|
||||||
|
void handleOptimizePrompt(panel, message.prompt);
|
||||||
|
} else {
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "optimizeResult",
|
||||||
|
success: false,
|
||||||
|
error: "提示词为空或格式错误",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
// 处理计划操作(只做模式切换,响应已通过 submitAnswer 发送)
|
// 处理计划操作(只做模式切换,响应已通过 submitAnswer 发送)
|
||||||
case "planAction":
|
case "planAction":
|
||||||
if (message.action === "confirm") {
|
if (message.action === "confirm") {
|
||||||
|
|||||||
@ -100,7 +100,18 @@ export async function fetchBalance(): Promise<number | null> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = session.accessToken;
|
return await fetchBalanceWithToken(session.accessToken);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[CreditsService] 查询余额异常:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用指定 token 查询余额(登录过程中使用)
|
||||||
|
*/
|
||||||
|
export async function fetchBalanceWithToken(token: string): Promise<number | null> {
|
||||||
|
try {
|
||||||
console.log('[CreditsService] 开始查询余额,token 长度:', token.length);
|
console.log('[CreditsService] 开始查询余额,token 长度:', token.length);
|
||||||
|
|
||||||
// 直接调用 StrangeLoop 的 /api/credit/balance 接口
|
// 直接调用 StrangeLoop 的 /api/credit/balance 接口
|
||||||
|
|||||||
@ -24,8 +24,23 @@ export class ICCoderAuthenticationProvider
|
|||||||
private _sessions: vscode.AuthenticationSession[] = [];
|
private _sessions: vscode.AuthenticationSession[] = [];
|
||||||
|
|
||||||
constructor(private readonly context: vscode.ExtensionContext) {
|
constructor(private readonly context: vscode.ExtensionContext) {
|
||||||
// 从存储中恢复会话
|
// 从存储中恢复会话(同步执行)
|
||||||
this.loadSessions();
|
this.loadSessionsSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从存储中加载会话(同步版本)
|
||||||
|
*/
|
||||||
|
private loadSessionsSync(): void {
|
||||||
|
const storedSessions = this.context.globalState.get<
|
||||||
|
vscode.AuthenticationSession[]
|
||||||
|
>("icCoderSessions", []);
|
||||||
|
this._sessions = storedSessions;
|
||||||
|
console.log("[AuthProvider] 同步加载 sessions, 数量:", this._sessions.length);
|
||||||
|
if (this._sessions.length > 0) {
|
||||||
|
console.log("[AuthProvider] Session ID:", this._sessions[0].id);
|
||||||
|
console.log("[AuthProvider] Account:", this._sessions[0].account.label);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,7 +57,9 @@ export class ICCoderAuthenticationProvider
|
|||||||
* 保存会话到存储
|
* 保存会话到存储
|
||||||
*/
|
*/
|
||||||
private async saveSessions(): Promise<void> {
|
private async saveSessions(): Promise<void> {
|
||||||
|
console.log("[AuthProvider] 保存 sessions, 数量:", this._sessions.length);
|
||||||
await this.context.globalState.update("icCoderSessions", this._sessions);
|
await this.context.globalState.update("icCoderSessions", this._sessions);
|
||||||
|
console.log("[AuthProvider] sessions 已保存到 globalState");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,6 +68,7 @@ export class ICCoderAuthenticationProvider
|
|||||||
async getSessions(
|
async getSessions(
|
||||||
scopes?: readonly string[]
|
scopes?: readonly string[]
|
||||||
): Promise<vscode.AuthenticationSession[]> {
|
): Promise<vscode.AuthenticationSession[]> {
|
||||||
|
console.log("[AuthProvider] getSessions 被调用, 当前 sessions 数量:", this._sessions.length);
|
||||||
return [...this._sessions];
|
return [...this._sessions];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
103
src/services/promptOptimizeService.ts
Normal file
103
src/services/promptOptimizeService.ts
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
/**
|
||||||
|
* 提示词优化服务
|
||||||
|
* 调用后端 API 优化用户输入的提示词
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as vscode from 'vscode';
|
||||||
|
import * as https from 'https';
|
||||||
|
import * as http from 'http';
|
||||||
|
import { URL } from 'url';
|
||||||
|
import { getApiUrl } from '../config/settings';
|
||||||
|
|
||||||
|
/** 优化响应类型 */
|
||||||
|
interface OptimizeResponse {
|
||||||
|
success: boolean;
|
||||||
|
optimizedPrompt?: string;
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 优化提示词
|
||||||
|
* @param prompt 原始提示词
|
||||||
|
* @returns 优化后的提示词
|
||||||
|
*/
|
||||||
|
export async function optimizePrompt(prompt: string): Promise<string> {
|
||||||
|
// 获取 JWT token
|
||||||
|
const session = await vscode.authentication.getSession('iccoder', [], { silent: true });
|
||||||
|
if (!session?.accessToken) {
|
||||||
|
throw new Error('未登录,请先登录');
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await callOptimizeApi(prompt, session.accessToken);
|
||||||
|
|
||||||
|
if (response.success && response.optimizedPrompt) {
|
||||||
|
return response.optimizedPrompt;
|
||||||
|
} else {
|
||||||
|
throw new Error(response.error || '优化失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调用后端优化 API
|
||||||
|
*/
|
||||||
|
async function callOptimizeApi(prompt: string, token: string): Promise<OptimizeResponse> {
|
||||||
|
const urlStr = getApiUrl('/api/prompt/optimize');
|
||||||
|
const url = new URL(urlStr);
|
||||||
|
|
||||||
|
const isHttps = url.protocol === 'https:';
|
||||||
|
const httpModule = isHttps ? https : http;
|
||||||
|
|
||||||
|
const body = JSON.stringify({ prompt });
|
||||||
|
|
||||||
|
const requestOptions: http.RequestOptions = {
|
||||||
|
hostname: url.hostname,
|
||||||
|
port: url.port || (isHttps ? 443 : 80),
|
||||||
|
path: url.pathname + url.search,
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Content-Length': Buffer.byteLength(body),
|
||||||
|
'Authorization': `Bearer ${token}`
|
||||||
|
},
|
||||||
|
timeout: 30000
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const req = httpModule.request(requestOptions, (res) => {
|
||||||
|
let data = '';
|
||||||
|
|
||||||
|
res.on('data', (chunk) => {
|
||||||
|
data += chunk;
|
||||||
|
});
|
||||||
|
|
||||||
|
res.on('end', () => {
|
||||||
|
console.log('[PromptOptimize] 响应状态码:', res.statusCode);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const json = JSON.parse(data);
|
||||||
|
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
||||||
|
resolve(json as OptimizeResponse);
|
||||||
|
} else if (res.statusCode === 401 || res.statusCode === 403) {
|
||||||
|
resolve({ success: false, error: '登录已过期,请重新登录' });
|
||||||
|
} else {
|
||||||
|
resolve({ success: false, error: json.error || json.message || `HTTP ${res.statusCode}` });
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
resolve({ success: false, error: `解析响应失败: ${data}` });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
req.on('error', (error) => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
req.on('timeout', () => {
|
||||||
|
req.destroy();
|
||||||
|
reject(new Error('请求超时'));
|
||||||
|
});
|
||||||
|
|
||||||
|
req.write(body);
|
||||||
|
req.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -8,7 +8,7 @@ import { URL } from 'url';
|
|||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { getStrangeLoopApiUrl, getConfig } from '../config/settings';
|
import { getStrangeLoopApiUrl, getConfig } from '../config/settings';
|
||||||
import type { UserInfoResponse, MembershipResponse, MultiMembershipVO, MembershipItemVO } from '../types/api';
|
import type { UserInfoResponse, MembershipResponse, MultiMembershipVO, MembershipItemVO } from '../types/api';
|
||||||
import { fetchBalance, getCachedBalance } from './creditsService';
|
import { fetchBalanceWithToken, getCachedBalance } from './creditsService';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTTP 请求选项
|
* HTTP 请求选项
|
||||||
@ -230,7 +230,7 @@ export async function onTokenReceived(token: string): Promise<UserInfo | null> {
|
|||||||
const [userInfo, membershipInfo, credits] = await Promise.all([
|
const [userInfo, membershipInfo, credits] = await Promise.all([
|
||||||
getUserInfo(token),
|
getUserInfo(token),
|
||||||
getMembershipInfo(token),
|
getMembershipInfo(token),
|
||||||
fetchBalance()
|
fetchBalanceWithToken(token)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (!userInfo) {
|
if (!userInfo) {
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import {
|
|||||||
checkBalanceBeforeSend,
|
checkBalanceBeforeSend,
|
||||||
fetchBalance,
|
fetchBalance,
|
||||||
} from "../services/creditsService";
|
} from "../services/creditsService";
|
||||||
|
import { optimizePrompt } from "../services/promptOptimizeService";
|
||||||
|
|
||||||
import type { RunMode, ServiceTier } from "../types/api";
|
import type { RunMode, ServiceTier } from "../types/api";
|
||||||
|
|
||||||
@ -1031,3 +1032,35 @@ async function handleVCDGeneration(
|
|||||||
vscode.window.showErrorMessage(errorMsg);
|
vscode.window.showErrorMessage(errorMsg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理提示词优化请求
|
||||||
|
*/
|
||||||
|
export async function handleOptimizePrompt(
|
||||||
|
panel: vscode.WebviewPanel,
|
||||||
|
prompt: string
|
||||||
|
): Promise<void> {
|
||||||
|
console.log("[MessageHandler] ========== 收到提示词优化请求 ==========");
|
||||||
|
console.log("[MessageHandler] prompt:", prompt);
|
||||||
|
console.log("[MessageHandler] prompt 长度:", prompt?.length);
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log("[MessageHandler] 开始调用 optimizePrompt...");
|
||||||
|
const optimized = await optimizePrompt(prompt);
|
||||||
|
console.log("[MessageHandler] 优化成功,结果:", optimized);
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "optimizeResult",
|
||||||
|
success: true,
|
||||||
|
optimizedPrompt: optimized,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
const errorMsg = error instanceof Error ? error.message : "优化失败";
|
||||||
|
console.error("[MessageHandler] 提示词优化失败:", errorMsg);
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "optimizeResult",
|
||||||
|
success: false,
|
||||||
|
error: errorMsg,
|
||||||
|
});
|
||||||
|
vscode.window.showErrorMessage(`提示词优化失败: ${errorMsg}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import {
|
|||||||
handleReplaceInFile,
|
handleReplaceInFile,
|
||||||
handleUserAnswer,
|
handleUserAnswer,
|
||||||
abortCurrentDialog,
|
abortCurrentDialog,
|
||||||
|
handleOptimizePrompt,
|
||||||
} from "../utils/messageHandler";
|
} from "../utils/messageHandler";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -70,6 +71,9 @@ export function showICHelperPanel(context: vscode.ExtensionContext) {
|
|||||||
// 处理消息
|
// 处理消息
|
||||||
panel.webview.onDidReceiveMessage(
|
panel.webview.onDidReceiveMessage(
|
||||||
(message) => {
|
(message) => {
|
||||||
|
console.log("[ICViewProvider] ====== 收到 WebView 消息 ======");
|
||||||
|
console.log("[ICViewProvider] command:", message.command);
|
||||||
|
console.log("[ICViewProvider] 完整消息:", JSON.stringify(message));
|
||||||
switch (message.command) {
|
switch (message.command) {
|
||||||
case "sendMessage":
|
case "sendMessage":
|
||||||
handleUserMessage(panel, message.text, context.extensionPath, message.mode);
|
handleUserMessage(panel, message.text, context.extensionPath, message.mode);
|
||||||
@ -117,6 +121,10 @@ export function showICHelperPanel(context: vscode.ExtensionContext) {
|
|||||||
case "abortDialog":
|
case "abortDialog":
|
||||||
void abortCurrentDialog();
|
void abortCurrentDialog();
|
||||||
break;
|
break;
|
||||||
|
// 新增:优化提示词
|
||||||
|
case "optimizePrompt":
|
||||||
|
handleOptimizePrompt(panel, message.prompt);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
|
|||||||
@ -60,35 +60,97 @@ export function getOptimizeButtonScript(): string {
|
|||||||
return `
|
return `
|
||||||
let isOptimized = false; // 标记是否已优化
|
let isOptimized = false; // 标记是否已优化
|
||||||
let originalText = ''; // 保存原始文本用于撤回
|
let originalText = ''; // 保存原始文本用于撤回
|
||||||
|
let isOptimizing = false; // 标记是否正在优化中
|
||||||
|
|
||||||
function handleOptimize() {
|
function handleOptimize() {
|
||||||
|
console.log('[Optimize] handleOptimize 被调用');
|
||||||
|
console.log('[Optimize] isOptimizing:', isOptimizing);
|
||||||
|
console.log('[Optimize] isOptimized:', isOptimized);
|
||||||
|
console.log('[Optimize] messageInput:', messageInput);
|
||||||
|
|
||||||
|
if (isOptimizing) {
|
||||||
|
console.log('[Optimize] 正在优化中,忽略点击');
|
||||||
|
return; // 正在优化中,忽略点击
|
||||||
|
}
|
||||||
|
|
||||||
if (isOptimized) {
|
if (isOptimized) {
|
||||||
// 撤回操作
|
// 撤回操作
|
||||||
|
console.log('[Optimize] 执行撤回操作');
|
||||||
messageInput.value = originalText;
|
messageInput.value = originalText;
|
||||||
resetOptimizeButton();
|
resetOptimizeButton();
|
||||||
} else {
|
} else {
|
||||||
// 优化操作
|
// 优化操作
|
||||||
|
const currentText = messageInput.value.trim();
|
||||||
|
console.log('[Optimize] 当前输入内容:', currentText);
|
||||||
|
console.log('[Optimize] 内容长度:', currentText.length);
|
||||||
|
|
||||||
|
if (!currentText) {
|
||||||
|
console.log('[Optimize] 输入框为空,不执行优化');
|
||||||
|
return; // 输入框为空,不执行优化
|
||||||
|
}
|
||||||
|
|
||||||
originalText = messageInput.value; // 保存原始文本
|
originalText = messageInput.value; // 保存原始文本
|
||||||
|
isOptimizing = true;
|
||||||
|
console.log('[Optimize] 开始优化,显示加载状态');
|
||||||
|
|
||||||
// 使用死数据替换输入框内容
|
// 显示加载状态
|
||||||
const optimizedTexts = [
|
showOptimizeLoading();
|
||||||
'请帮我优化这段代码,提高性能和可读性',
|
|
||||||
'请分析这个问题并给出最佳解决方案',
|
|
||||||
'请帮我重构这段代码,使其更加简洁高效',
|
|
||||||
'请检查代码中的潜在问题并提供改进建议'
|
|
||||||
];
|
|
||||||
const randomText = optimizedTexts[Math.floor(Math.random() * optimizedTexts.length)];
|
|
||||||
messageInput.value = randomText;
|
|
||||||
|
|
||||||
// 切换到撤回状态
|
// 发送优化请求到扩展
|
||||||
isOptimized = true;
|
console.log('[Optimize] 发送 optimizePrompt 消息');
|
||||||
updateOptimizeButton();
|
vscode.postMessage({
|
||||||
|
command: 'optimizePrompt',
|
||||||
|
prompt: currentText
|
||||||
|
});
|
||||||
|
console.log('[Optimize] postMessage 已发送');
|
||||||
}
|
}
|
||||||
|
|
||||||
messageInput.focus();
|
messageInput.focus();
|
||||||
autoResizeTextarea();
|
autoResizeTextarea();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理优化结果
|
||||||
|
function handleOptimizeResult(success, optimizedPrompt, error) {
|
||||||
|
isOptimizing = false;
|
||||||
|
hideOptimizeLoading();
|
||||||
|
|
||||||
|
if (success && optimizedPrompt) {
|
||||||
|
messageInput.value = optimizedPrompt;
|
||||||
|
isOptimized = true;
|
||||||
|
updateOptimizeButton();
|
||||||
|
} else {
|
||||||
|
// 优化失败,恢复原始文本
|
||||||
|
messageInput.value = originalText;
|
||||||
|
console.error('优化失败:', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
messageInput.focus();
|
||||||
|
autoResizeTextarea();
|
||||||
|
}
|
||||||
|
|
||||||
|
function showOptimizeLoading() {
|
||||||
|
const optimizeButton = document.getElementById('optimizeButton');
|
||||||
|
const optimizeIcon = document.getElementById('optimizeIcon');
|
||||||
|
if (optimizeButton && optimizeIcon) {
|
||||||
|
optimizeButton.disabled = true;
|
||||||
|
optimizeButton.style.opacity = '0.5';
|
||||||
|
// 显示加载动画
|
||||||
|
optimizeIcon.innerHTML = '<circle cx="512" cy="512" r="400" fill="none" stroke="#409eff" stroke-width="60" stroke-dasharray="1200" stroke-dashoffset="0"><animateTransform attributeName="transform" type="rotate" from="0 512 512" to="360 512 512" dur="1s" repeatCount="indefinite"/></circle>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideOptimizeLoading() {
|
||||||
|
const optimizeButton = document.getElementById('optimizeButton');
|
||||||
|
if (optimizeButton) {
|
||||||
|
optimizeButton.disabled = false;
|
||||||
|
optimizeButton.style.opacity = '1';
|
||||||
|
}
|
||||||
|
// 恢复图标会在 updateOptimizeButton 或 resetOptimizeButton 中处理
|
||||||
|
if (!isOptimized) {
|
||||||
|
resetOptimizeButton();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function updateOptimizeButton() {
|
function updateOptimizeButton() {
|
||||||
const optimizeIcon = document.getElementById('optimizeIcon');
|
const optimizeIcon = document.getElementById('optimizeIcon');
|
||||||
const optimizeTooltip = document.getElementById('optimizeTooltip');
|
const optimizeTooltip = document.getElementById('optimizeTooltip');
|
||||||
|
|||||||
@ -428,6 +428,7 @@ export function getWebviewContent(
|
|||||||
<script>
|
<script>
|
||||||
console.log('[WebView] 脚本开始执行');
|
console.log('[WebView] 脚本开始执行');
|
||||||
const vscode = acquireVsCodeApi();
|
const vscode = acquireVsCodeApi();
|
||||||
|
window.vscode = vscode; // 确保全局可访问
|
||||||
console.log('[WebView] vscode API 已获取');
|
console.log('[WebView] vscode API 已获取');
|
||||||
const messageInput = document.getElementById('messageInput');
|
const messageInput = document.getElementById('messageInput');
|
||||||
const modeSelect = document.getElementById('modeSelect');
|
const modeSelect = document.getElementById('modeSelect');
|
||||||
@ -742,6 +743,13 @@ export function getWebviewContent(
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'optimizeResult':
|
||||||
|
// 处理提示词优化结果
|
||||||
|
if (typeof handleOptimizeResult === 'function') {
|
||||||
|
handleOptimizeResult(message.success, message.optimizedPrompt, message.error);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
console.log('[WebView] 未处理的消息类型:', message.command);
|
console.log('[WebView] 未处理的消息类型:', message.command);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user