refactor: unify sandbox configuration naming and improve telemetry config

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
pomelo-nwu 2026-02-11 11:08:15 +08:00
parent 66f754e203
commit 8b3aeb4550
24 changed files with 141 additions and 143 deletions

View file

@ -42,11 +42,11 @@ steps:
args:
- '-c'
- |-
export GEMINI_SANDBOX_IMAGE_TAG=$$(cat /workspace/image_tag.txt)
echo "Using Docker image tag for build: $$GEMINI_SANDBOX_IMAGE_TAG"
export QWEN_SANDBOX_IMAGE_TAG=$$(cat /workspace/image_tag.txt)
echo "Using Docker image tag for build: $$QWEN_SANDBOX_IMAGE_TAG"
npm run build:sandbox -- --output-file /workspace/final_image_uri.txt
env:
- 'GEMINI_SANDBOX=$_CONTAINER_TOOL'
- 'QWEN_SANDBOX=$_CONTAINER_TOOL'
# Step 8: Publish sandbox container image
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
@ -61,7 +61,7 @@ steps:
echo "Pushing sandbox image: $${FINAL_IMAGE_URI}"
$_CONTAINER_TOOL push "$${FINAL_IMAGE_URI}"
env:
- 'GEMINI_SANDBOX=$_CONTAINER_TOOL'
- 'QWEN_SANDBOX=$_CONTAINER_TOOL'
options:
defaultLogsBucketBehavior: 'REGIONAL_USER_OWNED_BUCKET'

6
.vscode/launch.json vendored
View file

