English | 中文
English
Unified AI API configuration and credential management for VS Code extensions.
Features
- Centralized Configuration: Manage all AI providers, API keys, and models in one place
- Secure Storage: API keys are stored using VS Code's SecretStorage API
- Multi-Provider Support: Configure multiple AI providers (OpenAI, Claude, Gemini, etc.)
- Load Balancing: Automatically distribute requests across multiple API keys for the same model
- Graphical UI: Easy-to-use Webview interface for managing configurations
- Cross-Extension API: Simple API for other extensions to retrieve credentials
- Config Sync: Export/import configurations to sync with other extensions
- Access Control: Whitelist-based access control for sensitive API methods
- Connectivity Testing: Test model API connectivity directly from the configuration UI with latency display
- Credential Naming: Custom names for each API credential (e.g., "Production Key", "Test Key") for easier management
- Auto-Save: Configuration changes are automatically saved with 500ms debounce
- Status Bar Icon: Optional gear icon in the status bar for quick access to configuration
- Bilingual UI: Webview interface supports Chinese/English language switching
Installation
- Install the extension from VS Code marketplace
- Open Command Palette (
Ctrl+Shift+P/Cmd+Shift+P) - Run
AI Core: Open Configuration
Extension Settings
| Setting | Type | Default | Description |
|---|---|---|---|
aiCore.allowedExtensions |
string[] |
["23J1633.ghost-annotations", "23J1633.bcos", "23J1633.hilbert-go"] |
Whitelist of extension IDs allowed to access sensitive API methods |
aiCore.showStatusBarIcon |
boolean |
true |
Show/hide the AI Core gear icon in the status bar |
Configuration Structure
Provider (e.g., OpenAI)
├── Base URL (e.g., https://api.openai.com/v1)
├── API Format (OpenAI / Claude / Gemini)
└── Credentials[]
├── Name (optional, e.g., "Production Key")
├── API Key
└── Models[]
├── Model ID (e.g., gpt-4)
└── Display Name (e.g., GPT-4)
API Reference for Extension Developers
Getting the API
import * as vscode from 'vscode';
const coreApi = vscode.extensions.getExtension('23J1633-AI-core')?.exports;
Access Control
Sensitive API methods (getCredential, exportConfig, importConfig) require the caller to pass its extension ID as the first parameter. The ID is checked against the aiCore.allowedExtensions whitelist in VS Code settings.
Setup for your extension:
- Add your extension ID to the whitelist in VS Code settings (
settings.json):
{
"aiCore.allowedExtensions": [
"23J1633.ghost-extensions",
"23J1633.bcos"
]
}
- Pass your extension ID when calling sensitive methods:
const MY_ID = '23J1633.my-extension';
const cred = await coreApi.getCredential(MY_ID, 'OpenAI', 'gpt-4');
Non-sensitive methods (openConfigUI, getModelList, onConfigChange) do not require callerId.
Interface Definition
type ApiFormat = 'OpenAI' | 'Claude' | 'Gemini';
type ImportMode = 'merge' | 'replace';
interface AiModelConfig {
id: string;
name: string;
}
interface ApiCredential {
id: string;
name?: string; // Optional display name for the credential
apiKey: string;
models: AiModelConfig[];
}
interface AiProvider {
id: string;
name: string;
baseUrl: string;
format: ApiFormat;
credentials: ApiCredential[];
}
type RootConfig = AiProvider[];
interface CredentialResult {
apiKey: string; // The API key
baseUrl: string; // The provider's base URL
format: ApiFormat; // 'OpenAI' | 'Claude' | 'Gemini'
}
interface AiCoreApi {
openConfigUI(): void;
getCredential(callerId: string, providerName: string, modelName: string): Promise<CredentialResult>;
exportConfig(callerId: string): Promise<RootConfig>;
importConfig(callerId: string, config: RootConfig, mode?: ImportMode): Promise<void>;
onConfigChange: vscode.Event<RootConfig>;
getModelList(): Promise<string[]>;
}
Methods
openConfigUI(): void
Opens the AI Core configuration panel in the editor.
coreApi.openConfigUI();
getCredential(callerId: string, providerName: string, modelName: string): Promise<CredentialResult>
Retrieves credentials for a specific provider and model. Requires caller to be in the whitelist.
Parameters:
callerId: Your extension ID (checked againstaiCore.allowedExtensions)providerName: The name of the AI provider (must match the configured name exactly)modelName: The model ID (must match a model ID configured under the provider)
Returns: CredentialResult containing apiKey, baseUrl, and format
Throws: Error if caller not in whitelist, or provider/model not found
Load Balancing: If multiple credentials are configured for the same model, one is randomly selected.
exportConfig(callerId: string): Promise<RootConfig>
Exports the full configuration from AI Core. Returns all providers, credentials (with real API keys), and models. Requires caller to be in the whitelist.
Parameters:
callerId: Your extension ID (checked againstaiCore.allowedExtensions)
Returns: RootConfig — the complete configuration array
const config = await coreApi.exportConfig('23J1633.my-extension');
importConfig(callerId: string, config: RootConfig, mode?: ImportMode): Promise<void>
Imports an external configuration into AI Core. Requires caller to be in the whitelist.
Parameters:
callerId: Your extension ID (checked againstaiCore.allowedExtensions)config: The configuration to import (same structure asRootConfig)mode: Optional.'merge'(default) merges with existing config;'replace'overwrites entirely
Merge behavior:
- Providers are matched by
name— if a provider with the same name exists, its credentials are merged - Credentials within a provider are matched by
id— existing entries with the sameidare updated - New providers or credentials are appended
const MY_ID = '23J1633.my-extension';
// Merge: add/update providers and credentials
await coreApi.importConfig(MY_ID, externalConfig);
// Replace: overwrite entire configuration
await coreApi.importConfig(MY_ID, externalConfig, 'replace');
onConfigChange: vscode.Event<RootConfig>
Event that fires whenever the configuration changes (after save, import, etc.).
Callback parameter: The new RootConfig after the change
coreApi.onConfigChange((newConfig) => {
console.log('AI Core config changed, syncing...');
syncToMyExtension(newConfig);
});
getModelList(): Promise<string[]>
Returns a list of all configured models in "provider|model" format.
Returns: Array of strings like "OpenAI|gpt-4", "Anthropic|claude-3-sonnet", etc. Each provider-model combination appears only once.
const MY_ID = '23J1633.my-extension';
const models = await coreApi.getModelList();
console.log(models);
// ["OpenAI|gpt-4", "OpenAI|gpt-3.5-turbo", "Anthropic|claude-3-sonnet"]
// Use in a QuickPick
const selected = await vscode.window.showQuickPick(models, {
placeHolder: 'Select an AI model'
});
if (selected) {
const [providerName, modelName] = selected.split('|');
const cred = await coreApi.getCredential(MY_ID, providerName, modelName);
// ...
}
Config Sync
Sync from External Extension to AI Core
const MY_ID = '23J1633.my-extension';
async function syncToAiCore(myConfig: RootConfig) {
const coreApi = vscode.extensions.getExtension('23J1633-AI-core')?.exports;
if (!coreApi) { return; }
// Import external config, merging with existing
await coreApi.importConfig(MY_ID, myConfig, 'merge');
}
Sync from AI Core to External Extension
function watchAiCoreChanges() {
const coreApi = vscode.extensions.getExtension('23J1633-AI-core')?.exports;
if (!coreApi) { return; }
// Listen for config changes
coreApi.onConfigChange(async (newConfig) => {
// Adapt to your extension's format and save
const adapted = adaptConfigForMyExtension(newConfig);
await saveMyConfig(adapted);
});
}
// Initial sync on activation
async function initialSync() {
const coreApi = vscode.extensions.getExtension('23J1633-AI-core')?.exports;
if (!coreApi) { return; }
const config = await coreApi.exportConfig();
const adapted = adaptConfigForMyExtension(config);
await saveMyConfig(adapted);
}
Integration Examples
Basic Usage
const MY_ID = '23J1633.my-extension';
async function callAI() {
const coreApi = vscode.extensions.getExtension('23J1633-AI-core')?.exports;
try {
const cred = await coreApi.getCredential(MY_ID, 'OpenAI', 'gpt-4');
const response = await fetch(`${cred.baseUrl}/chat/completions`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${cred.apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'gpt-4',
messages: [{ role: 'user', content: 'Hello!' }]
})
});
return await response.json();
} catch (error) {
vscode.window.showErrorMessage(`AI Error: ${error.message}`, 'Configure').then(action => {
if (action === 'Configure') {
coreApi.openConfigUI();
}
});
}
}
Multi-Format Support
const MY_ID = '23J1633.my-extension';
async function callAI(providerName: string, modelName: string) {
const coreApi = vscode.extensions.getExtension('23J1633-AI-core')?.exports;
const cred = await coreApi.getCredential(MY_ID, providerName, modelName);
let requestBody: any;
let headers: Record<string, string>;
switch (cred.format) {
case 'OpenAI':
headers = {
'Authorization': `Bearer ${cred.apiKey}`,
'Content-Type': 'application/json'
};
requestBody = {
model: modelName,
messages: [{ role: 'user', content: 'Hello!' }]
};
break;
case 'Claude':
headers = {
'x-api-key': cred.apiKey,
'anthropic-version': '2023-06-01',
'Content-Type': 'application/json'
};
requestBody = {
model: modelName,
max_tokens: 1024,
messages: [{ role: 'user', content: 'Hello!' }]
};
break;
case 'Gemini':
headers = {
'Content-Type': 'application/json'
};
requestBody = {
contents: [{ parts: [{ text: 'Hello!' }] }]
};
break;
}
const url = cred.format === 'Gemini'
? `${cred.baseUrl}/models/${modelName}:generateContent?key=${cred.apiKey}`
: `${cred.baseUrl}/chat/completions`;
const response = await fetch(url, {
method: 'POST',
headers,
body: JSON.stringify(requestBody)
});
return await response.json();
}
Error Handling with Configuration Prompt
const MY_ID = '23J1633.my-extension';
async function handleAIRequest() {
const coreApi = vscode.extensions.getExtension('23J1633-AI-core')?.exports;
if (!coreApi) {
const action = await vscode.window.showErrorMessage(
'AI Core extension is not installed',
'Install'
);
if (action === 'Install') {
vscode.commands.executeCommand('extension.open', '23J1633-AI-core');
}
return;
}
try {
const cred = await coreApi.getCredential(MY_ID, 'Anthropic', 'claude-3-sonnet');
// ... make API call
} catch (error) {
if (error.message.includes('not in the AI Core access whitelist')) {
const action = await vscode.window.showWarningMessage(
`Access denied: ${error.message}`,
'Open Settings',
'Cancel'
);
if (action === 'Open Settings') {
coreApi.openConfigUI();
}
} else if (error.message.includes('not found')) {
const action = await vscode.window.showWarningMessage(
`Configuration missing for requested model: ${error.message}`,
'Open Settings',
'Cancel'
);
if (action === 'Open Settings') {
coreApi.openConfigUI();
}
} else {
throw error;
}
}
}
Security Notes
- API keys are stored in VS Code's SecretStorage (encrypted by the OS)
- Keys are masked (e.g.,
sk-****1234) when displayed in the UI - The configuration UI never receives full API keys in the DOM
- Keys are only transmitted in memory between extensions
- Access to sensitive APIs is controlled by the
aiCore.allowedExtensionswhitelist
Extension Dependencies
Add to your extension's package.json:
{
"extensionDependencies": [
"23J1633-AI-core"
]
}
This ensures AI Core is installed before your extension activates.
License
MIT
中文
为 VS Code 扩展提供统一的 AI API 配置和凭据管理。
功能特性
- 集中配置:在一个地方管理所有 AI 服务商、API 密钥和模型
- 安全存储:使用 VS Code 的 SecretStorage API 存储 API 密钥
- 多服务商支持:配置多个 AI 服务商(OpenAI、Claude、Gemini 等)
- 负载均衡:自动在同一模型的多个 API 密钥间分配请求
- 图形界面:易于使用的 Webview 界面管理配置
- 跨扩展 API:简单的 API 供其他扩展获取凭据
- 配置同步:导出/导入配置,与其他扩展保持同步
- 访问控制:基于白名单的敏感 API 访问控制
- 连通性测试:在配置界面中直接测试模型 API 可用性,显示延迟
- 凭证命名:为每个 API 凭证自定义名称(如"生产Key"、"测试Key"),便于区分管理
- 自动保存:配置修改后 500ms 防抖自动保存,无需手动操作
- 状态栏图标:可选的齿轮图标显示在状态栏,一键打开配置
- 中英文界面:Webview 界面支持中英文一键切换
安装使用
- 从 VS Code 应用商店安装扩展
- 打开命令面板(
Ctrl+Shift+P/Cmd+Shift+P) - 运行
AI Core: Open Configuration
扩展设置
| 设置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
aiCore.allowedExtensions |
string[] |
["23J1633.ghost-annotations", "23J1633.bcos", "23J1633.hilbert-go"] |
允许访问敏感 API 方法的扩展 ID 白名单 |
aiCore.showStatusBarIcon |
boolean |
true |
是否在状态栏显示 AI Core 齿轮图标 |
配置结构
服务商 (例如 OpenAI)
├── 基础 URL (例如 https://api.openai.com/v1)
├── API 格式 (OpenAI / Claude / Gemini)
└── 凭据[]
├── 名称 (可选,例如"生产Key")
├── API 密钥
└── 模型[]
├── 模型 ID (例如 gpt-4)
└── 显示名称 (例如 GPT-4)
扩展开发者 API 参考
获取 API
import * as vscode from 'vscode';
const coreApi = vscode.extensions.getExtension('23J1633-AI-core')?.exports;
访问控制
敏感 API 方法(getCredential、exportConfig、importConfig)要求调用方传入其扩展 ID 作为第一个参数。该 ID 会在 VS Code 设置的 aiCore.allowedExtensions 白名单中进行校验。
为你的扩展做配置:
- 在 VS Code 设置(
settings.json)中将你的扩展 ID 添加到白名单:
{
"aiCore.allowedExtensions": [
"23J1633.ghost-extensions",
"23J1633.bcos"
]
}
- 调用敏感方法时传入你的扩展 ID:
const MY_ID = '23J1633.my-extension';
const cred = await coreApi.getCredential(MY_ID, 'OpenAI', 'gpt-4');
非敏感方法(openConfigUI、getModelList、onConfigChange)不需要 callerId。
接口定义
type ApiFormat = 'OpenAI' | 'Claude' | 'Gemini';
type ImportMode = 'merge' | 'replace';
interface AiModelConfig {
id: string;
name: string;
}
interface ApiCredential {
id: string;
name?: string; // 可选的凭据显示名称
apiKey: string;
models: AiModelConfig[];
}
interface AiProvider {
id: string;
name: string;
baseUrl: string;
format: ApiFormat;
credentials: ApiCredential[];
}
type RootConfig = AiProvider[];
interface CredentialResult {
apiKey: string; // API 密钥
baseUrl: string; // 服务商的基础 URL
format: ApiFormat; // 'OpenAI' | 'Claude' | 'Gemini'
}
interface AiCoreApi {
openConfigUI(): void;
getCredential(callerId: string, providerName: string, modelName: string): Promise<CredentialResult>;
exportConfig(callerId: string): Promise<RootConfig>;
importConfig(callerId: string, config: RootConfig, mode?: ImportMode): Promise<void>;
onConfigChange: vscode.Event<RootConfig>;
getModelList(): Promise<string[]>;
}
方法说明
openConfigUI(): void
在编辑器中打开 AI Core 配置面板。
coreApi.openConfigUI();
getCredential(callerId: string, providerName: string, modelName: string): Promise<CredentialResult>
获取特定服务商和模型的凭据。需要调用方在白名单中。
参数:
callerId:你的扩展 ID(在aiCore.allowedExtensions中校验)providerName:AI 服务商的名称(必须与配置的名称完全匹配)modelName:模型 ID(必须与服务商下配置的模型 ID 匹配)
返回: 包含 apiKey、baseUrl 和 format 的 CredentialResult
异常: 如果调用方不在白名单中,或找不到服务商/模型会抛出错误
负载均衡: 如果同一模型配置了多个凭据,会随机选择一个。
exportConfig(callerId: string): Promise<RootConfig>
从 AI Core 导出完整配置。返回所有服务商、凭据(包含真实 API 密钥)和模型。需要调用方在白名单中。
参数:
callerId:你的扩展 ID(在aiCore.allowedExtensions中校验)
返回: RootConfig — 完整的配置数组
const config = await coreApi.exportConfig('23J1633.my-extension');
importConfig(callerId: string, config: RootConfig, mode?: ImportMode): Promise<void>
将外部配置导入到 AI Core。需要调用方在白名单中。
参数:
callerId:你的扩展 ID(在aiCore.allowedExtensions中校验)config:要导入的配置(与RootConfig结构相同)mode:可选。'merge'(默认)与现有配置合并;'replace'完全覆盖
合并行为:
- 服务商按
name匹配 — 如果同名服务商已存在,则合并其凭据 - 服务商内的凭据按
id匹配 — 相同id的条目会被更新 - 新的服务商或凭据会被追加
const MY_ID = '23J1633.my-extension';
// 合并:添加/更新服务商和凭据
await coreApi.importConfig(MY_ID, externalConfig);
// 替换:覆盖整个配置
await coreApi.importConfig(MY_ID, externalConfig, 'replace');
onConfigChange: vscode.Event<RootConfig>
配置变更时触发的事件(保存、导入等操作后)。
回调参数: 变更后的新的 RootConfig
coreApi.onConfigChange((newConfig) => {
console.log('AI Core 配置已变更,开始同步...');
syncToMyExtension(newConfig);
});
getModelList(): Promise<string[]>
返回所有已配置模型的列表,格式为 "服务商|模型"。
返回: 字符串数组,如 ["OpenAI|gpt-4", "Anthropic|claude-3-sonnet"]。每个服务商-模型组合仅出现一次。
const MY_ID = '23J1633.my-extension';
const models = await coreApi.getModelList();
console.log(models);
// ["OpenAI|gpt-4", "OpenAI|gpt-3.5-turbo", "Anthropic|claude-3-sonnet"]
// 用于 QuickPick 选择
const selected = await vscode.window.showQuickPick(models, {
placeHolder: '选择一个 AI 模型'
});
if (selected) {
const [providerName, modelName] = selected.split('|');
const cred = await coreApi.getCredential(MY_ID, providerName, modelName);
// ...
}
配置同步
从外部扩展同步到 AI Core
const MY_ID = '23J1633.my-extension';
async function syncToAiCore(myConfig: RootConfig) {
const coreApi = vscode.extensions.getExtension('23J1633-AI-core')?.exports;
if (!coreApi) { return; }
// 导入外部配置,与现有配置合并
await coreApi.importConfig(MY_ID, myConfig, 'merge');
}
从 AI Core 同步到外部扩展
function watchAiCoreChanges() {
const coreApi = vscode.extensions.getExtension('23J1633-AI-core')?.exports;
if (!coreApi) { return; }
// 监听配置变更
coreApi.onConfigChange(async (newConfig) => {
// 转换为你的扩展格式并保存
const adapted = adaptConfigForMyExtension(newConfig);
await saveMyConfig(adapted);
});
}
// 激活时初始同步
async function initialSync() {
const coreApi = vscode.extensions.getExtension('23J1633-AI-core')?.exports;
if (!coreApi) { return; }
const config = await coreApi.exportConfig(MY_ID);
const adapted = adaptConfigForMyExtension(config);
await saveMyConfig(adapted);
}
集成示例
基础用法
const MY_ID = '23J1633.my-extension';
async function callAI() {
const coreApi = vscode.extensions.getExtension('23J1633-AI-core')?.exports;
try {
const cred = await coreApi.getCredential(MY_ID, 'OpenAI', 'gpt-4');
const response = await fetch(`${cred.baseUrl}/chat/completions`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${cred.apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'gpt-4',
messages: [{ role: 'user', content: '你好!' }]
})
});
return await response.json();
} catch (error) {
vscode.window.showErrorMessage(`AI 错误: ${error.message}`, '去配置').then(action => {
if (action === '去配置') {
coreApi.openConfigUI();
}
});
}
}
多格式支持
const MY_ID = '23J1633.my-extension';
async function callAI(providerName: string, modelName: string) {
const coreApi = vscode.extensions.getExtension('23J1633-AI-core')?.exports;
const cred = await coreApi.getCredential(MY_ID, providerName, modelName);
let requestBody: any;
let headers: Record<string, string>;
switch (cred.format) {
case 'OpenAI':
headers = {
'Authorization': `Bearer ${cred.apiKey}`,
'Content-Type': 'application/json'
};
requestBody = {
model: modelName,
messages: [{ role: 'user', content: '你好!' }]
};
break;
case 'Claude':
headers = {
'x-api-key': cred.apiKey,
'anthropic-version': '2023-06-01',
'Content-Type': 'application/json'
};
requestBody = {
model: modelName,
max_tokens: 1024,
messages: [{ role: 'user', content: '你好!' }]
};
break;
case 'Gemini':
headers = {
'Content-Type': 'application/json'
};
requestBody = {
contents: [{ parts: [{ text: '你好!' }] }]
};
break;
}
const url = cred.format === 'Gemini'
? `${cred.baseUrl}/models/${modelName}:generateContent?key=${cred.apiKey}`
: `${cred.baseUrl}/chat/completions`;
const response = await fetch(url, {
method: 'POST',
headers,
body: JSON.stringify(requestBody)
});
return await response.json();
}
带配置引导的错误处理
const MY_ID = '23J1633.my-extension';
async function handleAIRequest() {
const coreApi = vscode.extensions.getExtension('23J1633-AI-core')?.exports;
if (!coreApi) {
const action = await vscode.window.showErrorMessage(
'AI Core 扩展未安装',
'去安装'
);
if (action === '去安装') {
vscode.commands.executeCommand('extension.open', '23J1633-AI-core');
}
return;
}
try {
const cred = await coreApi.getCredential(MY_ID, 'Anthropic', 'claude-3-sonnet');
// ... 发起 API 调用
} catch (error) {
if (error.message.includes('not in the AI Core access whitelist')) {
const action = await vscode.window.showWarningMessage(
`访问被拒绝: ${error.message}`,
'打开设置',
'取消'
);
if (action === '打开设置') {
coreApi.openConfigUI();
}
} else if (error.message.includes('not found')) {
const action = await vscode.window.showWarningMessage(
`请求的模型配置缺失: ${error.message}`,
'打开设置',
'取消'
);
if (action === '打开设置') {
coreApi.openConfigUI();
}
} else {
throw error;
}
}
}
安全说明
- API 密钥存储在 VS Code 的 SecretStorage 中(由操作系统加密)
- 密钥在 UI 中显示时会被脱敏(例如
sk-****1234) - 配置界面的 DOM 中永远不会接收到完整的 API 密钥
- 密钥仅在扩展间的内存中传输
- 敏感 API 的访问由
aiCore.allowedExtensions白名单控制
扩展依赖
在你的扩展的 package.json 中添加:
{
"extensionDependencies": [
"23J1633-AI-core"
]
}
这确保 AI Core 在你的扩展激活之前已安装。
许可证
MIT