Compare commits
4 Commits
ae703091d4
...
7444bb1140
| Author | SHA1 | Date | |
|---|---|---|---|
| 7444bb1140 | |||
| 6ef7e976cc | |||
| 31419e93a1 | |||
| 173132399e |
77
README.md
77
README.md
@ -1,40 +1,83 @@
|
||||
## 什么是 IC Coder
|
||||
## 什么是IC Coder
|
||||
|
||||
**IC Coder** 是一款 **专注于真实 FPGA 研发的 Verilog 智能体编程平台**。我们立志于用 AI 重塑 FPGA 研发效率,让 FPGA 开发者们,都能享受到 AI 发展所带来的科技福利!目标成为全球最好用的 **LLM 生成 Verilog**的平台!
|
||||
IC Coder是一款**The Agentic AI Verilog Coding Platform(自主式人工智能 Verilog 编码平台)**。我们立志于用AI重塑芯片开发者的效率,将芯片设计与验证的效率提升至少20倍!让芯片开发者们,都能享受到AI发展所带来的科技福利!目标成为全球最好用的"LLM生成Verilog"的平台!
|
||||
|
||||
从 WEB 端到插件端,IC Coder 智能体架构完成了**全新升级**,采用当前主流的**层级架构**设计,这种高内聚、低耦合的架构特性,不仅支持更多功能扩展,更预留了充足的迭代空间。当前,插件端拥有了调用本地工具的能力,不再是单纯代码生成的智能体,升级为拥有**语法校验、波形逻辑检查**等工具的**全流程 Verilog 编程智能体平台**,给用户带来更沉浸的**Vibe Verilog Coding**体验。
|
||||

|
||||
|
||||
## 输入需求 对话补充需求
|
||||
### 核心技术架构
|
||||
|
||||
**无需**输入完整需求,放心交给智能体补充完善。
|
||||
**我们采用全球顶尖的大语言模型**,加上自研的针对芯片设计领域深度优化的微调模型,为代码生成提供强大的AI能力支撑。
|
||||
|
||||
## Plan 模型下确认设计文档
|
||||
**核心技术栈**包括:
|
||||
|
||||
**确定**好用户需求以及相关参数后,整理并输出一份 FPGA 开发**设计文档**。Plan 模式下用户可以**进一步**与 IC Coder 沟通需求,或**直接修改**设计文档。
|
||||
- **多智能体架构(Multi-Agent System)**:多个专业化AI智能体协同工作,分别负责架构设计、代码生成、验证测试等不同环节
|
||||
- **增强上下文引擎**:智能理解和管理大规模设计上下文,确保生成代码的一致性和准确性
|
||||
- **自研EDA工具集**:完整的仿真、综合、时序分析工具链,无缝集成到AI工作流中
|
||||
|
||||
这些技术共同支撑着从需求分析、架构设计、代码生成到验证调试的全流程智能化开发体验。
|
||||
|
||||

|
||||
|
||||
## 自动搭建电路架构
|
||||
|
||||
根据需求自动搭建电路架构,并将电路信号关系结构化
|
||||
IC Coder能够根据自然语言描述的设计需求,自动生成完整的电路架构。系统会:
|
||||
|
||||
## 自动仿真
|
||||
- **智能解析需求**:理解功能规格、性能指标、接口要求等设计约束
|
||||
- **自动模块划分**:根据功能将设计合理拆分为多个子模块,确保模块化和可复用性
|
||||
- **生成层次结构**:建立清晰的模块层次关系,自动处理模块间的信号连接
|
||||
- **结构化信号管理**:将所有电路信号关系进行结构化表示,包括数据流向、控制逻辑、时序关系等
|
||||
- **可视化展示**:以图形化方式展示整体架构,便于理解和审查设计方案
|
||||
|
||||
自主搭建 Testbench 仿真平台,自动运行仿真生成波形
|
||||