@ -14,7 +14,7 @@
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"env": {
"GEMINI_SANDBOX": "false"
"QWEN_SANDBOX": "false"
}
},
{
@ -86,7 +86,7 @@
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"env": {
"GEMINI_SANDBOX": "false"
"QWEN_SANDBOX": "false"
}
},
{
@ -107,7 +107,7 @@
"internalConsoleOptions": "neverOpen",
"skipFiles": ["<node_internals>/**"],
"env": {
"GEMINI_SANDBOX": "false"
"QWEN_SANDBOX": "false"
}
},
{

View file

@ -1,6 +1,6 @@
# Example Proxy Script
The following is an example of a proxy script that can be used with the `GEMINI_SANDBOX_PROXY_COMMAND` environment variable. This script only allows `HTTPS` connections to `example.com:443` and declines all other requests.
The following is an example of a proxy script that can be used with the `QWEN_SANDBOX_PROXY_COMMAND` environment variable. This script only allows `HTTPS` connections to `example.com:443` and declines all other requests.
```javascript
#!/usr/bin/env node
@ -12,7 +12,7 @@ The following is an example of a proxy script that can be used with the `GEMINI_
*/
// Example proxy server that listens on :::8877 and only allows HTTPS connections to example.com.
// Set `GEMINI_SANDBOX_PROXY_COMMAND=scripts/example-proxy.js` to run proxy alongside sandbox
// Set `QWEN_SANDBOX_PROXY_COMMAND=scripts/example-proxy.js` to run proxy alongside sandbox
// Test via `curl https://example.com` inside sandbox (in shell mode or via shell tool)
import http from 'node:http';

View file

@ -59,7 +59,7 @@ RUN apt-get update && apt-get install -y \
#### 4、Create the first sandbox image under the root directory of your project
```bash
GEMINI_SANDBOX=docker BUILD_SANDBOX=1 qwen -s
QWEN_SANDBOX=docker BUILD_SANDBOX=1 qwen -s
# Observe whether the sandbox version of the tool you launched is consistent with the version of your custom image. If they are consistent, the startup will be successful
```

View file

@ -481,22 +481,22 @@ For authentication-related variables (like `OPENAI_*`) and the recommended `.qwe
### Environment Variables Table
| Variable | Description | Notes |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GEMINI_TELEMETRY_ENABLED` | Set to `true` or `1` to enable telemetry. Any other value is treated as disabling it. | Overrides the `telemetry.enabled` setting. |
| `GEMINI_TELEMETRY_TARGET` | Sets the telemetry target (`local` or `gcp`). | Overrides the `telemetry.target` setting. |
| `GEMINI_TELEMETRY_OTLP_ENDPOINT` | Sets the OTLP endpoint for telemetry. | Overrides the `telemetry.otlpEndpoint` setting. |
| `GEMINI_TELEMETRY_OTLP_PROTOCOL` | Sets the OTLP protocol (`grpc` or `http`). | Overrides the `telemetry.otlpProtocol` setting. |
| `GEMINI_TELEMETRY_LOG_PROMPTS` | Set to `true` or `1` to enable or disable logging of user prompts. Any other value is treated as disabling it. | Overrides the `telemetry.logPrompts` setting. |
| `GEMINI_TELEMETRY_OUTFILE` | Sets the file path to write telemetry to when the target is `local`. | Overrides the `telemetry.outfile` setting. |
| `GEMINI_TELEMETRY_USE_COLLECTOR` | Set to `true` or `1` to enable or disable using an external OTLP collector. Any other value is treated as disabling it. | Overrides the `telemetry.useCollector` setting. |
| `GEMINI_SANDBOX` | Alternative to the `sandbox` setting in `settings.json`. | Accepts `true`, `false`, `docker`, `podman`, or a custom command string. |
| `SEATBELT_PROFILE` | (macOS specific) Switches the Seatbelt (`sandbox-exec`) profile on macOS. | `permissive-open`: (Default) Restricts writes to the project folder (and a few other folders, see `packages/cli/src/utils/sandbox-macos-permissive-open.sb`) but allows other operations. `strict`: Uses a strict profile that declines operations by default. `<profile_name>`: Uses a custom profile. To define a custom profile, create a file named `sandbox-macos-<profile_name>.sb` in your project's `.qwen/` directory (e.g., `my-project/.qwen/sandbox-macos-custom.sb`). |
| `DEBUG` or `DEBUG_MODE` | (often used by underlying libraries or the CLI itself) Set to `true` or `1` to enable verbose debug logging, which can be helpful for troubleshooting. | **Note:** These variables are automatically excluded from project `.env` files by default to prevent interference with the CLI behavior. Use `.qwen/.env` files if you need to set these for Qwen Code specifically. |
| `NO_COLOR` | Set to any value to disable all color output in the CLI. | |
| `CLI_TITLE` | Set to a string to customize the title of the CLI. | |
| `CODE_ASSIST_ENDPOINT` | Specifies the endpoint for the code assist server. | This is useful for development and testing. |
| `TAVILY_API_KEY` | Your API key for the Tavily web search service. | Used to enable the `web_search` tool functionality. Example: `export TAVILY_API_KEY="tvly-your-api-key-here"` |
| Variable | Description | Notes |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `QWEN_TELEMETRY_ENABLED` | Set to `true` or `1` to enable telemetry. Any other value is treated as disabling it. | Overrides the `telemetry.enabled` setting. |
| `QWEN_TELEMETRY_TARGET` | Sets the telemetry target (`local` or `gcp`). | Overrides the `telemetry.target` setting. |
| `QWEN_TELEMETRY_OTLP_ENDPOINT` | Sets the OTLP endpoint for telemetry. | Overrides the `telemetry.otlpEndpoint` setting. |
| `QWEN_TELEMETRY_OTLP_PROTOCOL` | Sets the OTLP protocol (`grpc` or `http`). | Overrides the `telemetry.otlpProtocol` setting. |
| `QWEN_TELEMETRY_LOG_PROMPTS` | Set to `true` or `1` to enable or disable logging of user prompts. Any other value is treated as disabling it. | Overrides the `telemetry.logPrompts` setting. |
| `QWEN_TELEMETRY_OUTFILE` | Sets the file path to write telemetry to when the target is `local`. | Overrides the `telemetry.outfile` setting. |
| `QWEN_TELEMETRY_USE_COLLECTOR` | Set to `true` or `1` to enable or disable using an external OTLP collector. Any other value is treated as disabling it. | Overrides the `telemetry.useCollector` setting. |
| `QWEN_SANDBOX` | Alternative to the `sandbox` setting in `settings.json`. | Accepts `true`, `false`, `docker`, `podman`, or a custom command string. |
| `SEATBELT_PROFILE` | (macOS specific) Switches the Seatbelt (`sandbox-exec`) profile on macOS. | `permissive-open`: (Default) Restricts writes to the project folder (and a few other folders, see `packages/cli/src/utils/sandbox-macos-permissive-open.sb`) but allows other operations. `strict`: Uses a strict profile that declines operations by default. `<profile_name>`: Uses a custom profile. To define a custom profile, create a file named `sandbox-macos-<profile_name>.sb` in your project's `.qwen/` directory (e.g., `my-project/.qwen/sandbox-macos-custom.sb`). |
| `DEBUG` or `DEBUG_MODE` | (often used by underlying libraries or the CLI itself) Set to `true` or `1` to enable verbose debug logging, which can be helpful for troubleshooting. | **Note:** These variables are automatically excluded from project `.env` files by default to prevent interference with the CLI behavior. Use `.qwen/.env` files if you need to set these for Qwen Code specifically. |
| `NO_COLOR` | Set to any value to disable all color output in the CLI. | |
| `CLI_TITLE` | Set to a string to customize the title of the CLI. | |
| `CODE_ASSIST_ENDPOINT` | Specifies the endpoint for the code assist server. | This is useful for development and testing. |
| `TAVILY_API_KEY` | Your API key for the Tavily web search service. | Used to enable the `web_search` tool functionality. Example: `export TAVILY_API_KEY="tvly-your-api-key-here"` |
## Command-Line Arguments
@ -599,7 +599,7 @@ Qwen Code can execute potentially unsafe operations (like shell commands and fil
[Sandbox](../features/sandbox) is disabled by default, but you can enable it in a few ways:
- Using `--sandbox` or `-s` flag.
- Setting `GEMINI_SANDBOX` environment variable.
- Setting `QWEN_SANDBOX` environment variable.
- Sandbox is enabled when using `--yolo` or `--approval-mode=yolo` by default.
By default, it uses a pre-built `qwen-code-sandbox` Docker image.

View file

@ -29,7 +29,7 @@ The benefits of sandboxing include:
> [!note]
>
> **Naming note:** Some sandbox-related environment variables still use the `GEMINI_*` prefix for backwards compatibility.
> **Naming note:** Some sandbox-related environment variables may have used the `GEMINI_*` prefix historically. All new environment variables use the `QWEN_*` prefix.
## Sandboxing methods
@ -68,7 +68,7 @@ The container sandbox mounts your workspace and your `~/.qwen` directory into th
qwen -s -p "analyze the code structure"
# Or enable sandboxing for your shell session (recommended for CI / scripts)
export GEMINI_SANDBOX=true # true auto-picks a provider (see notes below)
export QWEN_SANDBOX=true # true auto-picks a provider (see notes below)
qwen -p "run the test suite"
# Configure in settings.json
@ -83,26 +83,26 @@ qwen -p "run the test suite"
>
> **Provider selection notes:**
>
> - On **macOS**, `GEMINI_SANDBOX=true` typically selects `sandbox-exec` (Seatbelt) if available.
> - On **Linux/Windows**, `GEMINI_SANDBOX=true` requires `docker` or `podman` to be installed.
> - To force a provider, set `GEMINI_SANDBOX=docker|podman|sandbox-exec`.
> - On **macOS**, `QWEN_SANDBOX=true` typically selects `sandbox-exec` (Seatbelt) if available.
> - On **Linux/Windows**, `QWEN_SANDBOX=true` requires `docker` or `podman` to be installed.
> - To force a provider, set `QWEN_SANDBOX=docker|podman|sandbox-exec`.
## Configuration
### Enable sandboxing (in order of precedence)
1. **Environment variable**: `GEMINI_SANDBOX=true|false|docker|podman|sandbox-exec`
1. **Environment variable**: `QWEN_SANDBOX=true|false|docker|podman|sandbox-exec`
2. **Command flag / argument**: `-s`, `--sandbox`, or `--sandbox=<provider>`
3. **Settings file**: `tools.sandbox` in your `settings.json` (e.g., `{"tools": {"sandbox": true}}`).
> [!important]
>
> If `GEMINI_SANDBOX` is set, it **overrides** the CLI flag and `settings.json`.
> If `QWEN_SANDBOX` is set, it **overrides** the CLI flag and `settings.json`.
### Configure the sandbox image (Docker/Podman)
- **CLI flag**: `--sandbox-image <image>`
- **Environment variable**: `GEMINI_SANDBOX_IMAGE=<image>`
- **Environment variable**: `QWEN_SANDBOX_IMAGE=<image>`
If you dont set either, Qwen Code uses the default image configured in the CLI package (for example `ghcr.io/qwenlm/qwen-code:<version>`).
@ -150,7 +150,7 @@ export SANDBOX_FLAGS="--flag1 --flag2=value"
If you want to restrict outbound network access to an allowlist, you can run a local proxy alongside the sandbox:
- Set `GEMINI_SANDBOX_PROXY_COMMAND=<command>`
- Set `QWEN_SANDBOX_PROXY_COMMAND=<command>`
- The command must start a proxy server that listens on `:::8877`
This is especially useful with `*-proxied` Seatbelt profiles.

View file

@ -14,8 +14,8 @@ import { TestRig } from './test-helper.js';
const REQUEST_TIMEOUT_MS = 60_000;
const INITIAL_PROMPT = 'Create a quick note (smoke test).';
const IS_SANDBOX =
process.env['GEMINI_SANDBOX'] &&
process.env['GEMINI_SANDBOX']!.toLowerCase() !== 'false';
process.env['QWEN_SANDBOX'] &&
process.env['QWEN_SANDBOX']!.toLowerCase() !== 'false';
type PendingRequest = {
resolve: (value: unknown) => void;

View file

@ -94,7 +94,7 @@ export async function setup() {
// Environment variables for CLI integration tests
process.env['INTEGRATION_TEST_FILE_DIR'] = runDir;
process.env['GEMINI_CLI_INTEGRATION_TEST'] = 'true';
process.env['QWEN_CODE_INTEGRATION_TEST'] = 'true';
process.env['TELEMETRY_LOG_FILE'] = join(runDir, 'telemetry.log');
// Environment variables for SDK E2E tests

View file

@ -72,7 +72,7 @@ describe('run_shell_command', () => {
const rig = new TestRig();
await rig.setup('should propagate environment variables');
const varName = 'GEMINI_CLI_TEST_VAR';
const varName = 'QWEN_CODE_TEST_VAR';
const varValue = `test-value-${Math.random().toString(36).substring(7)}`;
process.env[varName] = varValue;

View file

@ -154,7 +154,7 @@ export class TestRig {
// Get timeout based on environment
getDefaultTimeout() {
if (env['CI']) return 60000; // 1 minute in CI
if (env['GEMINI_SANDBOX']) return 30000; // 30s in containers
if (env['QWEN_SANDBOX']) return 30000; // 30s in containers
return 15000; // 15s locally
}
@ -181,7 +181,7 @@ export class TestRig {
otlpEndpoint: '',
outfile: telemetryPath,
},
sandbox: env.GEMINI_SANDBOX !== 'false' ? env.GEMINI_SANDBOX : false,
sandbox: env.QWEN_SANDBOX !== 'false' ? env.QWEN_SANDBOX : false,
...options.settings, // Allow tests to override/add settings
};
writeFileSync(
@ -301,7 +301,7 @@ export class TestRig {
// Filter out telemetry output when running with Podman
// Podman seems to output telemetry to stdout even when writing to file
let result = stdout;
if (env['GEMINI_SANDBOX'] === 'podman') {
if (env['QWEN_SANDBOX'] === 'podman') {
// Remove telemetry JSON objects from output
// They are multi-line JSON objects that start with { and contain telemetry fields
const lines = result.split(EOL);
@ -727,7 +727,7 @@ export class TestRig {
readToolLogs() {
// For Podman, first check if telemetry file exists and has content
// If not, fall back to parsing from stdout
if (env['GEMINI_SANDBOX'] === 'podman') {
if (env['QWEN_SANDBOX'] === 'podman') {
// Try reading from file first
const logFilePath = join(this.testDir!, 'telemetry.log');

View file

@ -32,13 +32,13 @@
"test:scripts": "vitest run --config ./scripts/tests/vitest.config.ts",
"test:e2e": "cross-env VERBOSE=true KEEP_OUTPUT=true npm run test:integration:sandbox:none",
"test:integration:all": "npm run test:integration:sandbox:none && npm run test:integration:sandbox:docker && npm run test:integration:sandbox:podman",
"test:integration:sandbox:none": "cross-env GEMINI_SANDBOX=false vitest run --root ./integration-tests",
"test:integration:sandbox:docker": "cross-env GEMINI_SANDBOX=docker npm run build:sandbox && GEMINI_SANDBOX=docker vitest run --root ./integration-tests",
"test:integration:sandbox:podman": "cross-env GEMINI_SANDBOX=podman vitest run --root ./integration-tests",
"test:integration:sdk:sandbox:none": "cross-env GEMINI_SANDBOX=false vitest run --root ./integration-tests sdk-typescript",
"test:integration:sdk:sandbox:docker": "cross-env GEMINI_SANDBOX=docker npm run build:sandbox && GEMINI_SANDBOX=docker vitest run --root ./integration-tests sdk-typescript",
"test:integration:cli:sandbox:none": "cross-env GEMINI_SANDBOX=false vitest run --root ./integration-tests --exclude '**/sdk-typescript/**'",
"test:integration:cli:sandbox:docker": "cross-env GEMINI_SANDBOX=docker npm run build:sandbox && GEMINI_SANDBOX=docker vitest run --root ./integration-tests --exclude '**/sdk-typescript/**'",
"test:integration:sandbox:none": "cross-env QWEN_SANDBOX=false vitest run --root ./integration-tests",
"test:integration:sandbox:docker": "cross-env QWEN_SANDBOX=docker npm run build:sandbox && QWEN_SANDBOX=docker vitest run --root ./integration-tests",
"test:integration:sandbox:podman": "cross-env QWEN_SANDBOX=podman vitest run --root ./integration-tests",
"test:integration:sdk:sandbox:none": "cross-env QWEN_SANDBOX=false vitest run --root ./integration-tests sdk-typescript",
"test:integration:sdk:sandbox:docker": "cross-env QWEN_SANDBOX=docker npm run build:sandbox && QWEN_SANDBOX=docker vitest run --root ./integration-tests sdk-typescript",
"test:integration:cli:sandbox:none": "cross-env QWEN_SANDBOX=false vitest run --root ./integration-tests --exclude '**/sdk-typescript/**'",
"test:integration:cli:sandbox:docker": "cross-env QWEN_SANDBOX=docker npm run build:sandbox && QWEN_SANDBOX=docker vitest run --root ./integration-tests --exclude '**/sdk-typescript/**'",
"test:terminal-bench": "cross-env VERBOSE=true KEEP_OUTPUT=true vitest run --config ./vitest.terminal-bench.config.ts --root ./integration-tests",
"test:terminal-bench:oracle": "cross-env VERBOSE=true KEEP_OUTPUT=true vitest run --config ./vitest.terminal-bench.config.ts --root ./integration-tests -t 'oracle'",
"test:terminal-bench:qwen": "cross-env VERBOSE=true KEEP_OUTPUT=true vitest run --config ./vitest.terminal-bench.config.ts --root ./integration-tests -t 'qwen'",

View file

@ -2173,8 +2173,8 @@ describe('parseArguments with positional prompt', () => {
});
describe('Telemetry configuration via environment variables', () => {
it('should prioritize GEMINI_TELEMETRY_ENABLED over settings', async () => {
vi.stubEnv('GEMINI_TELEMETRY_ENABLED', 'true');
it('should prioritize QWEN_TELEMETRY_ENABLED over settings', async () => {
vi.stubEnv('QWEN_TELEMETRY_ENABLED', 'true');
process.argv = ['node', 'script.js'];
const argv = await parseArguments();
const settings: Settings = { telemetry: { enabled: false } };
@ -2182,8 +2182,8 @@ describe('Telemetry configuration via environment variables', () => {
expect(config.getTelemetryEnabled()).toBe(true);
});
it('should prioritize GEMINI_TELEMETRY_TARGET over settings', async () => {
vi.stubEnv('GEMINI_TELEMETRY_TARGET', 'gcp');
it('should prioritize QWEN_TELEMETRY_TARGET over settings', async () => {
vi.stubEnv('QWEN_TELEMETRY_TARGET', 'gcp');
process.argv = ['node', 'script.js'];
const argv = await parseArguments();
const settings: Settings = {
@ -2193,8 +2193,8 @@ describe('Telemetry configuration via environment variables', () => {
expect(config.getTelemetryTarget()).toBe('gcp');
});
it('should throw when GEMINI_TELEMETRY_TARGET is invalid', async () => {
vi.stubEnv('GEMINI_TELEMETRY_TARGET', 'bogus');
it('should throw when QWEN_TELEMETRY_TARGET is invalid', async () => {
vi.stubEnv('QWEN_TELEMETRY_TARGET', 'bogus');
process.argv = ['node', 'script.js'];
const argv = await parseArguments();
const settings: Settings = {
@ -2206,9 +2206,9 @@ describe('Telemetry configuration via environment variables', () => {
vi.unstubAllEnvs();
});
it('should prioritize GEMINI_TELEMETRY_OTLP_ENDPOINT over settings and default env var', async () => {
it('should prioritize QWEN_TELEMETRY_OTLP_ENDPOINT over settings and default env var', async () => {
vi.stubEnv('OTEL_EXPORTER_OTLP_ENDPOINT', 'http://default.env.com');
vi.stubEnv('GEMINI_TELEMETRY_OTLP_ENDPOINT', 'http://gemini.env.com');
vi.stubEnv('QWEN_TELEMETRY_OTLP_ENDPOINT', 'http://gemini.env.com');
process.argv = ['node', 'script.js'];
const argv = await parseArguments();
const settings: Settings = {
@ -2218,8 +2218,8 @@ describe('Telemetry configuration via environment variables', () => {
expect(config.getTelemetryOtlpEndpoint()).toBe('http://gemini.env.com');
});
it('should prioritize GEMINI_TELEMETRY_OTLP_PROTOCOL over settings', async () => {
vi.stubEnv('GEMINI_TELEMETRY_OTLP_PROTOCOL', 'http');
it('should prioritize QWEN_TELEMETRY_OTLP_PROTOCOL over settings', async () => {
vi.stubEnv('QWEN_TELEMETRY_OTLP_PROTOCOL', 'http');
process.argv = ['node', 'script.js'];
const argv = await parseArguments();
const settings: Settings = { telemetry: { otlpProtocol: 'grpc' } };
@ -2227,8 +2227,8 @@ describe('Telemetry configuration via environment variables', () => {
expect(config.getTelemetryOtlpProtocol()).toBe('http');
});
it('should prioritize GEMINI_TELEMETRY_LOG_PROMPTS over settings', async () => {
vi.stubEnv('GEMINI_TELEMETRY_LOG_PROMPTS', 'false');
it('should prioritize QWEN_TELEMETRY_LOG_PROMPTS over settings', async () => {
vi.stubEnv('QWEN_TELEMETRY_LOG_PROMPTS', 'false');
process.argv = ['node', 'script.js'];
const argv = await parseArguments();
const settings: Settings = { telemetry: { logPrompts: true } };
@ -2236,8 +2236,8 @@ describe('Telemetry configuration via environment variables', () => {
expect(config.getTelemetryLogPromptsEnabled()).toBe(false);
});
it('should prioritize GEMINI_TELEMETRY_OUTFILE over settings', async () => {
vi.stubEnv('GEMINI_TELEMETRY_OUTFILE', '/gemini/env/telemetry.log');
it('should prioritize QWEN_TELEMETRY_OUTFILE over settings', async () => {
vi.stubEnv('QWEN_TELEMETRY_OUTFILE', '/gemini/env/telemetry.log');
process.argv = ['node', 'script.js'];
const argv = await parseArguments();
const settings: Settings = {
@ -2247,8 +2247,8 @@ describe('Telemetry configuration via environment variables', () => {
expect(config.getTelemetryOutfile()).toBe('/gemini/env/telemetry.log');
});
it('should prioritize GEMINI_TELEMETRY_USE_COLLECTOR over settings', async () => {
vi.stubEnv('GEMINI_TELEMETRY_USE_COLLECTOR', 'true');
it('should prioritize QWEN_TELEMETRY_USE_COLLECTOR over settings', async () => {
vi.stubEnv('QWEN_TELEMETRY_USE_COLLECTOR', 'true');
process.argv = ['node', 'script.js'];
const argv = await parseArguments();
const settings: Settings = { telemetry: { useCollector: false } };
@ -2256,8 +2256,8 @@ describe('Telemetry configuration via environment variables', () => {
expect(config.getTelemetryUseCollector()).toBe(true);
});
it('should use settings value when GEMINI_TELEMETRY_ENABLED is not set', async () => {
vi.stubEnv('GEMINI_TELEMETRY_ENABLED', undefined);
it('should use settings value when QWEN_TELEMETRY_ENABLED is not set', async () => {
vi.stubEnv('QWEN_TELEMETRY_ENABLED', undefined);
process.argv = ['node', 'script.js'];
const argv = await parseArguments();
const settings: Settings = { telemetry: { enabled: true } };
@ -2265,8 +2265,8 @@ describe('Telemetry configuration via environment variables', () => {
expect(config.getTelemetryEnabled()).toBe(true);
});
it('should use settings value when GEMINI_TELEMETRY_TARGET is not set', async () => {
vi.stubEnv('GEMINI_TELEMETRY_TARGET', undefined);
it('should use settings value when QWEN_TELEMETRY_TARGET is not set', async () => {
vi.stubEnv('QWEN_TELEMETRY_TARGET', undefined);
process.argv = ['node', 'script.js'];
const argv = await parseArguments();
const settings: Settings = {
@ -2276,16 +2276,16 @@ describe('Telemetry configuration via environment variables', () => {
expect(config.getTelemetryTarget()).toBe('local');
});
it("should treat GEMINI_TELEMETRY_ENABLED='1' as true", async () => {
vi.stubEnv('GEMINI_TELEMETRY_ENABLED', '1');
it("should treat QWEN_TELEMETRY_ENABLED='1' as true", async () => {
vi.stubEnv('QWEN_TELEMETRY_ENABLED', '1');
process.argv = ['node', 'script.js'];
const argv = await parseArguments();
const config = await loadCliConfig({}, argv, undefined, []);
expect(config.getTelemetryEnabled()).toBe(true);
});
it("should treat GEMINI_TELEMETRY_ENABLED='0' as false", async () => {
vi.stubEnv('GEMINI_TELEMETRY_ENABLED', '0');
it("should treat QWEN_TELEMETRY_ENABLED='0' as false", async () => {
vi.stubEnv('QWEN_TELEMETRY_ENABLED', '0');
process.argv = ['node', 'script.js'];
const argv = await parseArguments();
const config = await loadCliConfig(
@ -2297,16 +2297,16 @@ describe('Telemetry configuration via environment variables', () => {
expect(config.getTelemetryEnabled()).toBe(false);
});
it("should treat GEMINI_TELEMETRY_LOG_PROMPTS='1' as true", async () => {
vi.stubEnv('GEMINI_TELEMETRY_LOG_PROMPTS', '1');
it("should treat QWEN_TELEMETRY_LOG_PROMPTS='1' as true", async () => {
vi.stubEnv('QWEN_TELEMETRY_LOG_PROMPTS', '1');
process.argv = ['node', 'script.js'];
const argv = await parseArguments();
const config = await loadCliConfig({}, argv, undefined, []);
expect(config.getTelemetryLogPromptsEnabled()).toBe(true);
});
it("should treat GEMINI_TELEMETRY_LOG_PROMPTS='false' as false", async () => {
vi.stubEnv('GEMINI_TELEMETRY_LOG_PROMPTS', 'false');
it("should treat QWEN_TELEMETRY_LOG_PROMPTS='false' as false", async () => {
vi.stubEnv('QWEN_TELEMETRY_LOG_PROMPTS', 'false');
process.argv = ['node', 'script.js'];
const argv = await parseArguments();
const config = await loadCliConfig(

View file

@ -38,7 +38,7 @@ function getSandboxCommand(
// note environment variable takes precedence over argument (from command line or settings)
const environmentConfiguredSandbox =
process.env['GEMINI_SANDBOX']?.toLowerCase().trim() ?? '';
process.env['QWEN_SANDBOX']?.toLowerCase().trim() ?? '';
sandbox =
environmentConfiguredSandbox?.length > 0
? environmentConfiguredSandbox
@ -63,7 +63,7 @@ function getSandboxCommand(
return sandbox;
}
throw new FatalSandboxError(
`Missing sandbox command '${sandbox}' (from GEMINI_SANDBOX)`,
`Missing sandbox command '${sandbox}' (from QWEN_SANDBOX)`,
);
}
@ -80,8 +80,8 @@ function getSandboxCommand(
// throw an error if user requested sandbox but no command was found
if (sandbox === true) {
throw new FatalSandboxError(
'GEMINI_SANDBOX is true but failed to determine command for sandbox; ' +
'install docker or podman or specify command in GEMINI_SANDBOX',
'QWEN_SANDBOX is true but failed to determine command for sandbox; ' +
'install docker or podman or specify command in QWEN_SANDBOX',
);
}
@ -98,7 +98,7 @@ export async function loadSandboxConfig(
const packageJson = await getPackageJson();
const image =
argv.sandboxImage ??
process.env['GEMINI_SANDBOX_IMAGE'] ??
process.env['QWEN_SANDBOX_IMAGE'] ??
packageJson?.config?.sandboxImageUri;
return command && image ? { command, image } : undefined;

View file

@ -158,9 +158,9 @@ describe('Trusted Folders Loading', () => {
expect(errors[0].message).toContain('Unexpected token');
});
it('should use GEMINI_CLI_TRUSTED_FOLDERS_PATH env var if set', () => {
it('should use QWEN_CODE_TRUSTED_FOLDERS_PATH env var if set', () => {
const customPath = '/custom/path/to/trusted_folders.json';
process.env['GEMINI_CLI_TRUSTED_FOLDERS_PATH'] = customPath;
process.env['QWEN_CODE_TRUSTED_FOLDERS_PATH'] = customPath;
(mockFsExistsSync as Mock).mockImplementation((p) => p === customPath);
const userContent = {
@ -180,7 +180,7 @@ describe('Trusted Folders Loading', () => {
]);
expect(errors).toEqual([]);
delete process.env['GEMINI_CLI_TRUSTED_FOLDERS_PATH'];
delete process.env['QWEN_CODE_TRUSTED_FOLDERS_PATH'];
});
it('setValue should update the user config and save it', () => {

View file

@ -22,8 +22,8 @@ export const SETTINGS_DIRECTORY_NAME = '.qwen';
export const USER_SETTINGS_DIR = path.join(homedir(), SETTINGS_DIRECTORY_NAME);
export function getTrustedFoldersPath(): string {
if (process.env['GEMINI_CLI_TRUSTED_FOLDERS_PATH']) {
return process.env['GEMINI_CLI_TRUSTED_FOLDERS_PATH'];
if (process.env['QWEN_CODE_TRUSTED_FOLDERS_PATH']) {
return process.env['QWEN_CODE_TRUSTED_FOLDERS_PATH'];
}
return path.join(USER_SETTINGS_DIR, TRUSTED_FOLDERS_FILENAME);
}

View file

@ -112,9 +112,9 @@ describe('gemini.tsx main function', () => {
beforeEach(() => {
// Store and clear sandbox-related env variables to ensure a consistent test environment
originalEnvGeminiSandbox = process.env['GEMINI_SANDBOX'];
originalEnvGeminiSandbox = process.env['QWEN_SANDBOX'];
originalEnvSandbox = process.env['SANDBOX'];
delete process.env['GEMINI_SANDBOX'];
delete process.env['QWEN_SANDBOX'];
delete process.env['SANDBOX'];
initialUnhandledRejectionListeners =
@ -124,9 +124,9 @@ describe('gemini.tsx main function', () => {
afterEach(() => {
// Restore original env variables
if (originalEnvGeminiSandbox !== undefined) {
process.env['GEMINI_SANDBOX'] = originalEnvGeminiSandbox;
process.env['QWEN_SANDBOX'] = originalEnvGeminiSandbox;
} else {
delete process.env['GEMINI_SANDBOX'];
delete process.env['QWEN_SANDBOX'];
}
if (originalEnvSandbox !== undefined) {
process.env['SANDBOX'] = originalEnvSandbox;

View file

@ -29,7 +29,7 @@
(allow network-inbound (local ip "localhost:9229"))
;; deny all outbound network traffic EXCEPT through proxy on localhost:8877
;; set `GEMINI_SANDBOX_PROXY_COMMAND=<command>` to run proxy alongside sandbox
;; set `QWEN_SANDBOX_PROXY_COMMAND=<command>` to run proxy alongside sandbox
;; proxy must listen on :::8877 (see docs/examples/proxy-script.md)
(deny network-outbound)
(allow network-outbound (remote tcp "localhost:8877"))

View file

@ -93,6 +93,6 @@
(allow network-inbound (local ip "localhost:9229"))
;; allow outbound network traffic through proxy on localhost:8877
;; set `GEMINI_SANDBOX_PROXY_COMMAND=<command>` to run proxy alongside sandbox
;; set `QWEN_SANDBOX_PROXY_COMMAND=<command>` to run proxy alongside sandbox
;; proxy must listen on :::8877 (see docs/examples/proxy-script.md)
(allow network-outbound (remote tcp "localhost:8877"))

View file

@ -263,8 +263,8 @@ export async function start_sandbox(
...finalArgv.map((arg) => quote([arg])),
].join(' '),
);
// start and set up proxy if GEMINI_SANDBOX_PROXY_COMMAND is set
const proxyCommand = process.env['GEMINI_SANDBOX_PROXY_COMMAND'];
// start and set up proxy if QWEN_SANDBOX_PROXY_COMMAND is set
const proxyCommand = process.env['QWEN_SANDBOX_PROXY_COMMAND'];
let proxyProcess: ChildProcess | undefined = undefined;
let sandboxProcess: ChildProcess | undefined = undefined;
const sandboxEnv = { ...process.env };
@ -378,7 +378,7 @@ export async function start_sandbox(
stdio: 'inherit',
env: {
...process.env,
GEMINI_SANDBOX: config.command, // in case sandbox is enabled via flags (see config.ts under cli package)
QWEN_SANDBOX: config.command, // in case sandbox is enabled via flags (see config.ts under cli package)
},
},
);
@ -498,8 +498,8 @@ export async function start_sandbox(
// copy proxy environment variables, replacing localhost with SANDBOX_PROXY_NAME
// copy as both upper-case and lower-case as is required by some utilities
// GEMINI_SANDBOX_PROXY_COMMAND implies HTTPS_PROXY unless HTTP_PROXY is set
const proxyCommand = process.env['GEMINI_SANDBOX_PROXY_COMMAND'];
// QWEN_SANDBOX_PROXY_COMMAND implies HTTPS_PROXY unless HTTP_PROXY is set
const proxyCommand = process.env['QWEN_SANDBOX_PROXY_COMMAND'];
if (proxyCommand) {
let proxy =
@ -541,10 +541,10 @@ export async function start_sandbox(
// name container after image, plus random suffix to avoid conflicts
const imageName = parseImageName(image);
const isIntegrationTest =
process.env['GEMINI_CLI_INTEGRATION_TEST'] === 'true';
process.env['QWEN_CODE_INTEGRATION_TEST'] === 'true';
let containerName;
if (isIntegrationTest) {
containerName = `gemini-cli-integration-test-${randomBytes(4).toString(
containerName = `qwen-code-integration-test-${randomBytes(4).toString(
'hex',
)}`;
writeStderrLine(`ContainerName: ${containerName}`);
@ -563,11 +563,11 @@ export async function start_sandbox(
}
args.push('--name', containerName, '--hostname', containerName);
// copy GEMINI_CLI_TEST_VAR for integration tests
if (process.env['GEMINI_CLI_TEST_VAR']) {
// copy QWEN_CODE_TEST_VAR for integration tests
if (process.env['QWEN_CODE_TEST_VAR']) {
args.push(
'--env',
`GEMINI_CLI_TEST_VAR=${process.env['GEMINI_CLI_TEST_VAR']}`,
`QWEN_CODE_TEST_VAR=${process.env['QWEN_CODE_TEST_VAR']}`,
);
}
@ -716,7 +716,7 @@ export async function start_sandbox(
let userFlag = '';
const finalEntrypoint = entrypoint(workdir, cliArgs);
if (process.env['GEMINI_CLI_INTEGRATION_TEST'] === 'true') {
if (process.env['QWEN_CODE_INTEGRATION_TEST'] === 'true') {
args.push('--user', 'root');
userFlag = '--user root';
} else if (await shouldUseCurrentUserInSandbox()) {
@ -763,7 +763,7 @@ export async function start_sandbox(
// push container entrypoint (including args)
args.push(...finalEntrypoint);
// start and set up proxy if GEMINI_SANDBOX_PROXY_COMMAND is set
// start and set up proxy if QWEN_SANDBOX_PROXY_COMMAND is set
let proxyProcess: ChildProcess | undefined = undefined;
let sandboxProcess: ChildProcess | undefined = undefined;

View file

@ -79,13 +79,13 @@ describe('telemetry/config helpers', () => {
useCollector: false,
};
const env = {
GEMINI_TELEMETRY_ENABLED: '1',
GEMINI_TELEMETRY_TARGET: 'gcp',
GEMINI_TELEMETRY_OTLP_ENDPOINT: 'http://env:4317',
GEMINI_TELEMETRY_OTLP_PROTOCOL: 'http',
GEMINI_TELEMETRY_LOG_PROMPTS: 'true',
GEMINI_TELEMETRY_OUTFILE: 'env.log',
GEMINI_TELEMETRY_USE_COLLECTOR: 'true',
QWEN_TELEMETRY_ENABLED: '1',
QWEN_TELEMETRY_TARGET: 'gcp',
QWEN_TELEMETRY_OTLP_ENDPOINT: 'http://env:4317',
QWEN_TELEMETRY_OTLP_PROTOCOL: 'http',
QWEN_TELEMETRY_LOG_PROMPTS: 'true',
QWEN_TELEMETRY_OUTFILE: 'env.log',
QWEN_TELEMETRY_USE_COLLECTOR: 'true',
} as Record<string, string>;
const argv = {
telemetry: false,
@ -133,7 +133,7 @@ describe('telemetry/config helpers', () => {
});
it('throws on unknown protocol values', async () => {
const env = { GEMINI_TELEMETRY_OTLP_PROTOCOL: 'unknown' } as Record<
const env = { QWEN_TELEMETRY_OTLP_PROTOCOL: 'unknown' } as Record<
string,
string
>;
@ -143,7 +143,7 @@ describe('telemetry/config helpers', () => {
});
it('throws on unknown target values', async () => {
const env = { GEMINI_TELEMETRY_TARGET: 'unknown' } as Record<
const env = { QWEN_TELEMETRY_TARGET: 'unknown' } as Record<
string,
string
>;

View file

@ -57,12 +57,12 @@ export async function resolveTelemetrySettings(options: {
const enabled =
argv.telemetry ??
parseBooleanEnvFlag(env['GEMINI_TELEMETRY_ENABLED']) ??
parseBooleanEnvFlag(env['QWEN_TELEMETRY_ENABLED']) ??
settings.enabled;
const rawTarget =
(argv.telemetryTarget as string | TelemetryTarget | undefined) ??
env['GEMINI_TELEMETRY_TARGET'] ??
env['QWEN_TELEMETRY_TARGET'] ??
(settings.target as string | TelemetryTarget | undefined);
const target = parseTelemetryTargetValue(rawTarget);
if (rawTarget !== undefined && target === undefined) {
@ -75,13 +75,13 @@ export async function resolveTelemetrySettings(options: {
const otlpEndpoint =
argv.telemetryOtlpEndpoint ??
env['GEMINI_TELEMETRY_OTLP_ENDPOINT'] ??
env['QWEN_TELEMETRY_OTLP_ENDPOINT'] ??
env['OTEL_EXPORTER_OTLP_ENDPOINT'] ??
settings.otlpEndpoint;
const rawProtocol =
(argv.telemetryOtlpProtocol as string | undefined) ??
env['GEMINI_TELEMETRY_OTLP_PROTOCOL'] ??
env['QWEN_TELEMETRY_OTLP_PROTOCOL'] ??
settings.otlpProtocol;
const otlpProtocol = (['grpc', 'http'] as const).find(
(p) => p === rawProtocol,
@ -96,16 +96,14 @@ export async function resolveTelemetrySettings(options: {
const logPrompts =
argv.telemetryLogPrompts ??
parseBooleanEnvFlag(env['GEMINI_TELEMETRY_LOG_PROMPTS']) ??
parseBooleanEnvFlag(env['QWEN_TELEMETRY_LOG_PROMPTS']) ??
settings.logPrompts;
const outfile =
argv.telemetryOutfile ??
env['GEMINI_TELEMETRY_OUTFILE'] ??
settings.outfile;
argv.telemetryOutfile ?? env['QWEN_TELEMETRY_OUTFILE'] ?? settings.outfile;
const useCollector =
parseBooleanEnvFlag(env['GEMINI_TELEMETRY_USE_COLLECTOR']) ??
parseBooleanEnvFlag(env['QWEN_TELEMETRY_USE_COLLECTOR']) ??
settings.useCollector;
return {

View file

@ -173,7 +173,7 @@ function buildImage(imageName, dockerfile) {
).version;
const imageTag =
process.env.GEMINI_SANDBOX_IMAGE_TAG || imageName.split(':')[1];
process.env.QWEN_SANDBOX_IMAGE_TAG || imageName.split(':')[1];
const finalImageName = `${imageName.split(':')[0]}:${imageTag}`;
try {

View file

@ -32,27 +32,27 @@ const argv = yargs(hideBin(process.argv)).option('q', {
default: false,
}).argv;
let geminiSandbox = process.env.GEMINI_SANDBOX;
let qwenSandbox = process.env.QWEN_SANDBOX;
if (!geminiSandbox) {
if (!qwenSandbox) {
const userSettingsFile = join(os.homedir(), '.qwen', 'settings.json');
if (existsSync(userSettingsFile)) {
const settings = JSON.parse(
stripJsonComments(readFileSync(userSettingsFile, 'utf-8')),
);
if (settings.sandbox) {
geminiSandbox = settings.sandbox;
qwenSandbox = settings.sandbox;
}
}
}
if (!geminiSandbox) {
if (!qwenSandbox) {
let currentDir = process.cwd();
while (true) {
const geminiEnv = join(currentDir, '.qwen', '.env');
const qwenEnv = join(currentDir, '.qwen', '.env');
const regularEnv = join(currentDir, '.env');
if (existsSync(geminiEnv)) {
dotenv.config({ path: geminiEnv, quiet: true });
if (existsSync(qwenEnv)) {
dotenv.config({ path: qwenEnv, quiet: true });
break;
} else if (existsSync(regularEnv)) {
dotenv.config({ path: regularEnv, quiet: true });
@ -64,10 +64,10 @@ if (!geminiSandbox) {
}
currentDir = parentDir;
}
geminiSandbox = process.env.GEMINI_SANDBOX;
qwenSandbox = process.env.QWEN_SANDBOX;
}
geminiSandbox = (geminiSandbox || '').toLowerCase();
qwenSandbox = (qwenSandbox || '').toLowerCase();
const commandExists = (cmd) => {
// Use 'where.exe' (not 'where') on Windows because PowerShell aliases
@ -90,23 +90,23 @@ const commandExists = (cmd) => {
};
let command = '';
if (['1', 'true'].includes(geminiSandbox)) {
if (['1', 'true'].includes(qwenSandbox)) {
if (commandExists('docker')) {
command = 'docker';
} else if (commandExists('podman')) {
command = 'podman';
} else {
console.error(
'ERROR: install docker or podman or specify command in GEMINI_SANDBOX',
'ERROR: install docker or podman or specify command in QWEN_SANDBOX',
);
process.exit(1);
}
} else if (geminiSandbox && !['0', 'false'].includes(geminiSandbox)) {
if (commandExists(geminiSandbox)) {
command = geminiSandbox;
} else if (qwenSandbox && !['0', 'false'].includes(qwenSandbox)) {
if (commandExists(qwenSandbox)) {
command = qwenSandbox;
} else {
console.error(
`ERROR: missing sandbox command '${geminiSandbox}' (from GEMINI_SANDBOX)`,
`ERROR: missing sandbox command '${qwenSandbox}' (from QWEN_SANDBOX)`,
);
process.exit(1);
}

View file

@ -26,7 +26,7 @@ const projectHash = crypto
// User-level .gemini directory in home
const USER_GEMINI_DIR = path.join(os.homedir(), '.qwen');
// Project-level .gemini directory in the workspace
const WORKSPACE_GEMINI_DIR = path.join(projectRoot, '.qwen');
const WORKSPACE_QWEN_DIR = path.join(projectRoot, '.qwen');
// Telemetry artifacts are stored in a hashed directory under the user's ~/.qwen/tmp
export const OTEL_DIR = path.join(USER_GEMINI_DIR, 'tmp', projectHash, 'otel');
@ -34,7 +34,7 @@ export const BIN_DIR = path.join(OTEL_DIR, 'bin');
// Workspace settings remain in the project's .gemini directory
export const WORKSPACE_SETTINGS_FILE = path.join(
WORKSPACE_GEMINI_DIR,
WORKSPACE_QWEN_DIR,
'settings.json',
);