- LingtrueAPIによる本プロジェクトへのスポンサーに感謝します!LingtrueAPIは世界的な大規模言語モデルAPI中継プラットフォームであり、Claude opus 4.6、GPT 5.4、Gemini 3.1 proなど各種モデルのAPI呼び出しサービスを提供しています。低コスト、高安定性で世界中のAI機能に接続し、生産性を最大化することを目指しています。LingtrueAPIは本ソフトウェアユーザー向けに特別優遇を提供しています。このリンクから登録し、初回チャージ時に「LingtrueAPI」のクーポンコードを入力すると、10%オフで利用できます。
+ VisionCoder による本プロジェクトへのスポンサーに感謝します!VisionCoder 開発プラットフォームは信頼性が高く効率的な API 中継サービスプロバイダーであり、Claude Code、Codex、Gemini などの主要な AI モデルへのアクセスを提供しています。開発者やチームが AI 機能をより簡単に統合し、生産性を向上させるのを支援します。VisionCoder は本ソフトウェアのユーザー向けに期間限定の Token Plan 特典を提供しています:1ヶ月の購入で1ヶ月分を無料で進呈。
+ Atlas Cloud による本プロジェクトへのスポンサーに感謝します!Atlas Cloud は、開発者が動画生成、画像生成、および LLM API にアクセスするための单二の AI API を提供する全モーダル AI 推論プラットフォームです。複数のベンダーの統合を管理する代わりに、一度接続するだけですべてのモダリティにわたる 300 以上の厳選されたモデルに統合アクセスできます。よりリーズナブルな API アクセスのために、Atlas Cloud の新しいコーディングプランプロモーション (coding plan)をぜひチェックしてください。
+
- Thanks to AICodeMirror for sponsoring this project! AICodeMirror provides official high-stability relay services for Claude Code / Codex / Gemini CLI, with enterprise-grade concurrency, fast invoicing, and 24/7 dedicated technical support. Claude Code / Codex / Gemini official channels at 38% / 2% / 9% of original price, with extra discounts on top-ups! AICodeMirror offers special benefits for AIClient-2-API users: register via this link to enjoy 20% off your first top-up, and enterprise customers can get up to 25% off!
+ Thanks to APIKEY.FUN for sponsoring this project! APIKEY.FUN is a professional enterprise-grade AI relay station, dedicated to providing stable, efficient, and low-cost AI model API access services for enterprises and individual developers. The platform supports mainstream popular models such as Claude, OpenAI, and Gemini, with prices as low as 7% of the official original price. Register through the project's exclusive link to enjoy an exclusive discount of up to 5% off (95% of original price) for permanent recharges.
- Thanks to LingtrueAPI for its sponsorship of this project! LingtrueAPI is a global large-model API intermediary service platform that offers API calling services for various models such as Claude opus 4.6, GPT 5.4, and Gemini 3.1 pro. It is committed to enabling users to connect to global AI capabilities at low cost and with high stability, maximizing production efficiency. LingtrueAPI provides special discounts for users of this software: register using this link and enter the LingtrueAPI promo code when making the first recharge to enjoy a 10% discount.
+ Thanks to VisionCoder for supporting this project. VisionCoder Developer Platform is a reliable and efficient API relay service provider, offering access to mainstream AI models such as Claude Code, Codex, and Gemini. It helps developers and teams integrate AI capabilities more easily and improve productivity. VisionCoder is also offering our users a limited-time Token Plan promotion: buy 1 month and get 1 month free.
+
+ Thanks to Atlas Cloud for sponsoring this project! Atlas Cloud is a full-modal AI inference platform that gives developers a single AI API to access video generation, image generation, and LLM APIs. Instead of managing multiple vendor integrations, you connect once and get unified access to 300+ curated models across all modalities. Check out Atlas Cloud's new coding plan promotion for more budget-friendly API access.
+
+
+
+
@@ -82,7 +140,7 @@
## 🚀 Overview
-`AIClient2API` is an API proxy service that breaks through client limitations, converting free large models originally restricted to client use only (such as Gemini, Antigravity, Codex, Grok, Kiro) into standard OpenAI-compatible interfaces that can be called by any application. Built on Node.js, it supports intelligent conversion between OpenAI, Claude, and Gemini protocols, enabling tools like Cherry-Studio, NextChat, and Cline to freely use advanced models such as Claude Opus 4.5, Gemini 3.0 Pro, and Qwen3 Coder Plus at scale. The project adopts a modular architecture based on strategy and adapter patterns, with built-in account pool management, intelligent polling, automatic failover, and health check mechanisms, ensuring 99.9% service availability.
+`AIClient2API` is an API proxy service that breaks through client limitations, converting free large models originally restricted to client use only (such as Gemini, Antigravity, Codex, Grok, Kiro) into standard OpenAI-compatible interfaces that can be called by any application. Built on Node.js, it supports intelligent conversion between OpenAI, Claude, and Gemini protocols, enabling tools like Cherry-Studio, NextChat, and Cline to freely use advanced models such as Claude Opus and Gemini Pro at scale. The project adopts a modular architecture based on strategy and adapter patterns, with built-in account pool management, intelligent polling, automatic failover, and health check mechanisms, ensuring 99.9% service availability.
> [!NOTE]
> **🎉 Important Milestone**
@@ -94,7 +152,10 @@
>
> Click to expand detailed version history
>
-> - **2026.03.02** - Added Grok protocol support, supporting access to xAI Grok series models (Grok 3/4) via Cookie/SSO, supporting multimodal input, image/video generation, automatic token refresh and streaming output
+> - **2026.06.03** - Added Grok Build (Grok CLI) support: integrated the `grok-cli-oauth` xAI OAuth / Responses API flow, covering Grok Build text models, multi-protocol conversion, built-in tools (web search, X search, code interpreter, collections/file attachments), and image/video generation models.
+> - **2026.05.04 (v3.0.0)** - **Milestone Update: Deep AI Integration & Self-Discovery Architecture**. Added automated Skill guides and remote `/api/help`, `/api/example` endpoints, enabling AI agents to seamlessly understand and operate 50+ full API endpoints; achieved full unification of CLI and REST API output results with enhanced structured JSON support.
+> - **2026.04.29** - Comprehensive support for OpenAI standard Image Generation (`/v1/images/generations`) and Image Editing (`/v1/images/edits`) interfaces. Supports automatic conversion from OpenAI format to native image generation protocols of various models, fully compatible with provider pool polling and retry mechanisms, significantly improving the stability of multimodal creation.
+> - **2026.03.02** - Added Grok protocol support, supporting access to xAI Grok series models (Grok) via Cookie/SSO, supporting multimodal input, image/video generation, automatic token refresh and streaming output
> - **2026.01.26** - Added Codex protocol support: supports OpenAI Codex OAuth authorization access
> - **2026.01.25** - Enhanced AI Monitor plugin: supports monitoring request parameters and responses before and after AI protocol conversion. Optimized log management: unified log format, visual configuration
> - **2026.01.15** - Optimized provider pool manager: added async refresh queue mechanism, buffer queue deduplication, global concurrency control, node warmup and automatic expiry detection
@@ -102,11 +163,10 @@
> - **2025.12.30** - Added main process management and automatic update functionality
> - **2025.12.25** - Unified configuration management: All configs centralized to `configs/` directory. Docker users need to update mount path to `-v "local_path:/app/configs"`
> - **2025.12.11** - Automatically built Docker images are now available on Docker Hub: [justlikemaki/aiclient-2-api](https://hub.docker.com/r/justlikemaki/aiclient-2-api)
-> - **2025.11.30** - Added Antigravity protocol support, enabling access to Gemini 3 Pro, Claude Sonnet 4.5, and other models via Google internal interfaces
+> - **2025.11.30** - Added Antigravity protocol support, enabling access to Gemini Pro, Claude Sonnet, and other models via Google internal interfaces
> - **2025.11.11** - Added Web UI management console, supporting real-time configuration management and health status monitoring
-> - **2025.11.06** - Added support for Gemini 3 Preview, enhanced model compatibility and performance optimization
-> - **2025.10.18** - Kiro open registration, new accounts get 500 credits, full support for Claude Sonnet 4.5
-> - **2025.09.01** - Integrated Qwen Code CLI, added `qwen3-coder-plus` model support
+> - **2025.11.06** - Added support for Gemini Preview, enhanced model compatibility and performance optimization
+> - **2025.10.18** - Kiro open registration, new accounts get 500 credits, full support for Claude Sonnet
> - **2025.08.29** - Released account pool management feature, supporting multi-account polling, intelligent failover, and automatic degradation strategies
> - Configuration: Add `PROVIDER_POOLS_FILE_PATH` parameter in `configs/config.json`
> - Reference configuration: [provider_pools.json](./configs/provider_pools.json.example)
@@ -119,8 +179,24 @@
## 💡 Core Advantages
+### 🤖 AI-First, Agent Interaction Support
+
+> **AI-First Design**: This project natively supports efficient interaction with mainstream AI Agents such as OpenClaw, Hermes, and Claude Code.
+>
+> **💡 Quick Command**: You can tell the AI this sentence directly, and it will automatically master all usage of this project:
+>
+> - **Remote Deployment**:
+> ```text
+> Please load and learn the Skill in https://raw.githubusercontent.com/justlovemaki/AIClient2API/main/docs/skills/aiclient-cli-usage.md (Service Address: your actual domain or IP, Login Password: your actual password) to master all usage of AIClient2API.
+> ```
+> - **Local Mode**:
+> If you are running the AI agent directly in your local environment, just send:
+> ```text
+> Please load and learn the Skill in docs/skills/aiclient-cli-usage.md to help me start, configure, and manage the AIClient2API service locally.
+> ```
+
### 🎯 Unified Access, One-Stop Management
-* **Multi-Model Unified Interface**: Through standard OpenAI-compatible protocol, configure once to access mainstream large models including Gemini, Claude, Grok, Codex, Kimi K2, MiniMax M2
+* **Multi-Model Unified Interface**: Through standard OpenAI-compatible protocol, configure once to access mainstream large models including Gemini, Claude, Grok, Codex, Kimi, MiniMax
* **Flexible Switching Mechanism**: Path routing, support dynamic model switching via startup parameters or environment variables to meet different scenario requirements
* **Zero-Cost Migration**: Fully compatible with OpenAI API specifications, tools like Cherry-Studio, NextChat, Cline can be used without modification
* **Multi-Protocol Intelligent Conversion**: Support intelligent conversion between OpenAI, Claude, and Gemini protocols for cross-protocol model invocation
@@ -128,7 +204,7 @@
### 🚀 Break Through Limitations, Improve Efficiency
* **Bypass Official Restrictions**: Utilize OAuth authorization mechanism to effectively break through rate and quota limits of services like Gemini, Antigravity
* **TLS Fingerprint Bypass**: Built-in TLS Sidecar (Go uTLS) to simulate browser features, effectively bypassing Cloudflare 403 blocks for services like Grok
-* **Free Advanced Models**: Use Claude Opus 4.5 for free via Kiro API mode, use Qwen3 Coder Plus via Qwen OAuth mode, reducing usage costs
+* **Free Advanced Models**: Use Claude Opus for free via Kiro API mode, reducing usage costs
* **Intelligent Account Pool Scheduling**: Support multi-account polling, automatic failover, and configuration degradation, ensuring 99.9% service availability
### 🛡️ Secure and Controllable, Data Transparent
@@ -164,19 +240,20 @@
### 🚀 Quick Start
-The most recommended way to use AIClient-2-API is to start it through an automated script and configure it visually directly in the **Web UI console**.
+The most recommended way to use AIClient2API is to start it through an automated script and configure it visually directly in the **Web UI console**.
#### 🐳 Docker Quick Start (Recommended)
```bash
-docker run -d -p 3000:3000 -p 8085-8086:8085-8086 -p 1455:1455 -p 19876-19880:19876-19880 --restart=always -v "your_path:/app/configs" --name aiclient2api justlikemaki/aiclient-2-api
+docker run -d -p 3000:3000 -p 8085-8086:8085-8086 -p 1455:1455 -p 56121:56121 -p 19876-19880:19876-19880 --restart=always -v "your_path/configs:/app/configs" -v "your_path/plugins:/app/src/plugins-user" --name aiclient2api justlikemaki/aiclient-2-api
```
**Parameter Description**:
- `-d`: Run container in background
-- `-p 3000:3000 ...`: Port mapping. 3000 is for Web UI, others are for OAuth callbacks (Gemini: 8085, Antigravity: 8086, Codex: 1455, Kiro: 19876-19880)
+- `-p 3000:3000 ...`: Port mapping. 3000 is for Web UI, others are for OAuth callbacks (Gemini: 8085, Antigravity: 8086, Codex: 1455, Grok CLI: 56121, Kiro: 19876-19880)
- `--restart=always`: Container auto-restart policy
-- `-v "your_path:/app/configs"`: Mount configuration directory (replace "your_path" with actual path, e.g., `/home/user/aiclient-configs`)
+- `-v "your_path/configs:/app/configs"`: Mount configuration directory (replace "your_path" with actual path, e.g., `/home/user/aiclient2api`)
+- `-v "your_path/plugins:/app/src/plugins-user"`: Mount user plugins directory
- `--name aiclient2api`: Container name
#### 🐳 Docker Compose Deployment
@@ -198,13 +275,19 @@ To build from source instead of using the pre-built image, edit `docker-compose.
* **Linux/macOS**: `chmod +x install-and-run.sh && ./install-and-run.sh`
* **Windows**: Double-click `install-and-run.bat`
-> **💡 If the script fails, you can try manually installing dependencies and starting:**
+> **💡 Manual installation and startup (supports custom parameters):**
> ```bash
> npm install
+> # Default startup
> npm start
+> # Show help information
+> npm run help
+> # Show API calling examples
+> npm run example:api
+> # Backend-only mode (disable frontend management UI)
+> npm start -- --no-ui
> ```
-
#### 2. Access the console
After the server starts, open your browser and visit:
👉 [**http://localhost:3000**](http://localhost:3000)
@@ -217,7 +300,7 @@ Go to the **"Configuration"** page, you can:
* ✅ Switch default model providers in real-time
* ✅ Monitor health status and real-time request logs
-#### 4. Local Environment Preparation (Non-Docker Users)
+#### 4. Local Environment Preparation (Non-Docker Users)s
If you are running directly on your local machine (via script or Node.js) and need to bypass TLS detection for services like Grok, please ensure:
* ✅ **Install Go Language**: Go to the [official Go website](https://go.dev/) to download and install (1.20+).
* ✅ **Manually Compile Sidecar**: Execute the following command to compile the TLS proxy component:
@@ -261,7 +344,7 @@ A functional Web management interface, including:
**📊 Dashboard**: System overview, interactive routing examples, client configuration guide
-**⚙️ Configuration**: Real-time parameter modification, supporting all providers (Gemini, Antigravity, OpenAI, Claude, Kiro, Qwen), including advanced settings and file uploads
+**⚙️ Configuration**: Real-time parameter modification, supporting all providers (Gemini, Antigravity, OpenAI, Claude, Kiro), including advanced settings and file uploads
**🔗 Provider Pools**: Monitor active connections, provider health statistics, enable/disable management
@@ -278,11 +361,10 @@ Supports various input types such as images and documents, providing you with a
#### Latest Model Support
Seamlessly support the following latest large models, just configure the corresponding endpoint in Web UI or [`configs/config.json`](./configs/config.json):
-* **Grok 3 / Grok 4** - xAI's flagship models, now supported via Grok Cookie/SSO, supporting thinking models, image generation, and video generation
-* **Claude 4.5 Opus** - Anthropic's strongest model ever, now supported via Kiro, Antigravity
-* **Gemini 3 Pro** - Google's next-generation architecture preview, now supported via Gemini, Antigravity
-* **Qwen3 Coder Plus** - Alibaba Tongyi Qianwen's latest code-specific model, now supported via Qwen Code
-* **Kimi K2 / MiniMax M2** - Synchronized support for top domestic flagship models, now supported via custom OpenAI, Claude
+* **Grok / Grok Build** - xAI's flagship models, now supported via Grok Cookie/SSO and Grok CLI OAuth, supporting thinking models, Grok Build, built-in tools, image generation, and video generation
+* **Claude Opus** - Anthropic's strongest model ever, now supported via Kiro, Antigravity
+* **Gemini Pro** - Google's next-generation architecture preview, now supported via Gemini, Antigravity
+* **Kimi / MiniMax** - Synchronized support for top domestic flagship models, now supported via custom OpenAI, Claude
---
@@ -295,8 +377,8 @@ Seamlessly support the following latest large models, just configure the corresp
#### 🌐 Web UI Quick Authorization (Recommended)
In the Web UI management interface, you can complete authorization configuration rapidly:
-1. **Generate Authorization**: On the **"Provider Pools"** page or **"Configuration"** page, click the **"Generate Authorization"** button in the upper right corner of the corresponding provider (e.g., Gemini, Qwen).
-2. **Scan/Login**: An authorization dialog will pop up, you can click **"Open in Browser"** for login verification. For Qwen, just complete the web login; for Gemini and Antigravity, complete the Google account authorization.
+1. **Generate Authorization**: On the **"Provider Pools"** page or **"Configuration"** page, click the **"Generate Authorization"** button in the upper right corner of the corresponding provider (e.g., Gemini).
+2. **Scan/Login**: An authorization dialog will pop up, you can click **"Open in Browser"** for login verification. For Gemini and Antigravity, complete the Google account authorization.
3. **Auto-Save**: After successful authorization, the system will automatically obtain credentials and save them to the corresponding directory in `configs/`. You can see the newly generated credentials on the **"Config Files"** page.
4. **Visual Management**: You can upload or delete credentials at any time in the Web UI, or use the **"Quick Associate"** function to bind existing credential files to providers with one click.
@@ -310,16 +392,6 @@ In the Web UI management interface, you can complete authorization configuration
2. **Pro Member**: Antigravity is temporarily open to Pro members, you need to purchase a Pro membership first.
3. **Organization Account**: Organization accounts require separate authorization, contact the administrator to obtain authorization.
-#### Qwen Code OAuth Configuration
-1. **First Authorization**: After configuring the Qwen service, the system will automatically open the authorization page in the browser
-2. **Recommended Parameters**: Use official default parameters for best results
- ```json
- {
- "temperature": 0,
- "top_p": 1
- }
- ```
-
#### Kiro API Configuration
1. **Environment Preparation**: [Download and install Kiro client](https://kiro.dev/pricing/)
2. **Complete Authorization**: Log in to your account in the client to generate `kiro-auth-token.json` credential file
@@ -327,7 +399,7 @@ In the Web UI management interface, you can complete authorization configuration
4. **Important Notice**: Kiro service usage policy has been updated, please visit the official website for the latest usage restrictions and terms
#### Kiro Extended Thinking (Claude Models)
-AIClient-2-API supports Kiro extended thinking when using Claude-compatible requests (`/v1/messages`) or OpenAI-compatible requests (`/v1/chat/completions`) routed to `claude-kiro-oauth`.
+AIClient2API supports Kiro extended thinking when using Claude-compatible requests (`/v1/messages`) or OpenAI-compatible requests (`/v1/chat/completions`) routed to `claude-kiro-oauth`.
**Claude-compatible (`/v1/messages`)**:
```bash
@@ -372,11 +444,17 @@ Notes:
3. **Auto Save**: After successful authorization, the system automatically saves the Codex OAuth credential file
4. **Callback Port**: Ensure the OAuth callback port `1455` is not occupied
+#### Grok CLI OAuth Configuration
+1. **Generate Authorization**: On the Web UI "Provider Pools" or "Configuration" page, click the "Generate Authorization" button for Grok CLI
+2. **Browser Login**: The system opens the xAI authorization page to complete OAuth login
+3. **Auto Save**: After successful authorization, the system automatically saves the Grok CLI OAuth credential file to `configs/grok-cli/`
+4. **Callback Port**: Ensure the OAuth callback port `56121` is not occupied
+
#### Grok Cookie/SSO Configuration
1. **Obtain SSO Token**: Log in to the [Grok official website](https://grok.com/), copy the value of `sso` from Application -> Cookies in browser developer tools
2. **Enter Configuration**: In the Web UI "Configuration" page or directly modify the configuration file, enter the token into `GROK_COOKIE_TOKEN`
3. **Supported Features**:
- - Chat and Thinking models (Grok 3 Thinking)
+ - Chat and Thinking models (Grok Thinking)
- Image generation (Grok Imagine)
- Video generation (Grok Video)
4. **Notes**: Ensure `GROK_USER_AGENT` matches the browser used when obtaining the cookie to avoid being blocked
@@ -400,9 +478,9 @@ Default storage locations for authorization credential files of each service:
|------|---------|------|
| **Gemini** | `~/.gemini/oauth_creds.json` | OAuth authentication credentials |
| **Kiro** | `~/.aws/sso/cache/kiro-auth-token.json` | Kiro authentication token |
-| **Qwen** | `~/.qwen/oauth_creds.json` | Qwen OAuth credentials |
-| **Antigravity** | `~/.antigravity/oauth_creds.json` | Antigravity OAuth credentials (supports Claude 4.5 Opus) |
+| **Antigravity** | `~/.antigravity/oauth_creds.json` | Antigravity OAuth credentials (supports Claude Opus) |
| **Codex** | `~/.codex/oauth_creds.json` | Codex OAuth credentials |
+| **Grok CLI** | `configs/grok-cli/..._xai-..._oauth_creds.json` | Grok CLI OAuth credentials |
> **Note**: `~` represents the user home directory (Windows: `C:\Users\username`, Linux/macOS: `/home/username` or `/Users/username`)
@@ -441,10 +519,11 @@ This project supports flexible proxy configuration, allowing you to configure a
"gemini-cli-oauth",
"gemini-antigravity",
"claude-kiro-oauth",
- "grok-custom"
+ "grok-web"
]
-}
- ```
+ }
+ ```
+
3. **Provider-Specific Proxied Endpoints**: Some providers (like OpenAI, Claude) support configuring proxied API endpoints
@@ -579,7 +658,7 @@ For services like Grok that strictly validate TLS fingerprints (JA3/JA4), this p
**Solutions**:
- **Check Network Connection**: Ensure you can access Google, Alibaba Cloud, and other services normally
-- **Check Port Occupation**: OAuth callbacks require specific ports (Gemini: 8085, Antigravity: 8086, Codex: 1455, Kiro: 19876-19880), ensure these ports are not occupied
+- **Check Port Occupation**: OAuth callbacks require specific ports (Gemini: 8085, Antigravity: 8086, Codex: 1455, Grok CLI: 56121, Kiro: 19876-19880), ensure these ports are not occupied
- **Clear Browser Cache**: Try using incognito mode or clearing browser cache and retry
- **Check Firewall Settings**: Ensure the firewall allows access to local callback ports
- **Docker Users**: Ensure all OAuth callback ports are correctly mapped
@@ -628,6 +707,7 @@ Or modify the port configuration in `configs/config.json` to use a different por
**Solutions**:
- **Configure Account Pool**: Add multiple accounts to `provider_pools.json`, enable polling mechanism
- **Configure Fallback**: Configure `providerFallbackChain` in `config.json` for cross-type degradation
+- **Enable 429 Cooldown**: Set `RATE_LIMIT_COOLDOWN_ENABLED` to `true` and tune `RATE_LIMIT_COOLDOWN_MS` so rate-limited accounts temporarily leave the pool and recover automatically
- **Reduce Request Frequency**: Appropriately increase request intervals to avoid triggering rate limits
- **Wait for Quota Reset**: Free quotas usually reset daily or per minute
@@ -714,6 +794,25 @@ Or modify the port configuration in `configs/config.json` to use a different por
- **Check Request Frequency**: Some providers have strict request frequency limits; reduce request frequency and retry
- **View Provider Documentation**: Visit the official documentation of the corresponding provider to understand specific access restrictions and requirements
+### 14. Why should I enable "OAuth Token Auto-Refresh"?
+
+**Problem Description**: Unsure if token auto-refresh is necessary.
+
+**Solution**:
+OAuth tokens (e.g., Gemini, Antigravity, Codex) typically have a limited lifespan (e.g., 1 hour).
+- **With it enabled**: The system automatically checks and refreshes tokens before they expire in the background. This ensures 24/7 stable API service and avoids `401 Unauthorized` or `403 Forbidden` errors due to expired tokens.
+- **Without it**: Once a token expires, the system cannot automatically obtain a new one, causing API requests to fail until you manually re-authorize.
+
+### 15. What is the impact of not enabling "Preload Model Providers" on token maintenance?
+
+**Problem Description**: Confusion about the "Preload Model Providers" configuration and its relation to token refresh.
+
+**Solution**:
+The system only performs auto-refresh tasks for providers that are **loaded into the active pool**.
+- **Impact**: If a provider is not checked as a "Preload Model Provider" in the configuration, it won't be initialized when the system starts. Since it's not in the pool, the background refresh task will **not** process its token.
+- **Consequence**: If you don't use that provider for a long time, its token will expire silently. When you eventually call it via a specific route, the request will fail due to the expired token.
+- **Recommendation**: Always check providers you intend to use frequently and need to keep active in the "Preload Model Providers" list.
+
---
@@ -728,7 +827,7 @@ The development of this project was greatly inspired by the official Google Gemi
### Contributor List
-Thanks to all the developers who contributed to the AIClient-2-API project:
+Thanks to all the developers who contributed to the AIClient2API project:
[](https://github.com/justlovemaki/AIClient-2-API/graphs/contributors)
@@ -743,7 +842,7 @@ Thanks to all the developers who contributed to the AIClient-2-API project:
## ⚠️ Disclaimer
### Usage Risk Warning
-This project (AIClient-2-API) is for learning and research purposes only. Users assume all risks when using this project. The author is not responsible for any direct, indirect, or consequential losses resulting from the use of this project.
+This project (AIClient2API) is for learning and research purposes only. Users assume all risks when using this project. The author is not responsible for any direct, indirect, or consequential losses resulting from the use of this project.
### Third-Party Service Responsibility Statement
This project is an API proxy tool and does not provide any AI model services. All AI model services are provided by their respective third-party providers (such as Google, OpenAI, Anthropic, etc.). Users should comply with the terms of service and policies of each third-party service when accessing them through this project. The author is not responsible for the availability, quality, security, or legality of third-party services.
diff --git a/VERSION b/VERSION
index ea55a03fa..75a557aba 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.13.7
+3.2.2.2
\ No newline at end of file
diff --git a/configs/config.json.example b/configs/config.json.example
index 6a4bf1dd6..be9e6f8ce 100644
--- a/configs/config.json.example
+++ b/configs/config.json.example
@@ -13,9 +13,14 @@
"PROMPT_LOG_MODE": "none",
"REQUEST_MAX_RETRIES": 3,
"REQUEST_BASE_DELAY": 1000,
+ "RATE_LIMIT_COOLDOWN_ENABLED": false,
+ "RATE_LIMIT_COOLDOWN_MS": 30000,
+ "RATE_LIMIT_COOLDOWN_JITTER_MS": 5000,
+ "RATE_LIMIT_COOLDOWN_MAX_MS": 300000,
"CRON_NEAR_MINUTES": 1,
"CRON_REFRESH_TOKEN": false,
"PROVIDER_POOLS_FILE_PATH": "configs/provider_pools.json",
+ "CUSTOM_MODELS_FILE_PATH": "configs/custom_models.json",
"MAX_ERROR_COUNT": 3,
"GROK_COOKIE_TOKEN": "your-sso-cookie-token",
"GROK_CF_CLEARANCE": "your-cf-clearance-cookie",
@@ -63,5 +68,6 @@
"LOG_MAX_FILE_SIZE": 10485760,
"LOG_MAX_FILES": 10,
"TLS_SIDECAR_ENABLED": false,
- "TLS_SIDECAR_PORT": 9090
+ "TLS_SIDECAR_PORT": 9090,
+ "UI_ENABLED": true
}
diff --git a/configs/custom_models.json.example b/configs/custom_models.json.example
new file mode 100644
index 000000000..c7c48250d
--- /dev/null
+++ b/configs/custom_models.json.example
@@ -0,0 +1,35 @@
+[
+ {
+ "id": "atlas-custom-model",
+ "name": "Atlas Custom Model",
+ "alias": "gpt-4o",
+ "provider": "atlascloud",
+ "actualProvider": "atlascloud",
+ "actualModel": "gpt-4o",
+ "contextLength": 128000,
+ "temperature": 0.7
+ },
+ {
+ "id": "my-custom-gpt-4",
+ "name": "Custom GPT-4",
+ "alias": "gpt-4",
+ "provider": "openai-custom",
+ "actualProvider": "openai-custom",
+ "actualModel": "gpt-4-0613",
+ "contextLength": 8192,
+ "maxTokens": 4096,
+ "temperature": 0.7,
+ "topP": 1.0,
+ "description": "Custom configuration for GPT-4"
+ },
+ {
+ "id": "claude-3-7-sonnet-custom",
+ "name": "Claude 3.7 Sonnet Custom",
+ "alias": "claude-3-7-sonnet",
+ "provider": "claude-custom",
+ "actualProvider": "claude-kiro-oauth",
+ "actualModel": "claude-3-7-sonnet-20250219",
+ "contextLength": 200000,
+ "temperature": 0.5
+ }
+]
diff --git a/configs/market.json b/configs/market.json
new file mode 100644
index 000000000..52179c500
--- /dev/null
+++ b/configs/market.json
@@ -0,0 +1,13 @@
+[
+ {
+ "id": "ip-node-proxy",
+ "name": "IP 节点代理绑定",
+ "version": "1.0.0",
+ "minSystemVersion": "3.1.1",
+ "isPaid": true,
+ "price": "¥699",
+ "description": "按客户端 IP 为指定提供商节点1对1绑定出站代理,实现精准的链路控制和 TLS 指纹伪装覆盖。",
+ "paymentUrl": "https://pay.ldxp.cn/item/xq10cf",
+ "qrCode": "ip2node.png"
+ }
+]
\ No newline at end of file
diff --git a/configs/market.json.example b/configs/market.json.example
new file mode 100644
index 000000000..c3f35f396
--- /dev/null
+++ b/configs/market.json.example
@@ -0,0 +1,37 @@
+[
+ {
+ "id": "api-potluck",
+ "name": "API 大锅饭",
+ "version": "1.0.2",
+ "minSystemVersion": "3.0.0",
+ "description": "多用户 Key 管理系统。支持每日配额限制、Token 用量统计、自定义过期时间及独立的用户查询界面。",
+ "downloadUrl": "https://raw.githubusercontent.com/justlovemaki/AIClient-2-API/main/plugins/api-potluck.zip"
+ },
+ {
+ "id": "model-usage-stats",
+ "name": "模型用量统计",
+ "version": "1.0.0",
+ "minSystemVersion": "2.8.0",
+ "description": "全自动统计 Provider 和 Model 维度的请求次数、Token 消耗,并提供直观的可视化仪表盘和趋势图。",
+ "downloadUrl": "https://raw.githubusercontent.com/justlovemaki/AIClient-2-API/main/plugins/model-usage-stats.zip"
+ },
+ {
+ "id": "ai-monitor",
+ "name": "AI 实时监控",
+ "version": "1.0.0",
+ "minSystemVersion": "3.0.0",
+ "description": "监控系统吞吐量(QPS/TPS)、延迟和错误率,帮助您实时掌握各个节点的运行状况。",
+ "downloadUrl": "https://raw.githubusercontent.com/justlovemaki/AIClient-2-API/main/plugins/ai-monitor.zip"
+ },
+ {
+ "id": "premium-export",
+ "name": "高级数据导出插件",
+ "version": "1.2.0",
+ "minSystemVersion": "3.0.0",
+ "isPaid": true,
+ "price": "¥19.9",
+ "description": "支持将所有用量统计、监控日志导出为 Excel、PDF 及自动同步至外部数据库。",
+ "paymentUrl": "https://example.com/pay/export-plugin",
+ "qrCode": "sponsor.png"
+ }
+]
diff --git a/configs/provider_pools.json.example b/configs/provider_pools.json.example
index 98e56433f..2aa4c1f47 100644
--- a/configs/provider_pools.json.example
+++ b/configs/provider_pools.json.example
@@ -1,4 +1,21 @@
{
+ "atlascloud": [
+ {
+ "customName": "AtlasCloud节点1",
+ "OPENAI_API_KEY": "sk-atlascloud-key1",
+ "OPENAI_BASE_URL": "https://api.openai.com/v1",
+ "checkModelName": null,
+ "checkHealth": false,
+ "notSupportedModels": [],
+ "uuid": "4f579c65-d3c5-41b1-9985-9f6e3d7bf39d",
+ "isHealthy": true,
+ "isDisabled": false,
+ "lastUsed": null,
+ "usageCount": 0,
+ "errorCount": 0,
+ "lastErrorTime": null
+ }
+ ],
"openai-custom": [
{
"customName": "OpenAI节点1",
diff --git a/docker/VERSION b/docker/VERSION
new file mode 100644
index 000000000..56fea8a08
--- /dev/null
+++ b/docker/VERSION
@@ -0,0 +1 @@
+3.0.0
\ No newline at end of file
diff --git a/docker/docker-compose.build.yml b/docker/docker-compose.build.yml
index 31fa518d0..9064cb418 100644
--- a/docker/docker-compose.build.yml
+++ b/docker/docker-compose.build.yml
@@ -1,7 +1,7 @@
services:
aiclient-api:
# 方式二:从 Dockerfile 本地构建
- # 使用方法: docker compose -f docker-compose.build.yml up -d --build
+ # 使用方法: docker compose -f docker/docker-compose.build.yml -f docker/docker-compose.dev.yml up -d --build
build:
context: ..
dockerfile: Dockerfile
@@ -9,11 +9,13 @@ services:
restart: unless-stopped
ports:
- "3000:3000"
- - "8085-8087:8085-8087"
+ - "8085-8087:8085-8087"
- "1455:1455"
+ - "56121:56121"
- "19876-19880:19876-19880"
volumes:
- - ./configs:/app/configs
+ - ../configs:/app/configs
+ - ../plugins:/app/src/plugins-user
environment:
- ARGS=
healthcheck:
@@ -21,4 +23,4 @@ services:
interval: 30s
timeout: 3s
start_period: 5s
- retries: 3
\ No newline at end of file
+ retries: 3
diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml
new file mode 100644
index 000000000..4abd77a81
--- /dev/null
+++ b/docker/docker-compose.dev.yml
@@ -0,0 +1,17 @@
+# 开发模式覆盖配置 - 热重载
+# 使用方法: docker compose -f docker/docker-compose.build.yml -f docker/docker-compose.dev.yml up -d --build
+
+services:
+ aiclient-api:
+ build:
+ context: ..
+ dockerfile: Dockerfile
+ volumes:
+ # 从项目根目录挂载 configs,使用绝对路径确保正确
+ - ../configs:/app/configs
+ - ../plugins:/app/src/plugins-user
+ # 挂载源代码以支持热重载,但保留 node_modules
+ - ../src:/app/src:ro
+ - ../package.json:/app/package.json:ro
+ # 使用 shell form 以支持 $ARGS 展开
+ command: ["sh", "-c", "node --watch src/core/master.js $ARGS"]
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
index a9d2eaf22..91828182a 100644
--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
@@ -9,9 +9,11 @@ services:
- "3000:3000"
- "8085-8087:8085-8087"
- "1455:1455"
+ - "56121:56121"
- "19876-19880:19876-19880"
volumes:
- ./configs:/app/configs
+ - ./plugins:/app/src/plugins-user
environment:
- ARGS=
healthcheck:
diff --git a/docs/OPENCLAW_CONFIG_GUIDE-JA.md b/docs/OPENCLAW_CONFIG_GUIDE-JA.md
index d33f60e48..cb22b89bf 100644
--- a/docs/OPENCLAW_CONFIG_GUIDE-JA.md
+++ b/docs/OPENCLAW_CONFIG_GUIDE-JA.md
@@ -1,12 +1,12 @@
# OpenClaw 設定ガイド
-OpenClaw で AIClient-2-API を使用するためのクイック設定ガイド。
+OpenClaw で AIClient2API を使用するためのクイック設定ガイド。
---
## 前提条件
-1. AIClient-2-API サービスを起動
+1. AIClient2API サービスを起動
2. Web UI (`http://localhost:3000`) で少なくとも1つのプロバイダーを設定
3. 設定ファイルから API Key を記録
4. OpenClaw をインストール
@@ -195,7 +195,7 @@ openclaw chat --model aiclient2api/gemini-3-flash-preview "あなたの質問"
## よくある質問
**Q: 接続に失敗しますか?**
-- AIClient-2-API サービスが実行中であることを確認
+- AIClient2API サービスが実行中であることを確認
- Base URL が正しいか確認(OpenAI プロトコルには `/v1` サフィックスが必要)
- `localhost` の代わりに `127.0.0.1` を使用してみる
@@ -204,10 +204,10 @@ openclaw chat --model aiclient2api/gemini-3-flash-preview "あなたの質問"
- 環境変数 `AICLIENT2API_KEY` が設定されているか確認
**Q: モデルが利用できない?**
-- AIClient-2-API Web UI でプロバイダーが設定されているか確認
+- AIClient2API Web UI でプロバイダーが設定されているか確認
- `openclaw gateway restart` を実行してゲートウェイを再起動
- `openclaw models list` を実行してモデルリストを確認
---
-詳細については、[AIClient-2-API ドキュメント](../README-JA.md) を参照してください
+詳細については、[AIClient2API ドキュメント](../README-JA.md) を参照してください
diff --git a/docs/OPENCLAW_CONFIG_GUIDE-ZH.md b/docs/OPENCLAW_CONFIG_GUIDE-ZH.md
index e45b8cc86..d5770dcea 100644
--- a/docs/OPENCLAW_CONFIG_GUIDE-ZH.md
+++ b/docs/OPENCLAW_CONFIG_GUIDE-ZH.md
@@ -1,12 +1,12 @@
# OpenClaw 配置指南
-在 OpenClaw 中使用 AIClient-2-API 的快速配置指南。
+在 OpenClaw 中使用 AIClient2API 的快速配置指南。
---
## 前置准备
-1. 启动 AIClient-2-API 服务
+1. 启动 AIClient2API 服务
2. 在 Web UI (`http://localhost:3000`) 配置至少一个提供商
3. 记录配置文件中的 API Key
4. 安装 OpenClaw
@@ -195,7 +195,7 @@ openclaw chat --model aiclient2api/gemini-3-flash-preview "你的问题"
## 常见问题
**Q: 连接失败?**
-- 确认 AIClient-2-API 服务运行中
+- 确认 AIClient2API 服务运行中
- 检查 Base URL 是否正确(OpenAI 协议需要 `/v1` 后缀)
- 尝试使用 `127.0.0.1` 替代 `localhost`
@@ -204,10 +204,10 @@ openclaw chat --model aiclient2api/gemini-3-flash-preview "你的问题"
- 确认环境变量 `AICLIENT2API_KEY` 已设置
**Q: 模型不可用?**
-- 在 AIClient-2-API Web UI 确认已配置对应提供商
+- 在 AIClient2API Web UI 确认已配置对应提供商
- 运行 `openclaw gateway restart` 重启网关
- 运行 `openclaw models list` 验证模型列表
---
-更多信息请参考 [AIClient-2-API 文档](../README-ZH.md)
+更多信息请参考 [AIClient2API 文档](../README-ZH.md)
diff --git a/docs/OPENCLAW_CONFIG_GUIDE.md b/docs/OPENCLAW_CONFIG_GUIDE.md
index f62529e08..e016c6ce5 100644
--- a/docs/OPENCLAW_CONFIG_GUIDE.md
+++ b/docs/OPENCLAW_CONFIG_GUIDE.md
@@ -1,12 +1,12 @@
# OpenClaw Configuration Guide
-Quick configuration guide for using AIClient-2-API with OpenClaw.
+Quick configuration guide for using AIClient2API with OpenClaw.
---
## Prerequisites
-1. Start AIClient-2-API service
+1. Start AIClient2API service
2. Configure at least one provider in Web UI (`http://localhost:3000`)
3. Note the API Key from configuration file
4. Install OpenClaw
@@ -195,7 +195,7 @@ openclaw chat --model aiclient2api/gemini-3-flash-preview "your question"
## FAQ
**Q: Connection failed?**
-- Confirm AIClient-2-API service is running
+- Confirm AIClient2API service is running
- Check if Base URL is correct (OpenAI protocol needs `/v1` suffix)
- Try using `127.0.0.1` instead of `localhost`
@@ -204,10 +204,10 @@ openclaw chat --model aiclient2api/gemini-3-flash-preview "your question"
- Confirm environment variable `AICLIENT2API_KEY` is set
**Q: Model unavailable?**
-- Confirm provider is configured in AIClient-2-API Web UI
+- Confirm provider is configured in AIClient2API Web UI
- Run `openclaw gateway restart` to restart gateway
- Run `openclaw models list` to verify model list
---
-For more information, see [AIClient-2-API Documentation](../README.md)
+For more information, see [AIClient2API Documentation](../README.md)
diff --git a/docs/OPENCODE_CONFIG_EXAMPLE.md b/docs/OPENCODE_CONFIG_EXAMPLE.md
index 76016bd2c..c6a1734f9 100644
--- a/docs/OPENCODE_CONFIG_EXAMPLE.md
+++ b/docs/OPENCODE_CONFIG_EXAMPLE.md
@@ -24,19 +24,6 @@
}
}
},
- "qwen": {
- "npm": "@ai-sdk/openai-compatible",
- "name": "AIClient2API-qwen",
- "options": {
- "baseURL": "http://localhost:3000/openai-qwen-oauth/v1",
- "apiKey": "123456"
- },
- "models": {
- "qwen3-coder-plus": {
- "name": "Qwen3 Coder Plus Openai "
- }
- }
- },
"gemini-antigravity": {
"npm": "@ai-sdk/google",
"name": "AIClient2API-antigravity",
@@ -83,12 +70,12 @@
## 配置重点解释
### 1. `provider` (服务提供商配置)
-这是配置的核心部分,每个键(如 `kiro`, `qwen`, `gemini-cli`)代表一个独立的服务提供商实例。
+这是配置的核心部分,每个键(如 `kiro`, `gemini-cli`)代表一个独立的服务提供商实例。
* **`npm` (SDK 适配器)**:
* 指定底层使用的 AI SDK。例如:
* `@ai-sdk/anthropic`: 用于 Anthropic (Claude) 系列模型。
- * `@ai-sdk/openai-compatible`: 用于兼容 OpenAI 接口标准的模型(如通义千问 Qwen)。
+ * `@ai-sdk/openai-compatible`: 用于兼容 OpenAI 接口标准的模型。
* `@ai-sdk/google`: 用于 Google Gemini 系列模型。
* **重点**: 必须确保 `npm` 字段与您要使用的模型协议匹配,否则会导致连接失败。
diff --git a/docs/skills/aiclient-cli-usage.md b/docs/skills/aiclient-cli-usage.md
new file mode 100644
index 000000000..15698ecbe
--- /dev/null
+++ b/docs/skills/aiclient-cli-usage.md
@@ -0,0 +1,89 @@
+---
+name: aiclient-cli-usage
+description: Use when an agent needs to understand, configure, or call the AIClient2API service via its CLI tools or REST APIs.
+---
+
+# AIClient2API CLI & API Usage Skill
+
+## Overview
+This skill provides instructions for AI agents to interact with the AIClient2API service. It covers how to discover available commands and navigate the exhaustive API surface using both local CLI and remote REST interfaces.
+
+## Self-Discovery Modes
+
+**CRITICAL**: Regardless of how you access the service, using REST APIs (for management or AI tasks) requires the `Server Address` and appropriate credentials. **Always ask the user for the `Server Address` (use `http://localhost:3000` as default for local) and `Admin Password` before attempting API-based tasks.**
+
+### 1. Local Mode (CLI & REST API)
+Use these when you have shell access to the server environment. In this mode, you can use both local CLI tools and REST APIs:
+- **CLI Help**: `npm run help` (Add `--json` for structured data)
+- **CLI API Guide**: `npm run example:api` (Add `--json` for structured data)
+- **REST API**: You can access all endpoints via `http://localhost:` (Default: 3000).
+
+### 2. Remote Mode (REST API Only)
+Use these when interacting with a running instance over the network without shell access:
+- **Help JSON**: `GET /api/help` (Public, No Auth)
+- **API Guide JSON**: `GET /api/example` (Public, No Auth)
+- **REST API**: Use the user-provided `Server Address`.
+
+## When to Use
+- When you need to understand, configure, or call the AIClient2API service.
+- To programmatically manage model providers or account pools.
+- To monitor system health, logs, or usage.
+
+## Core API Categories (AI vs. Management)
+
+### 0. Public Endpoints (No Auth Required)
+Use these for self-discovery or system monitoring:
+- `GET /api/help`: Get full API help documentation (JSON).
+- `GET /api/example`: Get API calling examples (JSON).
+- `GET /provider_health`: Detailed health status of all model providers.
+- `POST /api/login`: Exchange `Admin Password` for a dynamic `Token`.
+
+### 1. AI Business Path (`/v1/*`, `/v1beta/*`, `/count_tokens`)
+- **Purpose**: AI model inference (chat, image, token counting).
+- **Auth**: Static `API Key`. **Ask the user for this if not provided.**
+- **Header**: `Authorization: Bearer `
+
+### 2. Management Path (`/api/*`, `/health`, `/provider_health`)
+- **Purpose**: Server config, node pool management, logs, stats.
+- **Auth**: Dynamic `Token` via `/api/login`. **Ask the user for the `Server Address` and `Admin Password`.**
+- **Header**: `Authorization: Bearer ` (Except for `/api/login` and public health checks).
+
+## Advanced Patterns
+
+### Path Routing
+Force a specific provider by prefixing the AI business path:
+- `/gemini-cli-oauth/v1/chat/completions`
+- `/claude-custom/v1/messages`
+
+### Real-time Logs (SSE)
+Subscribe to `GET /api/events` via `EventSource` for live system output.
+
+## Quick Reference
+
+| Mode | Method | Command/Endpoint | Format |
+|------|--------|------------------|--------|
+| Local | CLI | `npm run help -- --json` | JSON |
+| Local | CLI | `npm run example:api` | Text |
+| Local | REST | `GET http://localhost:3000/api/help` | JSON |
+| Remote | REST | `GET /api/help` | JSON |
+| Remote | REST | `GET /api/example?format=text` | Text |
+
+## Common Mistakes
+- **Wrong Key**: Using the static AI Key for management APIs (causes 401).
+- **No Login**: Attempting to fetch `/api/config` without first calling `/api/login`.
+- **Format Mismatch**: Expecting JSON from a CLI command without the `--json` flag.
+
+## Code Example: Management API Flow
+```javascript
+// 1. Login to get token
+const login = await fetch('/api/login', {
+ method: 'POST',
+ body: JSON.stringify({ password: 'admin' })
+});
+const { token } = await login.json();
+
+// 2. Use token for management
+const nodes = await fetch('/api/providers', {
+ headers: { 'Authorization': `Bearer ${token}` }
+});
+```
diff --git a/package-lock.json b/package-lock.json
index 8e81846b3..70ae2aee9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,21 +1,28 @@
{
- "name": "AIClient2API",
+ "name": "aiclient2api",
+ "version": "3.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
+ "name": "aiclient2api",
+ "version": "3.0.0",
"dependencies": {
+ "@ai-sdk/openai": "^3.0.61",
"@anthropic-ai/tokenizer": "^0.0.4",
"adm-zip": "^0.5.16",
- "axios": "^1.10.0",
+ "ai": "^6.0.175",
+ "axios": "^1.14.0",
+ "busboy": "^1.6.0",
"deepmerge": "^4.3.1",
- "dotenv": "^16.4.5",
+ "dotenv": "^16.6.1",
"google-auth-library": "^10.1.0",
"http-proxy-agent": "^7.0.2",
"https-proxy-agent": "^7.0.6",
"lodash": "^4.17.21",
"multer": "^2.0.2",
"open": "^10.2.0",
+ "openai": "^6.36.0",
"socks-proxy-agent": "^8.0.5",
"undici": "^7.12.0",
"uuid": "^11.1.0",
@@ -31,6 +38,68 @@
"supertest": "^6.3.3"
}
},
+ "node_modules/@ai-sdk/gateway": {
+ "version": "3.0.110",
+ "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-3.0.110.tgz",
+ "integrity": "sha512-sbv8+1L9/BRKydn8dMNwoMQKupA4iLJ9N+yvxgW6wMQ/94UepDf3FeYWMj/dLdzolAHZ6izRUP4s5WqQkmJ2Zg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@ai-sdk/provider": "3.0.10",
+ "@ai-sdk/provider-utils": "4.0.26",
+ "@vercel/oidc": "3.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.76 || ^4.1.8"
+ }
+ },
+ "node_modules/@ai-sdk/openai": {
+ "version": "3.0.61",
+ "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-3.0.61.tgz",
+ "integrity": "sha512-L5FdlTo+7IBOdXbnTZqVzowRdLGWnxPn01vHe4leLPp0G4ydoXhySu0tp0ttkoO0ZSzPjHo7iiAkYoT0J91Q8Q==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@ai-sdk/provider": "3.0.10",
+ "@ai-sdk/provider-utils": "4.0.26"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.76 || ^4.1.8"
+ }
+ },
+ "node_modules/@ai-sdk/provider": {
+ "version": "3.0.10",
+ "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.10.tgz",
+ "integrity": "sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "json-schema": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@ai-sdk/provider-utils": {
+ "version": "4.0.26",
+ "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.26.tgz",
+ "integrity": "sha512-CsKNLKsOpvPujRlIYvoz+Ybw+kGn7J4/fIZa/58+R7iWLLfwn6ifE2G6Yq8K9XvH/I/3bzaDAJ3NhRwEMsLBKQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@ai-sdk/provider": "3.0.10",
+ "@standard-schema/spec": "^1.1.0",
+ "eventsource-parser": "^3.0.8"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.76 || ^4.1.8"
+ }
+ },
"node_modules/@ampproject/remapping": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
@@ -71,13 +140,13 @@
"license": "MIT"
},
"node_modules/@babel/code-frame": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
- "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
+ "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/helper-validator-identifier": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.29.7",
"js-tokens": "^4.0.0",
"picocolors": "^1.1.1"
},
@@ -127,14 +196,14 @@
}
},
"node_modules/@babel/generator": {
- "version": "7.28.0",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz",
- "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
+ "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.28.0",
- "@babel/types": "^7.28.0",
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7",
"@jridgewell/gen-mapping": "^0.3.12",
"@jridgewell/trace-mapping": "^0.3.28",
"jsesc": "^3.0.2"
@@ -231,9 +300,9 @@
}
},
"node_modules/@babel/helper-globals": {
- "version": "7.28.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
- "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
+ "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -255,29 +324,29 @@
}
},
"node_modules/@babel/helper-module-imports": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
- "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
+ "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/traverse": "^7.27.1",
- "@babel/types": "^7.27.1"
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-transforms": {
- "version": "7.27.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz",
- "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
+ "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/helper-module-imports": "^7.27.1",
- "@babel/helper-validator-identifier": "^7.27.1",
- "@babel/traverse": "^7.27.3"
+ "@babel/helper-module-imports": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "@babel/traverse": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
@@ -300,9 +369,9 @@
}
},
"node_modules/@babel/helper-plugin-utils": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
- "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz",
+ "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -360,9 +429,9 @@
}
},
"node_modules/@babel/helper-string-parser": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
- "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+ "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -370,9 +439,9 @@
}
},
"node_modules/@babel/helper-validator-identifier": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
- "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+ "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -419,13 +488,13 @@
}
},
"node_modules/@babel/parser": {
- "version": "7.28.0",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz",
- "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
+ "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/types": "^7.28.0"
+ "@babel/types": "^7.29.7"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -1225,16 +1294,16 @@
}
},
"node_modules/@babel/plugin-transform-modules-systemjs": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz",
- "integrity": "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.7.tgz",
+ "integrity": "sha512-TM2ZcQLoG2/y4HODiStCo10DibYhWhGWAwVv+EQKmG/7GFl0N+AAmUiXOMKM+aiJ9XBJ9AHVZBvTzMnJ2sM3cQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/helper-module-transforms": "^7.27.1",
- "@babel/helper-plugin-utils": "^7.27.1",
- "@babel/helper-validator-identifier": "^7.27.1",
- "@babel/traverse": "^7.27.1"
+ "@babel/helper-module-transforms": "^7.29.7",
+ "@babel/helper-plugin-utils": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "@babel/traverse": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1760,33 +1829,33 @@
}
},
"node_modules/@babel/template": {
- "version": "7.27.2",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
- "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
+ "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.27.1",
- "@babel/parser": "^7.27.2",
- "@babel/types": "^7.27.1"
+ "@babel/code-frame": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
- "version": "7.28.0",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz",
- "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz",
+ "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.27.1",
- "@babel/generator": "^7.28.0",
- "@babel/helper-globals": "^7.28.0",
- "@babel/parser": "^7.28.0",
- "@babel/template": "^7.27.2",
- "@babel/types": "^7.28.0",
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.7",
+ "@babel/helper-globals": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.7",
"debug": "^4.3.1"
},
"engines": {
@@ -1794,14 +1863,14 @@
}
},
"node_modules/@babel/types": {
- "version": "7.28.2",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz",
- "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
+ "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/helper-string-parser": "^7.27.1",
- "@babel/helper-validator-identifier": "^7.27.1"
+ "@babel/helper-string-parser": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
@@ -2209,6 +2278,15 @@
"url": "https://paulmillr.com/funding/"
}
},
+ "node_modules/@opentelemetry/api": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
+ "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
"node_modules/@paralleldrive/cuid2": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz",
@@ -2246,6 +2324,12 @@
"@sinonjs/commons": "^3.0.0"
}
},
+ "node_modules/@standard-schema/spec": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
+ "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
+ "license": "MIT"
+ },
"node_modules/@types/babel__core": {
"version": "7.20.5",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
@@ -2369,6 +2453,15 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/@vercel/oidc": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@vercel/oidc/-/oidc-3.2.0.tgz",
+ "integrity": "sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 20"
+ }
+ },
"node_modules/adm-zip": {
"version": "0.5.16",
"resolved": "https://registry.npmmirror.com/adm-zip/-/adm-zip-0.5.16.tgz",
@@ -2387,6 +2480,24 @@
"node": ">= 14"
}
},
+ "node_modules/ai": {
+ "version": "6.0.175",
+ "resolved": "https://registry.npmjs.org/ai/-/ai-6.0.175.tgz",
+ "integrity": "sha512-6fFFHzbh6FIZnYc31V6osOxq25ABJYCShfG0O6ajHiA4FB/DgnPi1mP8cO5aAU3HNSbQHiMazdlh9bIsp97mVA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@ai-sdk/gateway": "3.0.110",
+ "@ai-sdk/provider": "3.0.10",
+ "@ai-sdk/provider-utils": "4.0.26",
+ "@opentelemetry/api": "1.9.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.76 || ^4.1.8"
+ }
+ },
"node_modules/ansi-escapes": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
@@ -2473,14 +2584,14 @@
"license": "MIT"
},
"node_modules/axios": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz",
- "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==",
+ "version": "1.16.0",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.0.tgz",
+ "integrity": "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==",
"license": "MIT",
"dependencies": {
- "follow-redirects": "^1.15.6",
- "form-data": "^4.0.0",
- "proxy-from-env": "^1.1.0"
+ "follow-redirects": "^1.16.0",
+ "form-data": "^4.0.5",
+ "proxy-from-env": "^2.1.0"
}
},
"node_modules/babel-jest": {
@@ -2675,9 +2786,9 @@
}
},
"node_modules/babel-jest/node_modules/picomatch": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
- "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2916,9 +3027,9 @@
}
},
"node_modules/brace-expansion": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
- "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
+ "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3011,7 +3122,7 @@
},
"node_modules/busboy": {
"version": "1.6.0",
- "resolved": "https://registry.npmmirror.com/busboy/-/busboy-1.6.0.tgz",
+ "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
"dependencies": {
"streamsearch": "^1.1.0"
@@ -3594,6 +3705,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/eventsource-parser": {
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.8.tgz",
+ "integrity": "sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
"node_modules/execa": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
@@ -3725,9 +3845,9 @@
}
},
"node_modules/follow-redirects": {
- "version": "1.15.9",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
- "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
+ "version": "1.16.0",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
+ "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
"funding": [
{
"type": "individual",
@@ -3745,9 +3865,9 @@
}
},
"node_modules/form-data": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
- "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+ "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
@@ -4149,9 +4269,9 @@
"license": "ISC"
},
"node_modules/ip-address": {
- "version": "10.1.0",
- "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
- "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
+ "version": "10.2.0",
+ "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz",
+ "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==",
"license": "MIT",
"engines": {
"node": ">= 12"
@@ -5021,9 +5141,9 @@
"license": "MIT"
},
"node_modules/js-yaml": {
- "version": "3.14.1",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
- "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+ "version": "3.14.2",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
+ "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5063,6 +5183,12 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/json-schema": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
+ "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
+ "license": "(AFL-2.1 OR BSD-3-Clause)"
+ },
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
@@ -5088,12 +5214,12 @@
}
},
"node_modules/jws": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
- "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
+ "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
"license": "MIT",
"dependencies": {
- "jwa": "^2.0.0",
+ "jwa": "^2.0.1",
"safe-buffer": "^5.0.1"
}
},
@@ -5138,9 +5264,9 @@
}
},
"node_modules/lodash": {
- "version": "4.17.21",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
- "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
+ "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
"license": "MIT"
},
"node_modules/lodash.debounce": {
@@ -5293,9 +5419,9 @@
}
},
"node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -5305,27 +5431,6 @@
"node": "*"
}
},
- "node_modules/minimist": {
- "version": "1.2.8",
- "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz",
- "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/mkdirp": {
- "version": "0.5.6",
- "resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-0.5.6.tgz",
- "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
- "license": "MIT",
- "dependencies": {
- "minimist": "^1.2.6"
- },
- "bin": {
- "mkdirp": "bin/cmd.js"
- }
- },
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -5333,21 +5438,22 @@
"license": "MIT"
},
"node_modules/multer": {
- "version": "2.0.2",
- "resolved": "https://registry.npmmirror.com/multer/-/multer-2.0.2.tgz",
- "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==",
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/multer/-/multer-2.1.1.tgz",
+ "integrity": "sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==",
"license": "MIT",
"dependencies": {
"append-field": "^1.0.0",
"busboy": "^1.6.0",
"concat-stream": "^2.0.0",
- "mkdirp": "^0.5.6",
- "object-assign": "^4.1.1",
- "type-is": "^1.6.18",
- "xtend": "^4.0.2"
+ "type-is": "^1.6.18"
},
"engines": {
"node": ">= 10.16.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
}
},
"node_modules/natural-compare": {
@@ -5432,15 +5538,6 @@
"node": ">=8"
}
},
- "node_modules/object-assign": {
- "version": "4.1.1",
- "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz",
- "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/object-inspect": {
"version": "1.13.4",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
@@ -5498,6 +5595,27 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/openai": {
+ "version": "6.36.0",
+ "resolved": "https://registry.npmjs.org/openai/-/openai-6.36.0.tgz",
+ "integrity": "sha512-Has2YbIusMq9wQEierFsgf9c783dy1y9arX459LmphNacEkkM5yxi2RIyXP0LmkOroQyW19iTwALHL8Yf26UKA==",
+ "license": "Apache-2.0",
+ "bin": {
+ "openai": "bin/cli"
+ },
+ "peerDependencies": {
+ "ws": "^8.18.0",
+ "zod": "^3.25 || ^4.0"
+ },
+ "peerDependenciesMeta": {
+ "ws": {
+ "optional": true
+ },
+ "zod": {
+ "optional": true
+ }
+ }
+ },
"node_modules/p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
@@ -5617,9 +5735,9 @@
"license": "ISC"
},
"node_modules/picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+ "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -5695,10 +5813,13 @@
}
},
"node_modules/proxy-from-env": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
- "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
- "license": "MIT"
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
+ "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
},
"node_modules/pure-rand": {
"version": "6.1.0",
@@ -5718,9 +5839,9 @@
"license": "MIT"
},
"node_modules/qs": {
- "version": "6.14.0",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
- "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
+ "version": "6.15.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz",
+ "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
@@ -6393,9 +6514,9 @@
"license": "MIT"
},
"node_modules/undici": {
- "version": "7.12.0",
- "resolved": "https://registry.npmjs.org/undici/-/undici-7.12.0.tgz",
- "integrity": "sha512-GrKEsc3ughskmGA9jevVlIOPMiiAHJ4OFUtaAH+NhfTUSiZ1wMPIQqQvAJUrJspFXJt3EBWgpAeoHEDVT1IBug==",
+ "version": "7.26.0",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-7.26.0.tgz",
+ "integrity": "sha512-3O9Tf67pGhgOv9jM35AbhkXAKi13f3oy3aE4CSgr+TckGeY+/iu97ZXN+J7DpHPzLbVApFd1IFhcnBjREYXYcg==",
"license": "MIT",
"engines": {
"node": ">=20.18.1"
@@ -6490,9 +6611,9 @@
"license": "MIT"
},
"node_modules/uuid": {
- "version": "11.1.0",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
- "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
+ "version": "11.1.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.1.tgz",
+ "integrity": "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
@@ -6592,9 +6713,9 @@
}
},
"node_modules/ws": {
- "version": "8.19.0",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
- "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
+ "version": "8.21.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz",
+ "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
@@ -6627,15 +6748,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/xtend": {
- "version": "4.0.2",
- "resolved": "https://registry.npmmirror.com/xtend/-/xtend-4.0.2.tgz",
- "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.4"
- }
- },
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
@@ -6694,6 +6806,16 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
+ },
+ "node_modules/zod": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz",
+ "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==",
+ "license": "MIT",
+ "peer": true,
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
}
}
}
diff --git a/package.json b/package.json
index 5b1b23bc9..fcffc5c23 100644
--- a/package.json
+++ b/package.json
@@ -1,17 +1,23 @@
{
+ "name": "aiclient2api",
+ "version": "3.0.0",
"type": "module",
"dependencies": {
+ "@ai-sdk/openai": "^3.0.61",
"@anthropic-ai/tokenizer": "^0.0.4",
"adm-zip": "^0.5.16",
+ "ai": "^6.0.175",
"axios": "^1.14.0",
+ "busboy": "^1.6.0",
"deepmerge": "^4.3.1",
- "dotenv": "^16.4.5",
+ "dotenv": "^16.6.1",
"google-auth-library": "^10.1.0",
"http-proxy-agent": "^7.0.2",
"https-proxy-agent": "^7.0.6",
"lodash": "^4.17.21",
"multer": "^2.0.2",
"open": "^10.2.0",
+ "openai": "^6.36.0",
"socks-proxy-agent": "^8.0.5",
"undici": "^7.12.0",
"uuid": "^11.1.0",
@@ -37,6 +43,8 @@
"test:silent": "jest --silent",
"test:unit": "node run-tests.js --unit",
"test:integration": "node run-tests.js --integration",
- "test:summary": "node test-summary.js"
+ "test:summary": "node test-summary.js",
+ "help": "node src/scripts/help.js",
+ "example:api": "node src/scripts/example-api.js"
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 3da5f0706..a5bc0ed91 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -8,20 +8,29 @@ importers:
.:
dependencies:
+ '@ai-sdk/openai':
+ specifier: ^3.0.61
+ version: 3.0.63(zod@4.4.3)
'@anthropic-ai/tokenizer':
specifier: ^0.0.4
version: 0.0.4
adm-zip:
specifier: ^0.5.16
version: 0.5.16
+ ai:
+ specifier: ^6.0.175
+ version: 6.0.177(zod@4.4.3)
axios:
specifier: ^1.14.0
version: 1.14.0
+ busboy:
+ specifier: ^1.6.0
+ version: 1.6.0
deepmerge:
specifier: ^4.3.1
version: 4.3.1
dotenv:
- specifier: ^16.4.5
+ specifier: ^16.6.1
version: 16.6.1
google-auth-library:
specifier: ^10.1.0
@@ -41,6 +50,9 @@ importers:
open:
specifier: ^10.2.0
version: 10.2.0
+ openai:
+ specifier: ^6.36.0
+ version: 6.37.0(ws@8.19.0)(zod@4.4.3)
socks-proxy-agent:
specifier: ^8.0.5
version: 8.0.5
@@ -78,6 +90,28 @@ importers:
packages:
+ '@ai-sdk/gateway@3.0.112':
+ resolution: {integrity: sha512-jiBao9pR4owWyjo0BnuNc7WSQBGOD0thysE4AFgZXaG+zMFbISQXUkJr7ePw/phBvePy7jE5FSA2Lf7lwqUiiQ==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ zod: ^3.25.76 || ^4.1.8
+
+ '@ai-sdk/openai@3.0.63':
+ resolution: {integrity: sha512-4yY/m8a57MNNVoJCsXuNblKf6BO4yuAuLKRX4tzSNffBEBSp1FlcWdPE0Z4FkqUeS0AJhYSSqp0GIiA/cIcDNA==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ zod: ^3.25.76 || ^4.1.8
+
+ '@ai-sdk/provider-utils@4.0.27':
+ resolution: {integrity: sha512-ubkAJ+xODouwtmN1tYlvTPphH1hPOBfZaEQe8U7skGvFAnIRs9PPpsq57bC2+Ky/MB4yzhd6YOsxTAx9sGpazw==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ zod: ^3.25.76 || ^4.1.8
+
+ '@ai-sdk/provider@3.0.10':
+ resolution: {integrity: sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw==}
+ engines: {node: '>=18'}
+
'@anthropic-ai/tokenizer@0.0.4':
resolution: {integrity: sha512-EHRKbxlxlc8W4KCBEseByJ7YwyYCmgu9OyN59H9+IYIGPoKv8tXyQXinkeGDI+cI8Tiuz9wk2jZb/kK7AyvL7g==}
@@ -774,6 +808,10 @@ packages:
resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==}
engines: {node: ^14.21.3 || >=16}
+ '@opentelemetry/api@1.9.0':
+ resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==}
+ engines: {node: '>=8.0.0'}
+
'@paralleldrive/cuid2@2.3.1':
resolution: {integrity: sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==}
@@ -793,6 +831,9 @@ packages:
'@sinonjs/fake-timers@10.3.0':
resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==}
+ '@standard-schema/spec@1.1.0':
+ resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
+
'@types/babel__core@7.20.5':
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
@@ -834,6 +875,11 @@ packages:
'@ungap/structured-clone@1.3.0':
resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
+ deprecated: Potential CWE-502 - Update to 1.3.1 or higher
+
+ '@vercel/oidc@3.2.0':
+ resolution: {integrity: sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==}
+ engines: {node: '>= 20'}
adm-zip@0.5.16:
resolution: {integrity: sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==}
@@ -843,6 +889,12 @@ packages:
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
engines: {node: '>= 14'}
+ ai@6.0.177:
+ resolution: {integrity: sha512-1xQtbeWwNcLyyM86ixZhkKvT+WRXc1lvarIKqPVtsyn8F9NDikwUMBqYu+aQKDgMht50SMXh4qboYuU8MeHZZA==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ zod: ^3.25.76 || ^4.1.8
+
ansi-escapes@4.3.2:
resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==}
engines: {node: '>=8'}
@@ -1203,6 +1255,10 @@ packages:
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
engines: {node: '>=0.10.0'}
+ eventsource-parser@3.0.8:
+ resolution: {integrity: sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ==}
+ engines: {node: '>=18.0.0'}
+
execa@5.1.1:
resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
engines: {node: '>=10'}
@@ -1615,6 +1671,9 @@ packages:
json-parse-even-better-errors@2.3.1:
resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
+ json-schema@0.4.0:
+ resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==}
+
json5@2.2.3:
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
engines: {node: '>=6'}
@@ -1766,6 +1825,18 @@ packages:
resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==}
engines: {node: '>=18'}
+ openai@6.37.0:
+ resolution: {integrity: sha512-0H5dEGFmmLv6KSd0W1w2nyL8WsLkX6yoLeQpU+dZAOuGcany5qkYQMmj35ZrKgb6yiyYqpUzFOpR8mZQkgqeEQ==}
+ hasBin: true
+ peerDependencies:
+ ws: ^8.18.0
+ zod: ^3.25 || ^4.0
+ peerDependenciesMeta:
+ ws:
+ optional: true
+ zod:
+ optional: true
+
p-limit@2.3.0:
resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
engines: {node: '>=6'}
@@ -2182,8 +2253,35 @@ packages:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
+ zod@4.4.3:
+ resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==}
+
snapshots:
+ '@ai-sdk/gateway@3.0.112(zod@4.4.3)':
+ dependencies:
+ '@ai-sdk/provider': 3.0.10
+ '@ai-sdk/provider-utils': 4.0.27(zod@4.4.3)
+ '@vercel/oidc': 3.2.0
+ zod: 4.4.3
+
+ '@ai-sdk/openai@3.0.63(zod@4.4.3)':
+ dependencies:
+ '@ai-sdk/provider': 3.0.10
+ '@ai-sdk/provider-utils': 4.0.27(zod@4.4.3)
+ zod: 4.4.3
+
+ '@ai-sdk/provider-utils@4.0.27(zod@4.4.3)':
+ dependencies:
+ '@ai-sdk/provider': 3.0.10
+ '@standard-schema/spec': 1.1.0
+ eventsource-parser: 3.0.8
+ zod: 4.4.3
+
+ '@ai-sdk/provider@3.0.10':
+ dependencies:
+ json-schema: 0.4.0
+
'@anthropic-ai/tokenizer@0.0.4':
dependencies:
'@types/node': 18.19.130
@@ -3164,6 +3262,8 @@ snapshots:
'@noble/hashes@1.8.0': {}
+ '@opentelemetry/api@1.9.0': {}
+
'@paralleldrive/cuid2@2.3.1':
dependencies:
'@noble/hashes': 1.8.0
@@ -3183,6 +3283,8 @@ snapshots:
dependencies:
'@sinonjs/commons': 3.0.1
+ '@standard-schema/spec@1.1.0': {}
+
'@types/babel__core@7.20.5':
dependencies:
'@babel/parser': 7.29.0
@@ -3236,10 +3338,20 @@ snapshots:
'@ungap/structured-clone@1.3.0': {}
+ '@vercel/oidc@3.2.0': {}
+
adm-zip@0.5.16: {}
agent-base@7.1.4: {}
+ ai@6.0.177(zod@4.4.3):
+ dependencies:
+ '@ai-sdk/gateway': 3.0.112(zod@4.4.3)
+ '@ai-sdk/provider': 3.0.10
+ '@ai-sdk/provider-utils': 4.0.27(zod@4.4.3)
+ '@opentelemetry/api': 1.9.0
+ zod: 4.4.3
+
ansi-escapes@4.3.2:
dependencies:
type-fest: 0.21.3
@@ -3612,6 +3724,8 @@ snapshots:
esutils@2.0.3: {}
+ eventsource-parser@3.0.8: {}
+
execa@5.1.1:
dependencies:
cross-spawn: 7.0.6
@@ -4253,6 +4367,8 @@ snapshots:
json-parse-even-better-errors@2.3.1: {}
+ json-schema@0.4.0: {}
+
json5@2.2.3: {}
jwa@2.0.1:
@@ -4384,6 +4500,11 @@ snapshots:
is-inside-container: 1.0.0
wsl-utils: 0.1.0
+ openai@6.37.0(ws@8.19.0)(zod@4.4.3):
+ optionalDependencies:
+ ws: 8.19.0
+ zod: 4.4.3
+
p-limit@2.3.0:
dependencies:
p-try: 2.2.0
@@ -4766,3 +4887,5 @@ snapshots:
yargs-parser: 21.1.1
yocto-queue@0.1.0: {}
+
+ zod@4.4.3: {}
diff --git a/src/auth/codex-import-normalizer.js b/src/auth/codex-import-normalizer.js
new file mode 100644
index 000000000..c2de533dd
--- /dev/null
+++ b/src/auth/codex-import-normalizer.js
@@ -0,0 +1,208 @@
+function isPlainObject(value) {
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
+}
+
+function cleanString(value) {
+ return typeof value === 'string' ? value.trim() : '';
+}
+
+function parseJwtPayload(token) {
+ const tokenValue = cleanString(token);
+ if (!tokenValue) return null;
+
+ const parts = tokenValue.split('.');
+ if (parts.length !== 3) return null;
+
+ try {
+ const payload = Buffer.from(parts[1], 'base64url').toString('utf8');
+ return JSON.parse(payload);
+ } catch {
+ return null;
+ }
+}
+
+function getAuthClaims(claims) {
+ return claims?.['https://api.openai.com/auth'] || {};
+}
+
+function getProfileClaims(claims) {
+ return claims?.['https://api.openai.com/profile'] || {};
+}
+
+function expiryFromValues({ expired, expiresAt, expires_at, expires_in, claims }) {
+ const explicit = expired || expiresAt;
+ if (explicit) {
+ const date = new Date(explicit);
+ if (!Number.isNaN(date.getTime())) return date.toISOString();
+ }
+
+ if (expires_at) {
+ const timestamp = Number(expires_at);
+ if (Number.isFinite(timestamp)) {
+ const milliseconds = timestamp > 1000000000000 ? timestamp : timestamp * 1000;
+ const date = new Date(milliseconds);
+ if (!Number.isNaN(date.getTime())) return date.toISOString();
+ }
+ }
+
+ if (expires_in) {
+ const seconds = Number(expires_in);
+ if (Number.isFinite(seconds) && seconds > 0) {
+ return new Date(Date.now() + seconds * 1000).toISOString();
+ }
+ }
+
+ if (claims?.exp) {
+ const exp = Number(claims.exp);
+ if (Number.isFinite(exp)) {
+ return new Date(exp * 1000).toISOString();
+ }
+ }
+
+ return new Date(Date.now() + 3600 * 1000).toISOString();
+}
+
+function makeFallbackEmail(accountId, index) {
+ if (accountId) return `codex-${accountId}`;
+ return `codex-import-${index}`;
+}
+
+function normalizeCredential(raw, source, index, exportedAt = null) {
+ if (!isPlainObject(raw)) {
+ return {
+ error: '凭据必须是 JSON 对象',
+ source,
+ index
+ };
+ }
+
+ const nestedCredentials = isPlainObject(raw.credentials) ? raw.credentials : {};
+ const nestedExtra = isPlainObject(raw.extra) ? raw.extra : {};
+ const data = { ...raw, ...nestedCredentials };
+
+ const accessToken = cleanString(data.access_token);
+ if (!accessToken) {
+ return {
+ error: '缺少 access_token',
+ source,
+ index,
+ email: cleanString(raw.email || raw.name || nestedExtra.email)
+ };
+ }
+
+ const idToken = cleanString(data.id_token);
+ const accessClaims = parseJwtPayload(accessToken);
+ const idClaims = parseJwtPayload(idToken);
+ const claims = idClaims || accessClaims || {};
+ const authClaims = getAuthClaims(claims);
+ const profileClaims = getProfileClaims(claims);
+
+ const accountId = cleanString(
+ data.account_id ||
+ data.chatgpt_account_id ||
+ authClaims.chatgpt_account_id ||
+ claims.sub
+ );
+ const email = cleanString(
+ raw.email ||
+ nestedExtra.email ||
+ raw.name ||
+ data.email ||
+ profileClaims.email ||
+ claims.email
+ ) || makeFallbackEmail(accountId, index);
+
+ if (!accountId) {
+ return {
+ error: '无法获取 account_id 或 chatgpt_account_id',
+ source,
+ index,
+ email
+ };
+ }
+
+ const refreshToken = cleanString(data.refresh_token);
+ const expired = expiryFromValues({
+ expired: data.expired,
+ expiresAt: data.expiresAt,
+ expires_at: data.expires_at,
+ expires_in: data.expires_in,
+ claims
+ });
+
+ return {
+ source,
+ id_token: idToken,
+ access_token: accessToken,
+ refresh_token: refreshToken,
+ account_id: accountId,
+ chatgpt_account_id: accountId,
+ email,
+ name: email,
+ type: 'codex',
+ last_refresh: cleanString(data.last_refresh || exportedAt) || new Date().toISOString(),
+ expired,
+ access_token_only: !refreshToken
+ };
+}
+
+export function normalizeCpaCodexCredentials(payload) {
+ const items = Array.isArray(payload) ? payload : [payload];
+ return items.map((item, index) => normalizeCredential(item, 'cpa', index + 1));
+}
+
+export function normalizeSub2ApiCodexCredentials(payload) {
+ let accounts;
+ let exportedAt = null;
+
+ if (Array.isArray(payload)) {
+ accounts = payload;
+ } else if (isPlainObject(payload) && Array.isArray(payload.accounts)) {
+ accounts = payload.accounts;
+ exportedAt = payload.exported_at || null;
+ } else if (isPlainObject(payload) && isPlainObject(payload.credentials)) {
+ accounts = [payload];
+ } else {
+ throw new Error('sub2api 数据必须是完整导出对象、账号数组或单个账号对象');
+ }
+
+ const results = [];
+
+ accounts.forEach((account, index) => {
+ if (!isPlainObject(account)) {
+ results.push({
+ error: '账号条目必须是 JSON 对象',
+ source: 'sub2api',
+ index: index + 1
+ });
+ return;
+ }
+
+ if (account.platform && account.platform !== 'openai') {
+ results.push({
+ skipped: true,
+ source: 'sub2api',
+ index: index + 1,
+ email: cleanString(account.extra?.email || account.name),
+ reason: `跳过非 openai 平台账号: ${account.platform}`
+ });
+ return;
+ }
+
+ results.push(normalizeCredential(account, 'sub2api', index + 1, exportedAt));
+ });
+
+ return results;
+}
+
+export function normalizeCodexExternalCredentials(source, payload) {
+ if (source === 'cpa') {
+ return normalizeCpaCodexCredentials(payload);
+ }
+
+ if (source === 'sub2api') {
+ return normalizeSub2ApiCodexCredentials(payload);
+ }
+
+ throw new Error(`Unsupported Codex import source: ${source}`);
+}
diff --git a/src/auth/codex-oauth.js b/src/auth/codex-oauth.js
index 6e4e44241..446e499ee 100644
--- a/src/auth/codex-oauth.js
+++ b/src/auth/codex-oauth.js
@@ -28,6 +28,16 @@ const CODEX_OAUTH_CONFIG = {
*/
const activeServers = new Map();
+function sanitizeCodexCredentialFilenamePart(value) {
+ const sanitized = String(value || 'default')
+ .trim()
+ .replace(/[^a-zA-Z0-9@._+-]/g, '_')
+ .replace(/_+/g, '_')
+ .slice(0, 120);
+
+ return sanitized || 'default';
+}
+
/**
* 关闭指定端口的活动服务器
*/
@@ -493,6 +503,7 @@ class CodexAuth {
*/
async saveCredentials(creds) {
const email = creds.email || this.config.CODEX_EMAIL || 'default';
+ const safeEmail = sanitizeCodexCredentialFilenamePart(email);
// 优先使用配置中指定的路径,否则保存到 configs/codex 目录
let credsPath;
@@ -504,7 +515,7 @@ class CodexAuth {
const targetDir = path.join(projectDir, 'configs', 'codex');
await fs.promises.mkdir(targetDir, { recursive: true });
const timestamp = Date.now();
- const filename = `${timestamp}_codex-${email}.json`;
+ const filename = `${timestamp}_codex-${safeEmail}_oauth_creds.json`;
credsPath = path.join(targetDir, filename);
}
@@ -674,24 +685,52 @@ export async function batchImportCodexTokensStream(tokens, onProgress = null, sk
};
try {
- // 验证 token 数据
- if (!tokenData.access_token || !tokenData.id_token) {
- throw new Error('Token 缺少必需字段 (access_token 或 id_token)');
+ if (!tokenData || typeof tokenData !== 'object') {
+ throw new Error('Token 数据必须是 JSON 对象');
+ }
+
+ if (tokenData.skipped || tokenData.error) {
+ throw new Error(tokenData.reason || tokenData.error || 'skipped');
+ }
+
+ // 验证 token 数据:access_token 是唯一必需字段,id_token/refresh_token 可为空。
+ if (!tokenData.access_token) {
+ throw new Error('Token 缺少必需字段 access_token');
}
- // 解析 JWT 提取账户信息
- const claims = auth.parseJWT(tokenData.id_token);
- const accountId = claims['https://api.openai.com/auth']?.chatgpt_account_id || claims.sub;
- const email = claims.email;
+ // 解析 JWT 提取账户信息。外部导入格式可能没有 id_token,因此回退解析 access_token。
+ let claims = {};
+ for (const candidate of [tokenData.id_token, tokenData.access_token]) {
+ if (!candidate) continue;
+ try {
+ claims = auth.parseJWT(candidate);
+ break;
+ } catch {
+ // access_token-only 导入允许无法解析 JWT,只要外部字段提供了账号信息。
+ }
+ }
+
+ const authClaims = claims['https://api.openai.com/auth'] || {};
+ const profileClaims = claims['https://api.openai.com/profile'] || {};
+ const accountId = tokenData.account_id || tokenData.chatgpt_account_id || authClaims.chatgpt_account_id || claims.sub;
+ const email = tokenData.email || tokenData.name || profileClaims.email || claims.email || (accountId ? `codex-${accountId}` : null);
+
+ if (!accountId) {
+ throw new Error('Token 缺少 account_id/chatgpt_account_id,且无法从 JWT 中解析账号 ID');
+ }
+
+ const refreshToken = tokenData.refresh_token || '';
// 检查重复
if (!skipDuplicateCheck) {
- const duplicateCheck = await auth.checkDuplicate(accountId, tokenData.refresh_token);
+ const duplicateCheck = await auth.checkDuplicate(accountId, refreshToken);
if (duplicateCheck.isDuplicate) {
progressData.current = {
index: i + 1,
success: false,
error: 'duplicate',
+ email,
+ accountId,
existingPath: duplicateCheck.existingPath
};
results.failed++;
@@ -707,16 +746,44 @@ export async function batchImportCodexTokensStream(tokens, onProgress = null, sk
}
}
+ const expiredValue = tokenData.expired || tokenData.expiresAt || tokenData.expire || tokenData.expires_at;
+ let expired;
+ if (expiredValue) {
+ const parsed = typeof expiredValue === 'number'
+ ? new Date(expiredValue > 1000000000000 ? expiredValue : expiredValue * 1000)
+ : new Date(expiredValue);
+ expired = Number.isNaN(parsed.getTime()) ? null : parsed.toISOString();
+ }
+ if (!expired && tokenData.expires_in) {
+ const seconds = Number(tokenData.expires_in);
+ if (Number.isFinite(seconds) && seconds > 0) {
+ expired = new Date(Date.now() + seconds * 1000).toISOString();
+ }
+ }
+ if (!expired && claims.exp) {
+ const claimExp = Number(claims.exp);
+ if (Number.isFinite(claimExp)) {
+ const parsed = new Date(claimExp * 1000);
+ if (!Number.isNaN(parsed.getTime())) {
+ expired = parsed.toISOString();
+ }
+ }
+ }
+ if (!expired) {
+ expired = new Date(Date.now() + 3600 * 1000).toISOString();
+ }
+
// 构建凭据对象
const credentials = {
- id_token: tokenData.id_token,
+ id_token: tokenData.id_token || '',
access_token: tokenData.access_token,
- refresh_token: tokenData.refresh_token,
+ refresh_token: refreshToken,
account_id: accountId,
- last_refresh: new Date().toISOString(),
+ last_refresh: tokenData.last_refresh || new Date().toISOString(),
email: email,
type: 'codex',
- expired: new Date(Date.now() + (tokenData.expires_in || 3600) * 1000).toISOString()
+ expired,
+ access_token_only: !refreshToken
};
// 保存凭据
@@ -728,6 +795,9 @@ export async function batchImportCodexTokensStream(tokens, onProgress = null, sk
progressData.current = {
index: i + 1,
success: true,
+ email,
+ accountId,
+ accessTokenOnly: !refreshToken,
path: relativePath
};
results.success++;
@@ -744,6 +814,8 @@ export async function batchImportCodexTokensStream(tokens, onProgress = null, sk
progressData.current = {
index: i + 1,
success: false,
+ email: tokenData?.email || tokenData?.name,
+ accountId: tokenData?.account_id || tokenData?.chatgpt_account_id,
error: error.message
};
results.failed++;
@@ -1053,4 +1125,4 @@ export async function handleCodexOAuthCallback(code, state) {
error: error.message
};
}
-}
\ No newline at end of file
+}
diff --git a/src/auth/gemini-oauth.js b/src/auth/gemini-oauth.js
index 4842efb36..fb51d7337 100644
--- a/src/auth/gemini-oauth.js
+++ b/src/auth/gemini-oauth.js
@@ -19,7 +19,7 @@ const OAUTH_PROVIDERS = {
port: 8085,
credentialsDir: '.gemini',
credentialsFile: 'oauth_creds.json',
- scope: ['https://www.googleapis.com/auth/cloud-platform'],
+ scope: ['https://www.googleapis.com/auth/cloud-platform', 'https://www.googleapis.com/auth/userinfo.email'],
logPrefix: '[Gemini Auth]'
},
'gemini-antigravity': {
@@ -28,7 +28,7 @@ const OAUTH_PROVIDERS = {
port: 8086,
credentialsDir: '.antigravity',
credentialsFile: 'oauth_creds.json',
- scope: ['https://www.googleapis.com/auth/cloud-platform'],
+ scope: ['https://www.googleapis.com/auth/cloud-platform', 'https://www.googleapis.com/auth/userinfo.email'],
logPrefix: '[Antigravity Auth]'
}
};
diff --git a/src/auth/grok-auth.js b/src/auth/grok-auth.js
new file mode 100644
index 000000000..30ce04d52
--- /dev/null
+++ b/src/auth/grok-auth.js
@@ -0,0 +1,170 @@
+import fs from 'fs';
+import path from 'path';
+import crypto from 'crypto';
+import logger from '../utils/logger.js';
+import { broadcastEvent } from '../services/ui-manager.js';
+import { getProviderPoolManager } from '../services/service-manager.js';
+import { CONFIG } from '../core/config-manager.js';
+import { createProviderConfig } from '../utils/provider-utils.js';
+import { withFileLock, atomicWriteFile } from '../utils/file-lock.js';
+
+/**
+ * 批量导入 Grok SSO Tokens (流式处理)
+ * @param {Array} tokens - Token 数组 (可以是字符串数组 or 对象数组)
+ * @param {Function} onProgress - 进度回调函数
+ * @param {Boolean} skipDuplicateCheck - 是否跳过重复检查
+ * @returns {Promise