mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-25 11:09:13 +00:00
Compare commits
6 Commits
feature/re
...
v0.0.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c13149a3b5 | ||
|
|
2786f10e8c | ||
|
|
964d211270 | ||
|
|
a09a9f2261 | ||
|
|
09c2a1871b | ||
|
|
999f3af098 |
6
.github/workflows/e2e.yml
vendored
6
.github/workflows/e2e.yml
vendored
@@ -44,7 +44,7 @@ jobs:
|
||||
|
||||
- name: Run E2E tests
|
||||
env:
|
||||
QWEN_CODE_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
QWEN_CODE_BASE_URL: ${{ secrets.OPENAI_BASE_URL }}
|
||||
QWEN_CODE_MODEL: ${{ secrets.OPENAI_MODEL }}
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
OPENAI_BASE_URL: ${{ secrets.OPENAI_BASE_URL }}
|
||||
OPENAI_MODEL: ${{ secrets.OPENAI_MODEL }}
|
||||
run: npm run test:integration:${{ matrix.sandbox }} -- --verbose --keep-output
|
||||
|
||||
24
.github/workflows/release.yml
vendored
24
.github/workflows/release.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
||||
environment:
|
||||
name: production-release
|
||||
url: ${{ github.server_url }}/${{ github.repository }}/releases/tag/${{ steps.version.outputs.RELEASE_TAG }}
|
||||
if: github.repository == 'google-gemini/gemini-cli'
|
||||
if: github.repository == 'QwenLM/qwen-code'
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
@@ -95,7 +95,9 @@ jobs:
|
||||
npm run test:integration:sandbox:none
|
||||
npm run test:integration:sandbox:docker
|
||||
env:
|
||||
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
OPENAI_BASE_URL: ${{ secrets.OPENAI_BASE_URL }}
|
||||
OPENAI_MODEL: ${{ secrets.OPENAI_MODEL }}
|
||||
|
||||
- name: Configure Git User
|
||||
run: |
|
||||
@@ -133,22 +135,22 @@ jobs:
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version: '20'
|
||||
registry-url: 'https://wombat-dressing-room.appspot.com'
|
||||
scope: '@google'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
scope: '@qwen-code'
|
||||
|
||||
- name: Publish @google/gemini-cli-core
|
||||
run: npm publish --workspace=@google/gemini-cli-core --tag=${{ steps.version.outputs.NPM_TAG }} ${{ steps.vars.outputs.is_dry_run == 'true' && '--dry-run' || '' }}
|
||||
- name: Publish @qwen-code/qwen-code-core
|
||||
run: npm publish --workspace=@qwen-code/qwen-code-core --access public --tag=${{ steps.version.outputs.NPM_TAG }} ${{ steps.vars.outputs.is_dry_run == 'true' && '--dry-run' || '' }}
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.WOMBAT_TOKEN_CORE }}
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
- name: Install latest core package
|
||||
if: steps.vars.outputs.is_dry_run == 'false'
|
||||
run: npm install @google/gemini-cli-core@${{ steps.version.outputs.RELEASE_VERSION }} --workspace=@google/gemini-cli --save-exact
|
||||
run: npm install @qwen-code/qwen-code-core@${{ steps.version.outputs.RELEASE_VERSION }} --workspace=@qwen-code/qwen-code --save-exact
|
||||
|
||||
- name: Publish @google/gemini-cli
|
||||
run: npm publish --workspace=@google/gemini-cli --tag=${{ steps.version.outputs.NPM_TAG }} ${{ steps.vars.outputs.is_dry_run == 'true' && '--dry-run' || '' }}
|
||||
- name: Publish @qwen-code/qwen-code
|
||||
run: npm publish --workspace=@qwen-code/qwen-code --access public --tag=${{ steps.version.outputs.NPM_TAG }} ${{ steps.vars.outputs.is_dry_run == 'true' && '--dry-run' || '' }}
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.WOMBAT_TOKEN_CLI }}
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
- name: Create GitHub Release and Tag
|
||||
if: ${{ steps.vars.outputs.is_dry_run == 'false' }}
|
||||
|
||||
36
README.md
36
README.md
@@ -97,17 +97,17 @@ Qwen Code supports multiple API providers. You can configure your API key throug
|
||||
1. **Environment Variables**
|
||||
|
||||
```bash
|
||||
export QWEN_CODE_API_KEY="your_api_key_here"
|
||||
export QWEN_CODE_BASE_URL="your_api_endpoint"
|
||||
export QWEN_CODE_MODEL="your_model_choice"
|
||||
export OPENAI_API_KEY="your_api_key_here"
|
||||
export OPENAI_BASE_URL="your_api_endpoint"
|
||||
export OPENAI_MODEL="your_model_choice"
|
||||
```
|
||||
|
||||
2. **Project `.env` File**
|
||||
Create a `.env` file in your project root:
|
||||
```env
|
||||
QWEN_CODE_API_KEY=your_api_key_here
|
||||
QWEN_CODE_BASE_URL=your_api_endpoint
|
||||
QWEN_CODE_MODEL=your_model_choice
|
||||
OPENAI_API_KEY=your_api_key_here
|
||||
OPENAI_BASE_URL=your_api_endpoint
|
||||
OPENAI_MODEL=your_model_choice
|
||||
```
|
||||
|
||||
#### API Provider Options
|
||||
@@ -123,9 +123,9 @@ Qwen Code supports multiple API providers. You can configure your API key throug
|
||||
**Option 1: Alibaba Cloud Bailian** ([Apply for API Key](https://bailian.console.aliyun.com/))
|
||||
|
||||
```bash
|
||||
export QWEN_CODE_API_KEY="your_api_key_here"
|
||||
export QWEN_CODE_BASE_URL="https://dashscope.aliyuncs.com/compatible-mode/v1"
|
||||
export QWEN_CODE_MODEL="qwen3-coder-plus"
|
||||
export OPENAI_API_KEY="your_api_key_here"
|
||||
export OPENAI_BASE_URL="https://dashscope.aliyuncs.com/compatible-mode/v1"
|
||||
export OPENAI_MODEL="qwen3-coder-plus"
|
||||
```
|
||||
|
||||
**Option 2: ModelScope (Free Tier)** ([Apply for API Key](https://modelscope.cn/docs/model-service/API-Inference/intro))
|
||||
@@ -134,9 +134,9 @@ export QWEN_CODE_MODEL="qwen3-coder-plus"
|
||||
- ⚠️ Connect your Aliyun account to avoid authentication errors
|
||||
|
||||
```bash
|
||||
export QWEN_CODE_API_KEY="your_api_key_here"
|
||||
export QWEN_CODE_BASE_URL="https://api-inference.modelscope.cn/v1"
|
||||
export QWEN_CODE_MODEL="Qwen/Qwen3-Coder-480B-A35B-Instruct"
|
||||
export OPENAI_API_KEY="your_api_key_here"
|
||||
export OPENAI_BASE_URL="https://api-inference.modelscope.cn/v1"
|
||||
export OPENAI_MODEL="Qwen/Qwen3-Coder-480B-A35B-Instruct"
|
||||
```
|
||||
|
||||
</details>
|
||||
@@ -147,17 +147,17 @@ export QWEN_CODE_MODEL="Qwen/Qwen3-Coder-480B-A35B-Instruct"
|
||||
**Option 1: Alibaba Cloud ModelStudio** ([Apply for API Key](https://modelstudio.console.alibabacloud.com/))
|
||||
|
||||
```bash
|
||||
export QWEN_CODE_API_KEY="your_api_key_here"
|
||||
export QWEN_CODE_BASE_URL="https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
|
||||
export QWEN_CODE_MODEL="qwen3-coder-plus"
|
||||
export OPENAI_API_KEY="your_api_key_here"
|
||||
export OPENAI_BASE_URL="https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
|
||||
export OPENAI_MODEL="qwen3-coder-plus"
|
||||
```
|
||||
|
||||
**Option 2: OpenRouter (Free Tier Available)** ([Apply for API Key](https://openrouter.ai/))
|
||||
|
||||
```bash
|
||||
export QWEN_CODE_API_KEY="your_api_key_here"
|
||||
export QWEN_CODE_BASE_URL="https://openrouter.ai/api/v1"
|
||||
export QWEN_CODE_MODEL="qwen/qwen3-coder:free"
|
||||
export OPENAI_API_KEY="your_api_key_here"
|
||||
export OPENAI_BASE_URL="https://openrouter.ai/api/v1"
|
||||
export OPENAI_MODEL="qwen/qwen3-coder:free"
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
@@ -219,8 +219,8 @@ In addition to a project settings file, a project's `.gemini` directory can cont
|
||||
- **Description:** Configures custom system prompt templates for specific model names and base URLs. This allows you to use different system prompts for different AI models or API endpoints.
|
||||
- **Default:** `undefined` (uses default system prompt)
|
||||
- **Properties:**
|
||||
- **`baseUrls`** (array of strings, optional): Array of base URLs to exactly match against `QWEN_CODE_BASE_URL` environment variable. If not specified, matches any base URL.
|
||||
- **`modelNames`** (array of strings, optional): Array of model names to exactly match against `QWEN_CODE_MODEL` environment variable. If not specified, matches any model.
|
||||
- **`baseUrls`** (array of strings, optional): Array of base URLs to exactly match against `OPENAI_BASE_URL` environment variable. If not specified, matches any base URL.
|
||||
- **`modelNames`** (array of strings, optional): Array of model names to exactly match against `OPENAI_MODEL` environment variable. If not specified, matches any model.
|
||||
- **`template`** (string): The system prompt template to use when both baseUrl and modelNames match. Supports placeholders:
|
||||
- `{RUNTIME_VARS_IS_GIT_REPO}`: Replaced with `true` or `false` based on whether the current directory is a git repository
|
||||
- `{RUNTIME_VARS_SANDBOX}`: Replaced with the sandbox type (e.g., `"sandbox-exec"`, `"docker"`, or empty string)
|
||||
|
||||
@@ -40,9 +40,9 @@ qwen-code --openai-api-key "your-api-key-here" --model "gpt-4-turbo"
|
||||
Set the following environment variables in your shell or `.env` file:
|
||||
|
||||
```bash
|
||||
export QWEN_CODE_API_KEY="your-api-key-here"
|
||||
export QWEN_CODE_BASE_URL="https://api.openai.com/v1" # Optional, defaults to this value
|
||||
export QWEN_CODE_MODEL="gpt-4o" # Optional, defaults to gpt-4o
|
||||
export OPENAI_API_KEY="your-api-key-here"
|
||||
export OPENAI_BASE_URL="https://api.openai.com/v1" # Optional, defaults to this value
|
||||
export OPENAI_MODEL="gpt-4o" # Optional, defaults to gpt-4o
|
||||
```
|
||||
|
||||
## Supported Models
|
||||
@@ -58,7 +58,7 @@ The CLI supports all OpenAI models that are available through the OpenAI API, in
|
||||
|
||||
## Custom Endpoints
|
||||
|
||||
You can use custom endpoints by setting the `QWEN_CODE_BASE_URL` environment variable or using the `--openai-base-url` command line argument. This is useful for:
|
||||
You can use custom endpoints by setting the `OPENAI_BASE_URL` environment variable or using the `--openai-base-url` command line argument. This is useful for:
|
||||
|
||||
- Using Azure OpenAI
|
||||
- Using other OpenAI-compatible APIs
|
||||
|
||||
13
package-lock.json
generated
13
package-lock.json
generated
@@ -1,18 +1,15 @@
|
||||
{
|
||||
"name": "@qwen-code/qwen-code",
|
||||
"version": "0.0.1-alpha.12",
|
||||
"version": "0.0.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@qwen-code/qwen-code",
|
||||
"version": "0.0.1-alpha.12",
|
||||
"version": "0.0.2",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
"dependencies": {
|
||||
"@qwen-code/qwen-code": "^0.0.1-alpha.8"
|
||||
},
|
||||
"bin": {
|
||||
"qwen": "bundle/gemini.js"
|
||||
},
|
||||
@@ -11945,7 +11942,7 @@
|
||||
},
|
||||
"packages/cli": {
|
||||
"name": "@qwen-code/qwen-code",
|
||||
"version": "0.0.1-alpha.12",
|
||||
"version": "0.0.2",
|
||||
"dependencies": {
|
||||
"@qwen-code/qwen-code-core": "file:../core",
|
||||
"@types/update-notifier": "^6.0.8",
|
||||
@@ -12123,7 +12120,7 @@
|
||||
},
|
||||
"packages/core": {
|
||||
"name": "@qwen-code/qwen-code-core",
|
||||
"version": "0.0.1-alpha.12",
|
||||
"version": "0.0.2",
|
||||
"dependencies": {
|
||||
"@google/genai": "1.8.0",
|
||||
"@modelcontextprotocol/sdk": "^1.11.0",
|
||||
@@ -12197,7 +12194,7 @@
|
||||
},
|
||||
"packages/vscode-ide-companion": {
|
||||
"name": "@qwen-code/qwen-code-vscode-ide-companion",
|
||||
"version": "0.0.1-alpha.12",
|
||||
"version": "0.0.2",
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.15.1",
|
||||
"cors": "^2.8.5",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@qwen-code/qwen-code",
|
||||
"version": "0.0.1-alpha.12",
|
||||
"version": "0.0.2",
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
@@ -10,10 +10,10 @@
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+http://gitlab.alibaba-inc.com/Qwen-Coder/qwen-code.git"
|
||||
"url": "git+https://github.com/QwenLM/qwen-code.git"
|
||||
},
|
||||
"config": {
|
||||
"sandboxImageUri": "ghcr.io/qwenlm/qwen-code:0.0.1-alpha.12"
|
||||
"sandboxImageUri": "ghcr.io/qwenlm/qwen-code:0.0.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node scripts/start.js",
|
||||
@@ -81,8 +81,5 @@
|
||||
"typescript-eslint": "^8.30.1",
|
||||
"vitest": "^3.2.4",
|
||||
"yargs": "^18.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@qwen-code/qwen-code": "^0.0.1-alpha.8"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "@qwen-code/qwen-code",
|
||||
"version": "0.0.1-alpha.12",
|
||||
"description": "Gemini CLI",
|
||||
"version": "0.0.2",
|
||||
"description": "Qwen Code",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+http://gitlab.alibaba-inc.com/Qwen-Coder/qwen-code.git"
|
||||
"url": "git+https://github.com/QwenLM/qwen-code.git"
|
||||
},
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
@@ -25,7 +25,7 @@
|
||||
"dist"
|
||||
],
|
||||
"config": {
|
||||
"sandboxImageUri": "ghcr.io/qwenlm/qwen-code:0.0.1-alpha.12"
|
||||
"sandboxImageUri": "ghcr.io/qwenlm/qwen-code:0.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@qwen-code/qwen-code-core": "file:../core",
|
||||
|
||||
@@ -39,8 +39,8 @@ export const validateAuthMethod = (authMethod: string): string | null => {
|
||||
}
|
||||
|
||||
if (authMethod === AuthType.USE_OPENAI) {
|
||||
if (!process.env.QWEN_CODE_API_KEY) {
|
||||
return 'QWEN_CODE_API_KEY environment variable not found. You can enter it interactively or add it to your .env file.';
|
||||
if (!process.env.OPENAI_API_KEY) {
|
||||
return 'OPENAI_API_KEY environment variable not found. You can enter it interactively or add it to your .env file.';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -49,13 +49,13 @@ export const validateAuthMethod = (authMethod: string): string | null => {
|
||||
};
|
||||
|
||||
export const setOpenAIApiKey = (apiKey: string): void => {
|
||||
process.env.QWEN_CODE_API_KEY = apiKey;
|
||||
process.env.OPENAI_API_KEY = apiKey;
|
||||
};
|
||||
|
||||
export const setOpenAIBaseUrl = (baseUrl: string): void => {
|
||||
process.env.QWEN_CODE_BASE_URL = baseUrl;
|
||||
process.env.OPENAI_BASE_URL = baseUrl;
|
||||
};
|
||||
|
||||
export const setOpenAIModel = (model: string): void => {
|
||||
process.env.QWEN_CODE_MODEL = model;
|
||||
process.env.OPENAI_MODEL = model;
|
||||
};
|
||||
|
||||
@@ -264,12 +264,12 @@ export async function loadCliConfig(
|
||||
|
||||
// Handle OpenAI API key from command line
|
||||
if (argv.openaiApiKey) {
|
||||
process.env.QWEN_CODE_API_KEY = argv.openaiApiKey;
|
||||
process.env.OPENAI_API_KEY = argv.openaiApiKey;
|
||||
}
|
||||
|
||||
// Handle OpenAI base URL from command line
|
||||
if (argv.openaiBaseUrl) {
|
||||
process.env.QWEN_CODE_BASE_URL = argv.openaiBaseUrl;
|
||||
process.env.OPENAI_BASE_URL = argv.openaiBaseUrl;
|
||||
}
|
||||
|
||||
// Set the context filename in the server's memoryTool module BEFORE loading memory
|
||||
|
||||
@@ -1173,7 +1173,7 @@ describe('useGeminiStream', () => {
|
||||
mockAuthType,
|
||||
undefined,
|
||||
'gemini-2.5-pro',
|
||||
'qwen3-coder-flash',
|
||||
'gemini-2.5-flash',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -39,7 +39,7 @@ describe('parseAndFormatApiError', () => {
|
||||
);
|
||||
expect(result).toContain('[API Error: Rate limit exceeded');
|
||||
expect(result).toContain(
|
||||
'Possible quota limitations in place or slow response times detected. Switching to the qwen3-coder-flash model',
|
||||
'Possible quota limitations in place or slow response times detected. Switching to the gemini-2.5-flash model',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -55,7 +55,7 @@ describe('parseAndFormatApiError', () => {
|
||||
);
|
||||
expect(result).toContain('[API Error: Rate limit exceeded');
|
||||
expect(result).toContain(
|
||||
'Possible quota limitations in place or slow response times detected. Switching to the qwen3-coder-flash model',
|
||||
'Possible quota limitations in place or slow response times detected. Switching to the gemini-2.5-flash model',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -169,7 +169,7 @@ describe('parseAndFormatApiError', () => {
|
||||
);
|
||||
expect(result).toContain('[API Error: Rate limit exceeded');
|
||||
expect(result).toContain(
|
||||
'Possible quota limitations in place or slow response times detected. Switching to the qwen3-coder-flash model',
|
||||
'Possible quota limitations in place or slow response times detected. Switching to the gemini-2.5-flash model',
|
||||
);
|
||||
expect(result).not.toContain(
|
||||
'You have reached your daily gemini-2.5-pro quota limit',
|
||||
|
||||
@@ -517,15 +517,15 @@ export async function start_sandbox(
|
||||
args.push('--env', `GOOGLE_API_KEY=${process.env.GOOGLE_API_KEY}`);
|
||||
}
|
||||
|
||||
// copy QWEN_CODE_API_KEY and related env vars for Qwen
|
||||
if (process.env.QWEN_CODE_API_KEY) {
|
||||
args.push('--env', `QWEN_CODE_API_KEY=${process.env.QWEN_CODE_API_KEY}`);
|
||||
// copy OPENAI_API_KEY and related env vars for Qwen
|
||||
if (process.env.OPENAI_API_KEY) {
|
||||
args.push('--env', `OPENAI_API_KEY=${process.env.OPENAI_API_KEY}`);
|
||||
}
|
||||
if (process.env.QWEN_CODE_BASE_URL) {
|
||||
args.push('--env', `QWEN_CODE_BASE_URL=${process.env.QWEN_CODE_BASE_URL}`);
|
||||
if (process.env.OPENAI_BASE_URL) {
|
||||
args.push('--env', `OPENAI_BASE_URL=${process.env.OPENAI_BASE_URL}`);
|
||||
}
|
||||
if (process.env.QWEN_CODE_MODEL) {
|
||||
args.push('--env', `QWEN_CODE_MODEL=${process.env.QWEN_CODE_MODEL}`);
|
||||
if (process.env.OPENAI_MODEL) {
|
||||
args.push('--env', `OPENAI_MODEL=${process.env.OPENAI_MODEL}`);
|
||||
}
|
||||
|
||||
// copy GOOGLE_GENAI_USE_VERTEXAI
|
||||
|
||||
4
packages/core/package-lock.json
generated
4
packages/core/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@google/gemini-cli-core",
|
||||
"version": "0.0.1-alpha.12",
|
||||
"version": "0.0.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@google/gemini-cli-core",
|
||||
"version": "0.0.1-alpha.12",
|
||||
"version": "0.0.3",
|
||||
"dependencies": {
|
||||
"@google/genai": "^1.4.0",
|
||||
"@modelcontextprotocol/sdk": "^1.11.0",
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "@qwen-code/qwen-code-core",
|
||||
"version": "0.0.1-alpha.12",
|
||||
"version": "0.0.2",
|
||||
"description": "Qwen Code Core",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+http://gitlab.alibaba-inc.com/Qwen-Coder/qwen-code.git"
|
||||
"url": "git+https://github.com/QwenLM/qwen-code.git"
|
||||
},
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
*/
|
||||
|
||||
export const DEFAULT_GEMINI_MODEL = 'qwen3-coder-plus';
|
||||
export const DEFAULT_GEMINI_FLASH_MODEL = 'qwen3-coder-flash';
|
||||
export const DEFAULT_GEMINI_FLASH_MODEL = 'gemini-2.5-flash';
|
||||
export const DEFAULT_GEMINI_EMBEDDING_MODEL = 'gemini-embedding-001';
|
||||
|
||||
@@ -34,7 +34,7 @@ describe('OpenAIContentGenerator Timeout Handling', () => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
// Mock environment variables
|
||||
vi.stubEnv('QWEN_CODE_BASE_URL', '');
|
||||
vi.stubEnv('OPENAI_BASE_URL', '');
|
||||
|
||||
// Mock config
|
||||
mockConfig = {
|
||||
|
||||
@@ -75,7 +75,7 @@ export async function createContentGeneratorConfig(
|
||||
const googleApiKey = process.env.GOOGLE_API_KEY || undefined;
|
||||
const googleCloudProject = process.env.GOOGLE_CLOUD_PROJECT || undefined;
|
||||
const googleCloudLocation = process.env.GOOGLE_CLOUD_LOCATION || undefined;
|
||||
const openaiApiKey = process.env.QWEN_CODE_API_KEY;
|
||||
const openaiApiKey = process.env.OPENAI_API_KEY;
|
||||
|
||||
// Use runtime model from config if available, otherwise fallback to parameter or default
|
||||
const effectiveModel = model || DEFAULT_GEMINI_MODEL;
|
||||
@@ -117,7 +117,7 @@ export async function createContentGeneratorConfig(
|
||||
if (authType === AuthType.USE_OPENAI && openaiApiKey) {
|
||||
contentGeneratorConfig.apiKey = openaiApiKey;
|
||||
contentGeneratorConfig.model =
|
||||
process.env.QWEN_CODE_MODEL || DEFAULT_GEMINI_MODEL;
|
||||
process.env.OPENAI_MODEL || DEFAULT_GEMINI_MODEL;
|
||||
|
||||
return contentGeneratorConfig;
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ export class OpenAIContentGenerator implements ContentGenerator {
|
||||
constructor(apiKey: string, model: string, config: Config) {
|
||||
this.model = model;
|
||||
this.config = config;
|
||||
const baseURL = process.env.QWEN_CODE_BASE_URL || '';
|
||||
const baseURL = process.env.OPENAI_BASE_URL || '';
|
||||
|
||||
// Configure timeout settings - using progressive timeouts
|
||||
const timeoutConfig = {
|
||||
|
||||
@@ -130,8 +130,8 @@ describe('URL matching with trailing slash compatibility', () => {
|
||||
// Test case 1: No trailing slash in config, actual URL has trailing slash
|
||||
process.env = {
|
||||
...originalEnv,
|
||||
QWEN_CODE_BASE_URL: 'https://api.example.com/',
|
||||
QWEN_CODE_MODEL: 'gpt-4',
|
||||
OPENAI_BASE_URL: 'https://api.example.com/',
|
||||
OPENAI_MODEL: 'gpt-4',
|
||||
};
|
||||
|
||||
const result1 = getCoreSystemPrompt(undefined, config);
|
||||
@@ -140,8 +140,8 @@ describe('URL matching with trailing slash compatibility', () => {
|
||||
// Test case 2: Config has trailing slash, actual URL has no trailing slash
|
||||
process.env = {
|
||||
...originalEnv,
|
||||
QWEN_CODE_BASE_URL: 'https://api.openai.com',
|
||||
QWEN_CODE_MODEL: 'gpt-3.5-turbo',
|
||||
OPENAI_BASE_URL: 'https://api.openai.com',
|
||||
OPENAI_MODEL: 'gpt-3.5-turbo',
|
||||
};
|
||||
|
||||
const result2 = getCoreSystemPrompt(undefined, config);
|
||||
@@ -150,8 +150,8 @@ describe('URL matching with trailing slash compatibility', () => {
|
||||
// Test case 3: No trailing slash in config, actual URL has no trailing slash
|
||||
process.env = {
|
||||
...originalEnv,
|
||||
QWEN_CODE_BASE_URL: 'https://api.example.com',
|
||||
QWEN_CODE_MODEL: 'gpt-4',
|
||||
OPENAI_BASE_URL: 'https://api.example.com',
|
||||
OPENAI_MODEL: 'gpt-4',
|
||||
};
|
||||
|
||||
const result3 = getCoreSystemPrompt(undefined, config);
|
||||
@@ -160,8 +160,8 @@ describe('URL matching with trailing slash compatibility', () => {
|
||||
// Test case 4: Config has trailing slash, actual URL has trailing slash
|
||||
process.env = {
|
||||
...originalEnv,
|
||||
QWEN_CODE_BASE_URL: 'https://api.openai.com/',
|
||||
QWEN_CODE_MODEL: 'gpt-3.5-turbo',
|
||||
OPENAI_BASE_URL: 'https://api.openai.com/',
|
||||
OPENAI_MODEL: 'gpt-3.5-turbo',
|
||||
};
|
||||
|
||||
const result4 = getCoreSystemPrompt(undefined, config);
|
||||
@@ -187,8 +187,8 @@ describe('URL matching with trailing slash compatibility', () => {
|
||||
// Test case: URLs do not match
|
||||
process.env = {
|
||||
...originalEnv,
|
||||
QWEN_CODE_BASE_URL: 'https://api.different.com',
|
||||
QWEN_CODE_MODEL: 'gpt-4',
|
||||
OPENAI_BASE_URL: 'https://api.different.com',
|
||||
OPENAI_MODEL: 'gpt-4',
|
||||
};
|
||||
|
||||
const result = getCoreSystemPrompt(undefined, config);
|
||||
|
||||
@@ -65,8 +65,8 @@ export function getCoreSystemPrompt(
|
||||
|
||||
// Check for system prompt mappings from global config
|
||||
if (config?.systemPromptMappings) {
|
||||
const currentModel = process.env.QWEN_CODE_MODEL || DEFAULT_GEMINI_MODEL;
|
||||
const currentBaseUrl = process.env.QWEN_CODE_BASE_URL || '';
|
||||
const currentModel = process.env.OPENAI_MODEL || DEFAULT_GEMINI_MODEL;
|
||||
const currentBaseUrl = process.env.OPENAI_BASE_URL || '';
|
||||
|
||||
const matchedMapping = config.systemPromptMappings.find((mapping) => {
|
||||
const { baseUrls, modelNames } = mapping;
|
||||
|
||||
4
packages/vscode-ide-companion/package-lock.json
generated
4
packages/vscode-ide-companion/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "qwen-code-vscode",
|
||||
"version": "0.0.1-alpha.12",
|
||||
"version": "0.0.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "qwen-code-vscode",
|
||||
"version": "0.0.1-alpha.12",
|
||||
"version": "0.0.3",
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.15.1",
|
||||
"cors": "^2.8.5",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "@qwen-code/qwen-code-vscode-ide-companion",
|
||||
"displayName": "Qwen Code VSCode IDE Companion",
|
||||
"description": "",
|
||||
"version": "0.0.1-alpha.12",
|
||||
"version": "0.0.2",
|
||||
"engines": {
|
||||
"vscode": "^1.101.0"
|
||||
},
|
||||
|
||||
@@ -14,20 +14,44 @@ function getPackageVersion() {
|
||||
return packageJson.version;
|
||||
}
|
||||
|
||||
function getShortSha() {
|
||||
return execSync('git rev-parse --short HEAD').toString().trim();
|
||||
function incrementPatchVersion(version) {
|
||||
const parts = version.split('.');
|
||||
const major = parseInt(parts[0]);
|
||||
const minor = parseInt(parts[1]);
|
||||
const patch = parseInt(parts[2].split('-')[0]); // Handle pre-release versions
|
||||
return `${major}.${minor}.${patch + 1}`;
|
||||
}
|
||||
|
||||
function getLatestNightlyCount() {
|
||||
try {
|
||||
// Try to get the latest nightly tag from git to determine the counter
|
||||
const currentVersion = getPackageVersion();
|
||||
const nextVersion = incrementPatchVersion(currentVersion);
|
||||
const tags = execSync(`git tag -l "v${nextVersion}-nightly.*"`)
|
||||
.toString()
|
||||
.trim();
|
||||
|
||||
if (!tags) return 0;
|
||||
|
||||
const nightlyTags = tags.split('\n').filter(Boolean);
|
||||
const counts = nightlyTags.map((tag) => {
|
||||
const match = tag.match(/nightly\.(\d+)$/);
|
||||
return match ? parseInt(match[1]) : 0;
|
||||
});
|
||||
|
||||
return Math.max(...counts, -1) + 1;
|
||||
} catch (_error) {
|
||||
// If we can't get tags, start from 0
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
export function getNightlyTagName() {
|
||||
const version = getPackageVersion();
|
||||
const now = new Date();
|
||||
const year = now.getUTCFullYear().toString().slice(-2);
|
||||
const month = (now.getUTCMonth() + 1).toString().padStart(2, '0');
|
||||
const day = now.getUTCDate().toString().padStart(2, '0');
|
||||
const date = `${year}${month}${day}`;
|
||||
const nextVersion = incrementPatchVersion(version);
|
||||
const nightlyCount = getLatestNightlyCount();
|
||||
|
||||
const sha = getShortSha();
|
||||
return `v${version}-nightly.${date}.${sha}`;
|
||||
return `v${nextVersion}-nightly.${nightlyCount}`;
|
||||
}
|
||||
|
||||
export function getReleaseVersion() {
|
||||
@@ -72,7 +96,13 @@ export function getReleaseVersion() {
|
||||
const releaseVersion = releaseTag.substring(1);
|
||||
let npmTag = 'latest';
|
||||
if (releaseVersion.includes('-')) {
|
||||
npmTag = releaseVersion.split('-')[1].split('.')[0];
|
||||
const prereleasePart = releaseVersion.split('-')[1];
|
||||
npmTag = prereleasePart.split('.')[0];
|
||||
|
||||
// Ensure nightly releases use 'nightly' tag, not 'latest'
|
||||
if (npmTag === 'nightly') {
|
||||
npmTag = 'nightly';
|
||||
}
|
||||
}
|
||||
|
||||
return { releaseTag, releaseVersion, npmTag };
|
||||
|
||||
@@ -41,15 +41,14 @@ describe('getReleaseVersion', () => {
|
||||
|
||||
it('should calculate nightly version when IS_NIGHTLY is true', () => {
|
||||
process.env.IS_NIGHTLY = 'true';
|
||||
const knownDate = new Date('2025-07-20T10:00:00.000Z');
|
||||
vi.setSystemTime(knownDate);
|
||||
vi.mocked(fs.default.readFileSync).mockReturnValue(
|
||||
JSON.stringify({ version: '0.1.0' }),
|
||||
);
|
||||
vi.mocked(execSync).mockReturnValue('abcdef');
|
||||
// Mock git tag command to return empty (no existing nightly tags)
|
||||
vi.mocked(execSync).mockReturnValue('');
|
||||
const { releaseTag, releaseVersion, npmTag } = getReleaseVersion();
|
||||
expect(releaseTag).toBe('v0.1.0-nightly.250720.abcdef');
|
||||
expect(releaseVersion).toBe('0.1.0-nightly.250720.abcdef');
|
||||
expect(releaseTag).toBe('v0.1.1-nightly.0');
|
||||
expect(releaseVersion).toBe('0.1.1-nightly.0');
|
||||
expect(npmTag).toBe('nightly');
|
||||
});
|
||||
|
||||
@@ -99,8 +98,8 @@ describe('getReleaseVersion', () => {
|
||||
describe('get-release-version script', () => {
|
||||
it('should print version JSON to stdout when executed directly', () => {
|
||||
const expectedJson = {
|
||||
releaseTag: 'v0.1.0-nightly.20250705',
|
||||
releaseVersion: '0.1.0-nightly.20250705',
|
||||
releaseTag: 'v0.1.1-nightly.0',
|
||||
releaseVersion: '0.1.1-nightly.0',
|
||||
npmTag: 'nightly',
|
||||
};
|
||||
execSync.mockReturnValue(JSON.stringify(expectedJson));
|
||||
|
||||
@@ -23,18 +23,24 @@ function writeJson(filePath, data) {
|
||||
writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n');
|
||||
}
|
||||
|
||||
// 1. Get the version type from the command line arguments.
|
||||
const versionType = process.argv[2];
|
||||
if (!versionType) {
|
||||
console.error('Error: No version type specified.');
|
||||
console.error('Usage: npm run version <patch|minor|major|prerelease>');
|
||||
// 1. Get the version from the command line arguments.
|
||||
const versionArg = process.argv[2];
|
||||
if (!versionArg) {
|
||||
console.error('Error: No version specified.');
|
||||
console.error(
|
||||
'Usage: npm run version <version> (e.g., 1.2.3 or patch|minor|major|prerelease)',
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// 2. Bump the version in the root and all workspace package.json files.
|
||||
run(`npm version ${versionType} --no-git-tag-version --allow-same-version`);
|
||||
// 2. Determine if we have a specific version or a version type
|
||||
const isSpecificVersion = /^\d+\.\d+\.\d+/.test(versionArg);
|
||||
const npmVersionArg = isSpecificVersion ? versionArg : versionArg;
|
||||
|
||||
// 3. Bump the version in the root and all workspace package.json files.
|
||||
run(`npm version ${npmVersionArg} --no-git-tag-version --allow-same-version`);
|
||||
run(
|
||||
`npm version ${versionType} --workspaces --no-git-tag-version --allow-same-version`,
|
||||
`npm version ${npmVersionArg} --workspaces --no-git-tag-version --allow-same-version`,
|
||||
);
|
||||
|
||||
// 3. Get the new version number from the root package.json
|
||||
|
||||
Reference in New Issue
Block a user