|
||||
|
||||
## 实时跟随
|
||||
## AI自主仿真
|
||||
|
||||
实时展示全流程执行细节,与智能体协同随时反馈,让 AI 开发更清晰、高效
|
||||
IC Coder提供完全自动化的仿真验证流程,无需手动编写测试代码:
|
||||
|
||||
## VCD 波形解析
|
||||
- **智能Testbench生成**:根据设计模块自动生成完整的测试平台,包括激励生成、时钟复位、接口驱动等
|
||||
- **测试用例自动化**:根据设计规格自动生成覆盖各种场景的测试用例,包括正常功能、边界条件、异常情况等
|
||||
- **一键运行仿真**:自动调用集成仿真器执行仿真
|
||||
- **波形自动生成**:仿真完成后自动生成VCD、波形文件,便于后续分析
|
||||
- **实时进度反馈**:仿真过程中实时显示执行状态和日志信息
|
||||
|
||||
自动解析 VCD 波形文件,自动根据需求,检查是否存在逻辑错误
|
||||

|
||||
|
||||
## 自主代码迭代
|
||||
## AI自主代码迭代
|
||||
|
||||
根据波形解析结果,自动对代码进行优化,然后重新仿真并解析波形,如此迭代,直到仿真无误
|
||||
IC Coder实现了真正的自主式开发循环,能够持续优化代码直到满足设计要求:
|
||||
|
||||
- **智能问题诊断**:根据波形分析结果,自动定位代码中的问题根源
|
||||
- **自动代码修复**:针对发现的问题自动生成修复方案并更新代码
|
||||
- **迭代验证循环**:修复后自动重新运行仿真和波形分析,验证问题是否解决
|
||||
- **持续优化**:如果仍存在问题,继续分析和修复,形成闭环迭代
|
||||
- **收敛保证**:智能判断迭代进展,避免无效循环,确保最终收敛到正确设计
|
||||
- **全程可追溯**:记录每次迭代的修改内容和验证结果,便于回溯和审查
|
||||
|
||||
这种自主迭代能力大幅减少了人工调试时间,让设计验证过程更加高效可靠。
|
||||
|
||||
## 随时可掌控
|
||||
|
||||
IC Coder提供透明化的开发过程,让用户始终掌握AI的工作状态:
|
||||
|
||||
- **实时流程展示**:可视化展示当前执行到哪个阶段(需求分析、架构设计、代码生成、仿真验证等)
|
||||
- **详细执行日志**:记录每一步操作的详细信息,包括AI的思考过程、决策依据、执行结果
|
||||
- **人机协同交互**:在关键决策点支持用户介入,可随时提供反馈、调整方向或修改参数
|
||||
- **进度实时追踪**:显示任务完成进度、预计剩余步骤,让开发过程更加可预期
|
||||
- **智能建议系统**:AI主动提供优化建议和替代方案,用户可选择采纳或自定义
|
||||
- **即时响应机制**:支持随时暂停、恢复或调整AI的工作流程
|
||||
|
||||
这种透明可控的设计理念,让AI开发不再是"黑盒",而是真正的智能协作伙伴。
|
||||
|
||||
## 多层次安全保障
|
||||
|
||||
默认本地存储与云端即时加密保障隐私,真正做到了代码全链路加密传输、云端零存储
|
||||
IC Coder将数据安全和隐私保护作为核心设计原则,提供企业级的安全保障:
|
||||
|
||||
- **本地优先存储**:所有设计文件默认存储在本地,用户完全掌控自己的代码资产
|
||||
- **全链路加密传输**:与云端通信采用TLS/SSL加密,确保数据传输过程中不被窃取或篡改
|
||||
- **云端零存储策略**:云端服务器不保存用户的源代码,仅处理加密后的临时数据,处理完成后立即销毁
|
||||
- **定制化部署选项**:支持企业私有云或本地部署,满足高安全等级需求
|
||||
|
||||
真正做到了代码全链路加密传输、云端零存储,让芯片设计企业可以放心使用AI工具。
|
||||
|
||||
## 反馈
|
||||
|
||||
|
||||
@ -2,18 +2,28 @@
|
||||
* API 客户端
|
||||
* 封装与后端的 HTTP 通信
|
||||
*/
|
||||
import * as vscode from 'vscode';
|
||||
import * as https from 'https';
|
||||
import * as http from 'http';
|
||||
import { URL } from 'url';
|
||||
import { getApiUrl, getConfig } from '../config/settings';
|
||||
import type { ToolCallResult, AnswerRequest, ToolResultResponse, AnswerResponse, ToolConfirmResponse, UserInfoResponse, InvitationVerifyRequest, InvitationVerifyResponse, InvitationStatusResponse } from '../types/api';
|
||||
import * as vscode from "vscode";
|
||||
import * as https from "https";
|
||||
import * as http from "http";
|
||||
import { URL } from "url";
|
||||
import { getApiUrl, getConfig } from "../config/settings";
|
||||
import type {
|
||||
ToolCallResult,
|
||||
AnswerRequest,
|
||||
ToolResultResponse,
|
||||
AnswerResponse,
|
||||
ToolConfirmResponse,
|
||||
UserInfoResponse,
|
||||
InvitationVerifyRequest,
|
||||
InvitationVerifyResponse,
|
||||
InvitationStatusResponse,
|
||||
} from "../types/api";
|
||||
|
||||
/**
|
||||
* HTTP 请求选项
|
||||
*/
|
||||
interface RequestOptions {
|
||||
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
||||
method: "GET" | "POST" | "PUT" | "DELETE";
|
||||
headers?: Record<string, string>;
|
||||
body?: unknown;
|
||||
timeout?: number;
|
||||
@ -24,7 +34,9 @@ interface RequestOptions {
|
||||
*/
|
||||
async function getAuthToken(): Promise<string | undefined> {
|
||||
try {
|
||||
const session = await vscode.authentication.getSession('iccoder', [], { silent: true });
|
||||
const session = await vscode.authentication.getSession("iccoder", [], {
|
||||
silent: true,
|
||||
});
|
||||
return session?.accessToken;
|
||||
} catch {
|
||||
return undefined;
|
||||
@ -41,7 +53,7 @@ async function request<T>(path: string, options: RequestOptions): Promise<T> {
|
||||
// 自动获取 Token
|
||||
const token = await getAuthToken();
|
||||
|
||||
const isHttps = url.protocol === 'https:';
|
||||
const isHttps = url.protocol === "https:";
|
||||
const httpModule = isHttps ? https : http;
|
||||
|
||||
const requestOptions: http.RequestOptions = {
|
||||
@ -50,77 +62,84 @@ async function request<T>(path: string, options: RequestOptions): Promise<T> {
|
||||
path: url.pathname + url.search,
|
||||
method: options.method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(token ? { 'Authorization': `Bearer ${token}` } : {}),
|
||||
...options.headers
|
||||
"Content-Type": "application/json",
|
||||
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
||||
...options.headers,
|
||||
},
|
||||
timeout: options.timeout || timeout
|
||||
timeout: options.timeout || timeout,
|
||||
};
|
||||
|
||||
console.log('[HTTP] 请求详情:', {
|
||||
console.log("[HTTP] 请求详情:", {
|
||||
url: url.toString(),
|
||||
method: options.method,
|
||||
headers: requestOptions.headers,
|
||||
hasToken: !!token,
|
||||
body: options.body
|
||||
body: options.body,
|
||||
});
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const req = httpModule.request(requestOptions, (res) => {
|
||||
let data = '';
|
||||
let data = "";
|
||||
|
||||
console.log('[HTTP] 响应状态码:', res.statusCode);
|
||||
console.log('[HTTP] 响应头:', res.headers);
|
||||
// console.log('[HTTP] 响应状态码:', res.statusCode);
|
||||
// console.log('[HTTP] 响应头:', res.headers);
|
||||
|
||||
res.on('data', (chunk) => {
|
||||
res.on("data", (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
res.on('end', () => {
|
||||
console.log('[HTTP] 响应体:', data);
|
||||
res.on("end", () => {
|
||||
console.log("[HTTP] 响应体:", data);
|
||||
try {
|
||||
const json = JSON.parse(data);
|
||||
console.log('[HTTP] 解析后的响应:', JSON.stringify(json, null, 2));
|
||||
// console.log('[HTTP] 解析后的响应:', JSON.stringify(json, null, 2));
|
||||
|
||||
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
||||
console.log('[HTTP] 请求成功');
|
||||
console.log("[HTTP] 请求成功");
|
||||
resolve(json as T);
|
||||
} else {
|
||||
console.error('[HTTP] 请求失败:', {
|
||||
console.error("[HTTP] 请求失败:", {
|
||||
statusCode: res.statusCode,
|
||||
error: json.error,
|
||||
message: json.message,
|
||||
msg: json.msg
|
||||
msg: json.msg,
|
||||
});
|
||||
reject(new Error(json.error || json.message || json.msg || `HTTP ${res.statusCode}`));
|
||||
reject(
|
||||
new Error(
|
||||
json.error ||
|
||||
json.message ||
|
||||
json.msg ||
|
||||
`HTTP ${res.statusCode}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[HTTP] 解析响应失败:', e);
|
||||
console.error('[HTTP] 原始响应:', data);
|
||||
// console.error('[HTTP] 解析响应失败:', e);
|
||||
// console.error('[HTTP] 原始响应:', data);
|
||||
reject(new Error(`解析响应失败: ${data}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', (error) => {
|
||||
console.error('[HTTP] 请求错误:', error);
|
||||
req.on("error", (error) => {
|
||||
// console.error('[HTTP] 请求错误:', error);
|
||||
reject(error);
|
||||
});
|
||||
|
||||
req.on('timeout', () => {
|
||||
console.error('[HTTP] 请求超时');
|
||||
req.on("timeout", () => {
|
||||
// console.error('[HTTP] 请求超时');
|
||||
req.destroy();
|
||||
reject(new Error('请求超时'));
|
||||
reject(new Error("请求超时"));
|
||||
});
|
||||
|
||||
if (options.body) {
|
||||
const bodyStr = JSON.stringify(options.body);
|
||||
console.log('[HTTP] 发送请求体:', bodyStr);
|
||||
// console.log('[HTTP] 发送请求体:', bodyStr);
|
||||
req.write(bodyStr);
|
||||
}
|
||||
|
||||
req.end();
|
||||
console.log('[HTTP] 请求已发送');
|
||||
// console.log('[HTTP] 请求已发送');
|
||||
});
|
||||
}
|
||||
|
||||
@ -128,11 +147,13 @@ async function request<T>(path: string, options: RequestOptions): Promise<T> {
|
||||
* 提交工具执行结果
|
||||
* POST /api/tool/result
|
||||
*/
|
||||
export async function submitToolResult(result: ToolCallResult): Promise<ToolResultResponse> {
|
||||
export async function submitToolResult(
|
||||
result: ToolCallResult,
|
||||
): Promise<ToolResultResponse> {
|
||||
console.log(`[API] 提交工具结果: callId=${result.id}`);
|
||||
return request<ToolResultResponse>('/api/tool/result', {
|
||||
method: 'POST',
|
||||
body: result
|
||||
return request<ToolResultResponse>("/api/tool/result", {
|
||||
method: "POST",
|
||||
body: result,
|
||||
});
|
||||
}
|
||||
|
||||
@ -140,11 +161,13 @@ export async function submitToolResult(result: ToolCallResult): Promise<ToolResu
|
||||
* 提交用户回答
|
||||
* POST /api/task/answer
|
||||
*/
|
||||
export async function submitAnswer(answer: AnswerRequest): Promise<AnswerResponse> {
|
||||
export async function submitAnswer(
|
||||
answer: AnswerRequest,
|
||||
): Promise<AnswerResponse> {
|
||||
console.log(`[API] 提交用户回答: askId=${answer.askId}`);
|
||||
return request<AnswerResponse>('/api/task/answer', {
|
||||
method: 'POST',
|
||||
body: answer
|
||||
return request<AnswerResponse>("/api/task/answer", {
|
||||
method: "POST",
|
||||
body: answer,
|
||||
});
|
||||
}
|
||||
|
||||
@ -152,11 +175,15 @@ export async function submitAnswer(answer: AnswerRequest): Promise<AnswerRespons
|
||||
* 提交工具确认响应(Ask 模式)
|
||||
* POST /api/tool/confirm
|
||||
*/
|
||||
export async function submitToolConfirm(response: ToolConfirmResponse): Promise<ToolResultResponse> {
|
||||
console.log(`[API] 提交工具确认: confirmId=${response.confirmId}, approved=${response.approved}`);
|
||||
return request<ToolResultResponse>('/api/tool/confirm', {
|
||||
method: 'POST',
|
||||
body: response
|
||||
export async function submitToolConfirm(
|
||||
response: ToolConfirmResponse,
|
||||
): Promise<ToolResultResponse> {
|
||||
console.log(
|
||||
`[API] 提交工具确认: confirmId=${response.confirmId}, approved=${response.approved}`,
|
||||
);
|
||||
return request<ToolResultResponse>("/api/tool/confirm", {
|
||||
method: "POST",
|
||||
body: response,
|
||||
});
|
||||
}
|
||||
|
||||
@ -165,9 +192,9 @@ export async function submitToolConfirm(response: ToolConfirmResponse): Promise<
|
||||
* GET /api/dialog/health
|
||||
*/
|
||||
export async function healthCheck(): Promise<{ status: string }> {
|
||||
return request<{ status: string }>('/api/dialog/health', {
|
||||
method: 'GET',
|
||||
timeout: 5000
|
||||
return request<{ status: string }>("/api/dialog/health", {
|
||||
method: "GET",
|
||||
timeout: 5000,
|
||||
});
|
||||
}
|
||||
|
||||
@ -194,9 +221,9 @@ export interface StopDialogResponse {
|
||||
*/
|
||||
export async function stopDialog(taskId: string): Promise<StopDialogResponse> {
|
||||
console.log(`[API] 停止对话: taskId=${taskId}`);
|
||||
return request<StopDialogResponse>('/api/dialog/stop', {
|
||||
method: 'POST',
|
||||
body: { taskId }
|
||||
return request<StopDialogResponse>("/api/dialog/stop", {
|
||||
method: "POST",
|
||||
body: { taskId },
|
||||
});
|
||||
}
|
||||
|
||||
@ -212,11 +239,13 @@ export interface CompactDialogResponse {
|
||||
* 手动压缩对话历史
|
||||
* POST /api/dialog/compact
|
||||
*/
|
||||
export async function compactDialog(taskId: string): Promise<CompactDialogResponse> {
|
||||
export async function compactDialog(
|
||||
taskId: string,
|
||||
): Promise<CompactDialogResponse> {
|
||||
console.log(`[API] 压缩对话: taskId=${taskId}`);
|
||||
return request<CompactDialogResponse>('/api/dialog/compact', {
|
||||
method: 'POST',
|
||||
body: { taskId }
|
||||
return request<CompactDialogResponse>("/api/dialog/compact", {
|
||||
method: "POST",
|
||||
body: { taskId },
|
||||
});
|
||||
}
|
||||
|
||||
@ -225,37 +254,44 @@ export async function compactDialog(taskId: string): Promise<CompactDialogRespon
|
||||
*/
|
||||
export function createSuccessResult(id: number, text: string): ToolCallResult {
|
||||
return {
|
||||
jsonrpc: '2.0',
|
||||
jsonrpc: "2.0",
|
||||
id,
|
||||
result: {
|
||||
content: [{ type: 'text', text }],
|
||||
isError: false
|
||||
}
|
||||
content: [{ type: "text", text }],
|
||||
isError: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建业务错误的工具结果(如编译失败)
|
||||
*/
|
||||
export function createBusinessErrorResult(id: number, errorMessage: string): ToolCallResult {
|
||||
export function createBusinessErrorResult(
|
||||
id: number,
|
||||
errorMessage: string,
|
||||
): ToolCallResult {
|
||||
return {
|
||||
jsonrpc: '2.0',
|
||||
jsonrpc: "2.0",
|
||||
id,
|
||||
result: {
|
||||
content: [{ type: 'text', text: errorMessage }],
|
||||
isError: true
|
||||
}
|
||||
content: [{ type: "text", text: errorMessage }],
|
||||
isError: true,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建系统错误的工具结果
|
||||
*/
|
||||
export function createSystemErrorResult(id: number, code: number, message: string): ToolCallResult {
|
||||
export function createSystemErrorResult(
|
||||
id: number,
|
||||
code: number,
|
||||
message: string,
|
||||
): ToolCallResult {
|
||||
return {
|
||||
jsonrpc: '2.0',
|
||||
jsonrpc: "2.0",
|
||||
id,
|
||||
error: { code, message }
|
||||
error: { code, message },
|
||||
};
|
||||
}
|
||||
|
||||
@ -264,9 +300,9 @@ export function createSystemErrorResult(id: number, code: number, message: strin
|
||||
* GET /system/user/getInfo
|
||||
*/
|
||||
export async function getUserInfo(): Promise<UserInfoResponse> {
|
||||
console.log('[API] 获取用户信息');
|
||||
return request<UserInfoResponse>('/system/user/getInfo', {
|
||||
method: 'GET'
|
||||
console.log("[API] 获取用户信息");
|
||||
return request<UserInfoResponse>("/system/user/getInfo", {
|
||||
method: "GET",
|
||||
});
|
||||
}
|
||||
|
||||
@ -281,33 +317,43 @@ export interface CreditBalanceResponse {
|
||||
* 查询用户资源点余额
|
||||
* GET /api/dialog/balance?userId=xxx
|
||||
*/
|
||||
export async function getCreditBalance(userId: string): Promise<CreditBalanceResponse> {
|
||||
console.log('[API] 查询余额: userId=', userId);
|
||||
return request<CreditBalanceResponse>(`/api/dialog/balance?userId=${userId}`, {
|
||||
method: 'GET',
|
||||
timeout: 5000
|
||||
});
|
||||
export async function getCreditBalance(
|
||||
userId: string,
|
||||
): Promise<CreditBalanceResponse> {
|
||||
console.log("[API] 查询余额: userId=", userId);
|
||||
return request<CreditBalanceResponse>(
|
||||
`/api/dialog/balance?userId=${userId}`,
|
||||
{
|
||||
method: "GET",
|
||||
timeout: 5000,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证邀请码
|
||||
* POST /api/invitation/verify
|
||||
*/
|
||||
export async function verifyInvitationCode(code: string): Promise<InvitationVerifyResponse> {
|
||||
console.log('[API] 验证邀请码 - 开始');
|
||||
console.log('[API] 邀请码:', code);
|
||||
export async function verifyInvitationCode(
|
||||
code: string,
|
||||
): Promise<InvitationVerifyResponse> {
|
||||
// console.log('[API] 验证邀请码 - 开始');
|
||||
console.log("[API] 邀请码:", code);
|
||||
const body: InvitationVerifyRequest = { code };
|
||||
console.log('[API] 请求体:', JSON.stringify(body));
|
||||
console.log("[API] 请求体:", JSON.stringify(body));
|
||||
|
||||
try {
|
||||
const response = await request<InvitationVerifyResponse>('/api/invitation/verify', {
|
||||
method: 'POST',
|
||||
body
|
||||
});
|
||||
console.log('[API] 验证邀请码 - 响应:', JSON.stringify(response));
|
||||
const response = await request<InvitationVerifyResponse>(
|
||||
"/api/invitation/verify",
|
||||
{
|
||||
method: "POST",
|
||||
body,
|
||||
},
|
||||
);
|
||||
console.log("[API] 验证邀请码 - 响应:", JSON.stringify(response));
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error('[API] 验证邀请码 - 错误:', error);
|
||||
console.error("[API] 验证邀请码 - 错误:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@ -317,8 +363,33 @@ export async function verifyInvitationCode(code: string): Promise<InvitationVeri
|
||||
* GET /api/invitation/status
|
||||
*/
|
||||
export async function checkInvitationStatus(): Promise<InvitationStatusResponse> {
|
||||
console.log('[API] 查询邀请码验证状态');
|
||||
return request<InvitationStatusResponse>('/api/invitation/status', {
|
||||
method: 'GET'
|
||||
console.log("[API] 查询邀请码验证状态");
|
||||
return request<InvitationStatusResponse>("/api/invitation/status", {
|
||||
method: "GET",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置邀请码验证状态(退出登录时调用)
|
||||
* POST /api/invitation/reset
|
||||
*/
|
||||
export async function resetInvitationVerification(): Promise<{
|
||||
code: number;
|
||||
msg: string;
|
||||
}> {
|
||||
console.log("[API] 重置邀请码验证状态");
|
||||
try {
|
||||
const response = await request<{ code: number; msg: string }>(
|
||||
"/api/invitation/reset",
|
||||
{
|
||||
method: "POST",
|
||||
},
|
||||
);
|
||||
console.log("[API] 重置邀请码验证状态 - 响应:", JSON.stringify(response));
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.warn("[API] 重置邀请码验证状态 - 错误:", error);
|
||||
// 即使失败也不影响退出登录流程
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import * as path from "path";
|
||||
import * as fs from "fs";
|
||||
import { onTokenReceived, type UserInfo, clearUserInfo } from "./userService";
|
||||
import { getConfig } from "../config/settings";
|
||||
import { resetInvitationVerification } from "./apiClient";
|
||||
|
||||
/**
|
||||
* IC Coder Authentication Provider
|
||||
@ -142,13 +143,24 @@ export class ICCoderAuthenticationProvider
|
||||
const sessionIndex = this._sessions.findIndex((s) => s.id === sessionId);
|
||||
if (sessionIndex > -1) {
|
||||
const session = this._sessions[sessionIndex];
|
||||
|
||||
// 1. 先调用后端重置邀请码验证状态
|
||||
try {
|
||||
await resetInvitationVerification();
|
||||
console.log("[AuthProvider] 邀请码验证状态已重置");
|
||||
} catch (error) {
|
||||
console.warn("[AuthProvider] 重置邀请码验证状态失败,但继续退出流程:", error);
|
||||
// 即使失败也继续退出流程
|
||||
}
|
||||
|
||||
// 2. 清除本地 session
|
||||
this._sessions.splice(sessionIndex, 1);
|
||||
await this.saveSessions();
|
||||
|
||||
// 清除用户信息缓存
|
||||
// 3. 清除用户信息缓存
|
||||
await clearUserInfo();
|
||||
|
||||
// 触发会话变化事件
|
||||
// 4. 触发会话变化事件
|
||||
this._onDidChangeSessions.fire({
|
||||
added: [],
|
||||
removed: [session],
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
/**
|
||||
* 邀请码验证服务
|
||||
*/
|
||||
import * as vscode from 'vscode';
|
||||
import { verifyInvitationCode, checkInvitationStatus } from './apiClient';
|
||||
import * as vscode from "vscode";
|
||||
import { verifyInvitationCode, checkInvitationStatus } from "./apiClient";
|
||||
|
||||
/**
|
||||
* 邀请码验证服务类
|
||||
@ -13,47 +13,51 @@ export class InvitationService {
|
||||
*/
|
||||
static async isVerified(context: vscode.ExtensionContext): Promise<boolean> {
|
||||
// 【临时】使用本地验证,不调用后端
|
||||
const localVerified = context.globalState.get<boolean>('invitationCodeVerified');
|
||||
const localVerified = context.globalState.get<boolean>(
|
||||
"invitationCodeVerified",
|
||||
);
|
||||
return localVerified || false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证邀请码
|
||||
*/
|
||||
static async verifyCode(code: string): Promise<{ success: boolean; message: string }> {
|
||||
static async verifyCode(
|
||||
code: string,
|
||||
): Promise<{ success: boolean; message: string }> {
|
||||
try {
|
||||
console.log('[InvitationService] ========== 开始验证邀请码 ==========');
|
||||
console.log('[InvitationService] 邀请码:', code);
|
||||
console.log('[InvitationService] 邀请码长度:', code.length);
|
||||
// console.log('[InvitationService] ========== 开始验证邀请码 ==========');
|
||||
// console.log('[InvitationService] 邀请码:', code);
|
||||
// console.log('[InvitationService] 邀请码长度:', code.length);
|
||||
|
||||
const response = await verifyInvitationCode(code);
|
||||
|
||||
console.log('[InvitationService] 收到响应:', JSON.stringify(response, null, 2));
|
||||
console.log('[InvitationService] 响应代码:', response.code);
|
||||
console.log('[InvitationService] 响应消息:', response.msg);
|
||||
console.log('[InvitationService] 验证结果:', response.data?.verified);
|
||||
// console.log('[InvitationService] 收到响应:', JSON.stringify(response, null, 2));
|
||||
// console.log('[InvitationService] 响应代码:', response.code);
|
||||
// console.log('[InvitationService] 响应消息:', response.msg);
|
||||
// console.log('[InvitationService] 验证结果:', response.data?.verified);
|
||||
|
||||
if (response.code === 200 && response.data?.verified) {
|
||||
console.log('[InvitationService] ✓ 验证成功');
|
||||
console.log("[InvitationService] ✓ 验证成功");
|
||||
return {
|
||||
success: true,
|
||||
message: response.msg || '验证成功'
|
||||
message: response.msg || "验证成功",
|
||||
};
|
||||
} else {
|
||||
console.log('[InvitationService] ✗ 验证失败');
|
||||
console.log("[InvitationService] ✗ 验证失败");
|
||||
return {
|
||||
success: false,
|
||||
message: response.msg || '验证失败'
|
||||
message: response.msg || "验证失败",
|
||||
};
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('[InvitationService] ========== 验证邀请码异常 ==========');
|
||||
console.error('[InvitationService] 错误类型:', error.constructor.name);
|
||||
console.error('[InvitationService] 错误消息:', error.message);
|
||||
console.error('[InvitationService] 错误堆栈:', error.stack);
|
||||
// console.error('[InvitationService] ========== 验证邀请码异常 ==========');
|
||||
// console.error('[InvitationService] 错误类型:', error.constructor.name);
|
||||
// console.error('[InvitationService] 错误消息:', error.message);
|
||||
// console.error('[InvitationService] 错误堆栈:', error.stack);
|
||||
return {
|
||||
success: false,
|
||||
message: error.message || '网络连接失败,请检查网络后重试'
|
||||
message: error.message || "网络连接失败,请检查网络后重试",
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -64,20 +68,25 @@ export class InvitationService {
|
||||
static async saveVerificationStatus(
|
||||
context: vscode.ExtensionContext,
|
||||
code: string,
|
||||
verifiedTime?: string
|
||||
verifiedTime?: string,
|
||||
): Promise<void> {
|
||||
await context.globalState.update('invitationCodeVerified', true);
|
||||
await context.globalState.update('invitationCode', code);
|
||||
await context.globalState.update('invitationVerifiedTime', verifiedTime || new Date().toISOString());
|
||||
await context.globalState.update("invitationCodeVerified", true);
|
||||
await context.globalState.update("invitationCode", code);
|
||||
await context.globalState.update(
|
||||
"invitationVerifiedTime",
|
||||
verifiedTime || new Date().toISOString(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除验证状态(用于退出登录或更换邀请码)
|
||||
*/
|
||||
static async clearVerificationStatus(context: vscode.ExtensionContext): Promise<void> {
|
||||
await context.globalState.update('invitationCodeVerified', undefined);
|
||||
await context.globalState.update('invitationCode', undefined);
|
||||
await context.globalState.update('invitationVerifiedTime', undefined);
|
||||
static async clearVerificationStatus(
|
||||
context: vscode.ExtensionContext,
|
||||
): Promise<void> {
|
||||
await context.globalState.update("invitationCodeVerified", undefined);
|
||||
await context.globalState.update("invitationCode", undefined);
|
||||
await context.globalState.update("invitationVerifiedTime", undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -85,18 +94,18 @@ export class InvitationService {
|
||||
*/
|
||||
static async showInputDialog(): Promise<string | undefined> {
|
||||
const code = await vscode.window.showInputBox({
|
||||
prompt: '请输入邀请码以继续使用 IC Coder',
|
||||
placeHolder: '例如:INVITE2024ABC',
|
||||
prompt: "请输入邀请码以继续使用 IC Coder",
|
||||
placeHolder: "例如:INVITE2024ABC",
|
||||
ignoreFocusOut: true,
|
||||
validateInput: (value) => {
|
||||
if (!value || value.trim().length === 0) {
|
||||
return '邀请码不能为空';
|
||||
return "邀请码不能为空";
|
||||
}
|
||||
if (value.trim().length < 6) {
|
||||
return '邀请码格式不正确';
|
||||
return "邀请码格式不正确";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return code?.trim();
|
||||
|
||||
Reference in New Issue
Block a user