From c632f184c68ec824091ca7ba660dbd904de98851 Mon Sep 17 00:00:00 2001 From: hex2077 Date: Tue, 3 Feb 2026 18:55:22 +0800 Subject: [PATCH] =?UTF-8?q?feat(claude-kiro):=20=E4=B8=BA=E7=A9=BA?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E5=88=97=E8=A1=A8=E6=B7=BB=E5=8A=A0=E5=8D=A0?= =?UTF-8?q?=E4=BD=8D=E5=B7=A5=E5=85=B7=E9=81=BF=E5=85=8DAPI=E9=94=99?= =?UTF-8?q?=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refactor(usage-api): 提取支持的提供商列表为共享常量 fix(usage-service): 移除重置时间的本地化格式化避免显示问题 style(usage-manager): 为不支持用量显示的提供商优化UI展示 chore: 更新README中的文档链接指向docs目录 --- README-JA.md | 2 +- README-ZH.md | 2 +- README.md | 2 +- .../OPENCLAW_CONFIG_GUIDE-JA.md | 2 +- .../OPENCLAW_CONFIG_GUIDE-ZH.md | 0 .../OPENCLAW_CONFIG_GUIDE.md | 2 +- .../OPENCODE_CONFIG_EXAMPLE.md | 0 .../PROVIDER_ADAPTER_GUIDE.md | 0 src/providers/claude/claude-kiro.js | 33 +++++++++- src/services/usage-service.js | 10 ++- src/ui-modules/usage-api.js | 6 +- static/app/usage-manager.js | 64 +++++++++++++++---- 12 files changed, 92 insertions(+), 31 deletions(-) rename OPENCLAW_CONFIG_GUIDE-JA.md => docs/OPENCLAW_CONFIG_GUIDE-JA.md (98%) rename OPENCLAW_CONFIG_GUIDE-ZH.md => docs/OPENCLAW_CONFIG_GUIDE-ZH.md (100%) rename OPENCLAW_CONFIG_GUIDE.md => docs/OPENCLAW_CONFIG_GUIDE.md (98%) rename OPENCODE_CONFIG_EXAMPLE.md => docs/OPENCODE_CONFIG_EXAMPLE.md (100%) rename PROVIDER_ADAPTER_GUIDE.md => docs/PROVIDER_ADAPTER_GUIDE.md (100%) diff --git a/README-JA.md b/README-JA.md index 970cb55..9a15fb9 100644 --- a/README-JA.md +++ b/README-JA.md @@ -18,7 +18,7 @@ [![GitHub stars](https://img.shields.io/github/stars/justlovemaki/AIClient-2-API.svg?style=flat&label=Star)](https://github.com/justlovemaki/AIClient-2-API/stargazers) [![GitHub issues](https://img.shields.io/github/issues/justlovemaki/AIClient-2-API.svg)](https://github.com/justlovemaki/AIClient-2-API/issues) -[**🔧 OpenClaw 設定**](./OPENCLAW_CONFIG_GUIDE-JA.md) | [中文](./README-ZH.md) | [English](./README.md) | [**👉 日本語**](./README-JA.md) | [**📚 完全ドキュメント**](https://aiproxy.justlikemaki.vip/ja/) +[**🔧 OpenClaw 設定**](./docs/OPENCLAW_CONFIG_GUIDE-JA.md) | [中文](./README-ZH.md) | [English](./README.md) | [**👉 日本語**](./README-JA.md) | [**📚 完全ドキュメント**](https://aiproxy.justlikemaki.vip/ja/) diff --git a/README-ZH.md b/README-ZH.md index c064c95..11b870e 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -18,7 +18,7 @@ [![GitHub stars](https://img.shields.io/github/stars/justlovemaki/AIClient-2-API.svg?style=flat&label=Star)](https://github.com/justlovemaki/AIClient-2-API/stargazers) [![GitHub issues](https://img.shields.io/github/issues/justlovemaki/AIClient-2-API.svg)](https://github.com/justlovemaki/AIClient-2-API/issues) -[**🔧 OpenClaw 配置**](./OPENCLAW_CONFIG_GUIDE-ZH.md) | [**👉 中文**](./README-ZH.md) | [English](./README.md) | [日本語](./README-JA.md) | [**📚 完整文档**](https://aiproxy.justlikemaki.vip/zh/) +[**🔧 OpenClaw 配置**](./docs/OPENCLAW_CONFIG_GUIDE-ZH.md) | [**👉 中文**](./README-ZH.md) | [English](./README.md) | [日本語](./README-JA.md) | [**📚 完整文档**](https://aiproxy.justlikemaki.vip/zh/) diff --git a/README.md b/README.md index 3fa7f9b..e400776 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ [![GitHub stars](https://img.shields.io/github/stars/justlovemaki/AIClient-2-API.svg?style=flat&label=Star)](https://github.com/justlovemaki/AIClient-2-API/stargazers) [![GitHub issues](https://img.shields.io/github/issues/justlovemaki/AIClient-2-API.svg)](https://github.com/justlovemaki/AIClient-2-API/issues) -[**🔧 OpenClaw Config**](./OPENCLAW_CONFIG_GUIDE.md) | [中文](./README-ZH.md) | [**👉 English**](./README.md) | [日本語](./README-JA.md) | [**📚 Documentation**](https://aiproxy.justlikemaki.vip/en/) +[**🔧 OpenClaw Config**](./docs/OPENCLAW_CONFIG_GUIDE.md) | [中文](./README-ZH.md) | [**👉 English**](./README.md) | [日本語](./README-JA.md) | [**📚 Documentation**](https://aiproxy.justlikemaki.vip/en/) diff --git a/OPENCLAW_CONFIG_GUIDE-JA.md b/docs/OPENCLAW_CONFIG_GUIDE-JA.md similarity index 98% rename from OPENCLAW_CONFIG_GUIDE-JA.md rename to docs/OPENCLAW_CONFIG_GUIDE-JA.md index d6afec7..d33f60e 100644 --- a/OPENCLAW_CONFIG_GUIDE-JA.md +++ b/docs/OPENCLAW_CONFIG_GUIDE-JA.md @@ -210,4 +210,4 @@ openclaw chat --model aiclient2api/gemini-3-flash-preview "あなたの質問" --- -詳細については、[AIClient-2-API ドキュメント](./README-JA.md) を参照してください +詳細については、[AIClient-2-API ドキュメント](../README-JA.md) を参照してください diff --git a/OPENCLAW_CONFIG_GUIDE-ZH.md b/docs/OPENCLAW_CONFIG_GUIDE-ZH.md similarity index 100% rename from OPENCLAW_CONFIG_GUIDE-ZH.md rename to docs/OPENCLAW_CONFIG_GUIDE-ZH.md diff --git a/OPENCLAW_CONFIG_GUIDE.md b/docs/OPENCLAW_CONFIG_GUIDE.md similarity index 98% rename from OPENCLAW_CONFIG_GUIDE.md rename to docs/OPENCLAW_CONFIG_GUIDE.md index ff6fe25..f62529e 100644 --- a/OPENCLAW_CONFIG_GUIDE.md +++ b/docs/OPENCLAW_CONFIG_GUIDE.md @@ -210,4 +210,4 @@ openclaw chat --model aiclient2api/gemini-3-flash-preview "your question" --- -For more information, see [AIClient-2-API Documentation](./README.md) +For more information, see [AIClient-2-API Documentation](../README.md) diff --git a/OPENCODE_CONFIG_EXAMPLE.md b/docs/OPENCODE_CONFIG_EXAMPLE.md similarity index 100% rename from OPENCODE_CONFIG_EXAMPLE.md rename to docs/OPENCODE_CONFIG_EXAMPLE.md diff --git a/PROVIDER_ADAPTER_GUIDE.md b/docs/PROVIDER_ADAPTER_GUIDE.md similarity index 100% rename from PROVIDER_ADAPTER_GUIDE.md rename to docs/PROVIDER_ADAPTER_GUIDE.md diff --git a/src/providers/claude/claude-kiro.js b/src/providers/claude/claude-kiro.js index fb54b56..02ce9f3 100644 --- a/src/providers/claude/claude-kiro.js +++ b/src/providers/claude/claude-kiro.js @@ -870,8 +870,21 @@ async saveCredentialsToFile(filePath, newData) { }); if (filteredTools.length === 0) { - // 所有工具都被过滤掉了,不添加 tools 上下文 - logger.info('[Kiro] All tools were filtered out'); + // 所有工具都被过滤掉了,添加一个占位工具 + logger.info('[Kiro] All tools were filtered out, adding placeholder tool'); + const placeholderTool = { + toolSpecification: { + name: "no_tool_available", + description: "This is a placeholder tool when no other tools are available. It does nothing.", + inputSchema: { + json: { + type: "object", + properties: {} + } + } + } + }; + toolsContext = { tools: [placeholderTool] }; } else { const MAX_DESCRIPTION_LENGTH = 9216; @@ -903,6 +916,22 @@ async saveCredentialsToFile(filePath, newData) { toolsContext = { tools: kiroTools }; } + } else { + // tools 为空或长度为 0 时,自动添加一个占位工具 + logger.info('[Kiro] No tools provided, adding placeholder tool'); + const placeholderTool = { + toolSpecification: { + name: "no_tool_available", + description: "This is a placeholder tool when no other tools are available. It does nothing.", + inputSchema: { + json: { + type: "object", + properties: {} + } + } + } + }; + toolsContext = { tools: [placeholderTool] }; } const history = []; diff --git a/src/services/usage-service.js b/src/services/usage-service.js index a4e30b5..5f57b92 100644 --- a/src/services/usage-service.js +++ b/src/services/usage-service.js @@ -5,7 +5,7 @@ import { getProviderPoolManager } from './service-manager.js'; import { serviceInstances } from '../providers/adapter.js'; -import { MODEL_PROVIDER, formatToLocal } from '../utils/common.js'; +import { MODEL_PROVIDER } from '../utils/common.js'; /** * 用量查询服务类 @@ -396,8 +396,7 @@ export function formatGeminiUsage(usageData) { outputTokenLimit: modelInfo.outputTokenLimit || 0, remaining: remainingPercent, remainingPercent: Math.round(remainingPercent * 100), // 剩余百分比 - resetTime: (modelInfo.resetTimeRaw || modelInfo.resetTime) ? - formatToLocal(modelInfo.resetTimeRaw || modelInfo.resetTime) : '--', + resetTime: modelInfo.resetTime || '--', resetTimeRaw: modelInfo.resetTimeRaw || modelInfo.resetTime || null }; @@ -464,7 +463,7 @@ export function formatAntigravityUsage(usageData) { // 优先使用模型自己的重置时间,如果没有则使用全局重置时间 const resetTimeRaw = modelInfo.resetTimeRaw || (usageData.quotaInfo ? usageData.quotaInfo.quotaResetTime : null); - const resetTimeFormatted = resetTimeRaw ? formatToLocal(resetTimeRaw) : (modelInfo.resetTime || '--'); + const resetTimeFormatted = modelInfo.resetTime || '--'; const item = { resourceType: 'MODEL_USAGE', @@ -592,8 +591,7 @@ export function formatCodexUsage(usageData) { modelName: modelName, remaining: remainingPercent, remainingPercent: Math.round(remainingPercent * 100), // 剩余百分比 - resetTime: (modelInfo.resetTimeRaw || modelInfo.resetTime) ? - formatToLocal(modelInfo.resetTimeRaw || modelInfo.resetTime) : '--', + resetTime: modelInfo.resetTime || '--', resetTimeRaw: modelInfo.resetTimeRaw || modelInfo.resetTime || null, // 注入 raw 窗口信息以便前端使用 diff --git a/src/ui-modules/usage-api.js b/src/ui-modules/usage-api.js index 994d8ec..d9298c9 100644 --- a/src/ui-modules/usage-api.js +++ b/src/ui-modules/usage-api.js @@ -5,6 +5,8 @@ import { formatKiroUsage, formatGeminiUsage, formatAntigravityUsage, formatCodex import { readUsageCache, writeUsageCache, readProviderUsageCache, updateProviderUsageCache } from './usage-cache.js'; import path from 'path'; +const supportedProviders = ['claude-kiro-oauth', 'gemini-cli-oauth', 'gemini-antigravity', 'openai-codex-oauth']; + /** * 获取所有支持用量查询的提供商的用量信息 * @param {Object} currentConfig - 当前配置 @@ -17,9 +19,6 @@ async function getAllProvidersUsage(currentConfig, providerPoolManager) { providers: {} }; - // 支持用量查询的提供商列表 - const supportedProviders = ['claude-kiro-oauth', 'gemini-cli-oauth', 'gemini-antigravity', 'openai-codex-oauth']; - // 并发获取所有提供商的用量数据 const usagePromises = supportedProviders.map(async (providerType) => { try { @@ -225,7 +224,6 @@ function getProviderDisplayName(provider, providerType) { */ export async function handleGetSupportedProviders(req, res) { try { - const supportedProviders = ['claude-kiro-oauth', 'gemini-cli-oauth', 'gemini-antigravity', 'openai-codex-oauth']; res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(supportedProviders)); return true; diff --git a/static/app/usage-manager.js b/static/app/usage-manager.js index 62cb7b8..c4d23ab 100644 --- a/static/app/usage-manager.js +++ b/static/app/usage-manager.js @@ -4,6 +4,23 @@ import { showToast } from './utils.js'; import { getAuthHeaders } from './auth.js'; import { t, getCurrentLanguage } from './i18n.js'; +/** + * 不支持显示用量数据的提供商列表 + * 这些提供商只显示模型名称和重置时间,不显示用量数字和进度条 + */ +const PROVIDERS_WITHOUT_USAGE_DISPLAY = [ + 'gemini-antigravity' +]; + +/** + * 检查提供商是否支持显示用量 + * @param {string} providerType - 提供商类型 + * @returns {boolean} 是否支持显示用量 + */ +function shouldShowUsage(providerType) { + return !PROVIDERS_WITHOUT_USAGE_DISPLAY.includes(providerType); +} + /** * 初始化用量管理功能 */ @@ -404,6 +421,9 @@ function createInstanceUsageCard(instance, providerType) { const providerDisplayName = getProviderDisplayName(providerType); const providerIcon = getProviderIcon(providerType); + // 检查是否应该显示用量信息 + const showUsage = shouldShowUsage(providerType); + // 计算总用量(用于折叠摘要显示) const totalUsage = instance.usage ? calculateTotalUsage(instance.usage.usageBreakdown) : { hasData: false, percent: 0 }; const progressClass = totalUsage.percent >= 90 ? 'danger' : (totalUsage.percent >= 70 ? 'warning' : 'normal'); @@ -429,6 +449,7 @@ function createInstanceUsageCard(instance, providerType) { ${displayName} ${statusIcon} + ${showUsage ? `
${totalUsage.hasData ? `
@@ -438,6 +459,7 @@ function createInstanceUsageCard(instance, providerType) { ${displayUsageText} ` : (instance.error ? `${t('common.error')}` : '')}
+ ` : ''} `; // 点击折叠摘要切换展开状态 @@ -504,7 +526,7 @@ function createInstanceUsageCard(instance, providerType) {
`; } else if (instance.usage) { - content.appendChild(renderUsageDetails(instance.usage)); + content.appendChild(renderUsageDetails(instance.usage, providerType)); } expandedContent.appendChild(content); @@ -516,17 +538,21 @@ function createInstanceUsageCard(instance, providerType) { /** * 渲染用量详情 - 显示总用量、用量明细和到期时间 * @param {Object} usage - 用量数据 + * @param {string} providerType - 提供商类型 * @returns {HTMLElement} 详情元素 */ -function renderUsageDetails(usage) { +function renderUsageDetails(usage, providerType) { const container = document.createElement('div'); container.className = 'usage-details'; + // 检查是否应该显示用量信息 + const showUsage = shouldShowUsage(providerType); + // 计算总用量 const totalUsage = calculateTotalUsage(usage.usageBreakdown); - // 总用量进度条 - if (totalUsage.hasData) { + // 总用量进度条(不支持显示用量的提供商不显示) + if (totalUsage.hasData && showUsage) { const totalSection = document.createElement('div'); totalSection.className = 'usage-section total-usage'; @@ -543,11 +569,14 @@ function renderUsageDetails(usage) { `; } else { const resetTimeEntry = usage.usageBreakdown.find(b => b.resetTime && b.resetTime !== '--'); - resetTimeHTML = resetTimeEntry ? ` -
- ${t('usage.card.resetAt', { time: resetTimeEntry.resetTime })} -
- ` : ''; + if (resetTimeEntry) { + const formattedResetTime = formatDate(resetTimeEntry.resetTime); + resetTimeHTML = ` +
+ ${t('usage.card.resetAt', { time: formattedResetTime })} +
+ `; + } } const displayValue = totalUsage.isCodex @@ -582,7 +611,7 @@ function renderUsageDetails(usage) { let breakdownHTML = ''; for (const breakdown of usage.usageBreakdown) { - breakdownHTML += createUsageBreakdownHTML(breakdown); + breakdownHTML += createUsageBreakdownHTML(breakdown, providerType); } breakdownSection.innerHTML = breakdownHTML; @@ -595,14 +624,18 @@ function renderUsageDetails(usage) { /** * 创建用量明细 HTML(紧凑版) * @param {Object} breakdown - 用量明细数据 + * @param {string} providerType - 提供商类型 * @returns {string} HTML 字符串 */ -function createUsageBreakdownHTML(breakdown) { +function createUsageBreakdownHTML(breakdown, providerType) { // 特殊处理 Codex if (breakdown.rateLimit && breakdown.rateLimit.primary_window) { return createCodexUsageBreakdownHTML(breakdown); } + // 检查是否应该显示用量信息 + const showUsage = shouldShowUsage(providerType); + const usagePercent = breakdown.usageLimit > 0 ? Math.min(100, (breakdown.currentUsage / breakdown.usageLimit) * 100) : 0; @@ -613,21 +646,24 @@ function createUsageBreakdownHTML(breakdown) {
${breakdown.displayName || breakdown.resourceType} - ${formatNumber(breakdown.currentUsage)} / ${formatNumber(breakdown.usageLimit)} + ${showUsage ? `${formatNumber(breakdown.currentUsage)} / ${formatNumber(breakdown.usageLimit)}` : ''}
+ ${showUsage ? `
+ ` : ''} `; // 如果有重置时间,则显示 if (breakdown.resetTime && breakdown.resetTime !== '--') { - const resetText = t('usage.card.resetAt', { time: breakdown.resetTime }); + const formattedResetTime = formatDate(breakdown.resetTime); + const resetText = t('usage.card.resetAt', { time: formattedResetTime }); html += `
- ${resetText} + ${resetText}
`;