mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-04-28 03:30:40 +00:00
feat(cli): support tools.sandboxImage in settings (#3146)
Co-authored-by: jinye.djy <jinye.djy@alibaba-inc.com>
This commit is contained in:
parent
116796b2a4
commit
1557d93043
8 changed files with 146 additions and 19 deletions
|
|
@ -235,6 +235,7 @@ If you are experiencing performance issues with file searching (e.g., with `@` c
|
|||
| Setting | Type | Description | Default | Notes |
|
||||
| ------------------------------------ | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `tools.sandbox` | boolean or string | Sandbox execution environment (can be a boolean or a path string). | `undefined` | |
|
||||
| `tools.sandboxImage` | string | Sandbox image URI used by Docker/Podman when `--sandbox-image` and `QWEN_SANDBOX_IMAGE` are not set. | `undefined` | |
|
||||
| `tools.shell.enableInteractiveShell` | boolean | Use `node-pty` for an interactive shell experience. Fallback to `child_process` still applies. | `false` | |
|
||||
| `tools.core` | array of strings | **Deprecated.** Will be removed in next version. Use `permissions.allow` + `permissions.deny` instead. Restricts built-in tools to an allowlist. All tools not in the list are disabled. | `undefined` | |
|
||||
| `tools.exclude` | array of strings | **Deprecated.** Use `permissions.deny` instead. Tool names to exclude from discovery. Automatically migrated to the `permissions` format on first load. | `undefined` | |
|
||||
|
|
@ -442,6 +443,7 @@ Here is an example of a `settings.json` file with the nested structure, new as o
|
|||
"tools": {
|
||||
"approvalMode": "yolo",
|
||||
"sandbox": "docker",
|
||||
"sandboxImage": "ghcr.io/qwenlm/qwen-code:0.14.1",
|
||||
"discoveryCommand": "bin/get_tools",
|
||||
"callCommand": "bin/call_tool",
|
||||
"exclude": ["write_file"]
|
||||
|
|
@ -505,28 +507,32 @@ For authentication-related variables (like `OPENAI_*`) and the recommended `.qwe
|
|||
|
||||
### Environment Variables Table
|
||||
|
||||
| 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. |
|
||||
| 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. |
|
||||
| `QWEN_SANDBOX_IMAGE` | Overrides sandbox image selection for Docker/Podman. | Takes precedence over `tools.sandboxImage`. |
|
||||
| `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. |
|
||||
| `QWEN_CODE_MAX_OUTPUT_TOKENS` | Overrides the default maximum output tokens per response. When not set, Qwen Code uses an adaptive strategy: starts with 8K tokens and automatically retries with 64K if the response is truncated. Set this to a specific value (e.g., `16000`) to use a fixed limit instead. | Takes precedence over the capped default (8K) but is overridden by `samplingParams.max_tokens` in settings. Disables automatic escalation when set. Example: `export QWEN_CODE_MAX_OUTPUT_TOKENS=16000` |
|
||||
| `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"` |
|
||||
| `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
|
||||
|
||||
Arguments passed directly when running the CLI can override other configurations for that specific session.
|
||||
|
||||
For sandbox image selection, precedence is:
|
||||
`--sandbox-image` > `QWEN_SANDBOX_IMAGE` > `tools.sandboxImage` > built-in default image.
|
||||
|
||||
### Command-Line Arguments Table
|
||||
|
||||
| Argument | Alias | Description | Possible Values | Notes |
|
||||
|
|
|
|||
|
|
@ -103,8 +103,16 @@ qwen -p "run the test suite"
|
|||
|
||||
- **CLI flag**: `--sandbox-image <image>`
|
||||
- **Environment variable**: `QWEN_SANDBOX_IMAGE=<image>`
|
||||
- **Settings file**: `tools.sandboxImage` in your `settings.json` (e.g., `{"tools": {"sandboxImage": "ghcr.io/qwenlm/qwen-code:0.14.1"}}`)
|
||||
|
||||
If you don’t set either, Qwen Code uses the default image configured in the CLI package (for example `ghcr.io/qwenlm/qwen-code:<version>`).
|
||||
Priority order (highest to lowest):
|
||||
|
||||
1. `--sandbox-image`
|
||||
2. `QWEN_SANDBOX_IMAGE`
|
||||
3. `tools.sandboxImage`
|
||||
4. Built-in default image from the CLI package (for example `ghcr.io/qwenlm/qwen-code:<version>`)
|
||||
|
||||
`settings.env.QWEN_SANDBOX_IMAGE` also works as a generic env injection mechanism, but `tools.sandboxImage` is the preferred persistent setting.
|
||||
|
||||
### macOS Seatbelt profiles
|
||||
|
||||
|
|
|
|||
|
|
@ -111,10 +111,21 @@ vi.mock('open', () => ({
|
|||
|
||||
vi.mock('read-package-up', () => ({
|
||||
readPackageUp: vi.fn(() =>
|
||||
Promise.resolve({ packageJson: { version: 'test-version' } }),
|
||||
Promise.resolve({
|
||||
packageJson: {
|
||||
version: 'test-version',
|
||||
config: { sandboxImageUri: 'pkg-default-image' },
|
||||
},
|
||||
}),
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock('command-exists', () => ({
|
||||
default: {
|
||||
sync: vi.fn(() => true),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('@qwen-code/qwen-code-core', async (importOriginal) => {
|
||||
const actualServer = await importOriginal<typeof ServerConfig>();
|
||||
const SkillManagerMock = vi.fn();
|
||||
|
|
@ -2441,6 +2452,83 @@ describe('Telemetry configuration via environment variables', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('sandbox image resolution precedence', () => {
|
||||
const originalArgv = process.argv;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
|
||||
vi.stubEnv('GEMINI_API_KEY', 'test-api-key');
|
||||
delete process.env['QWEN_SANDBOX_IMAGE'];
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.argv = originalArgv;
|
||||
vi.unstubAllEnvs();
|
||||
vi.restoreAllMocks();
|
||||
delete process.env['QWEN_SANDBOX_IMAGE'];
|
||||
});
|
||||
|
||||
it('uses --sandbox-image over env and settings', async () => {
|
||||
vi.stubEnv('QWEN_SANDBOX_IMAGE', 'env-image');
|
||||
process.argv = [
|
||||
'node',
|
||||
'script.js',
|
||||
'--sandbox',
|
||||
'--sandbox-image',
|
||||
'cli-image',
|
||||
];
|
||||
const argv = await parseArguments();
|
||||
const settings: Settings = {
|
||||
tools: {
|
||||
sandbox: true,
|
||||
sandboxImage: 'settings-image',
|
||||
},
|
||||
};
|
||||
const config = await loadCliConfig(settings, argv, undefined, []);
|
||||
expect(config.getSandbox()?.image).toBe('cli-image');
|
||||
});
|
||||
|
||||
it('uses QWEN_SANDBOX_IMAGE over tools.sandboxImage', async () => {
|
||||
vi.stubEnv('QWEN_SANDBOX_IMAGE', 'env-image');
|
||||
process.argv = ['node', 'script.js', '--sandbox'];
|
||||
const argv = await parseArguments();
|
||||
const settings: Settings = {
|
||||
tools: {
|
||||
sandbox: true,
|
||||
sandboxImage: 'settings-image',
|
||||
},
|
||||
};
|
||||
const config = await loadCliConfig(settings, argv, undefined, []);
|
||||
expect(config.getSandbox()?.image).toBe('env-image');
|
||||
});
|
||||
|
||||
it('uses tools.sandboxImage when cli and env are absent', async () => {
|
||||
process.argv = ['node', 'script.js', '--sandbox'];
|
||||
const argv = await parseArguments();
|
||||
const settings: Settings = {
|
||||
tools: {
|
||||
sandbox: true,
|
||||
sandboxImage: 'settings-image',
|
||||
},
|
||||
};
|
||||
const config = await loadCliConfig(settings, argv, undefined, []);
|
||||
expect(config.getSandbox()?.image).toBe('settings-image');
|
||||
});
|
||||
|
||||
it('falls back to package default image when no explicit source is provided', async () => {
|
||||
process.argv = ['node', 'script.js', '--sandbox'];
|
||||
const argv = await parseArguments();
|
||||
const settings: Settings = {
|
||||
tools: {
|
||||
sandbox: true,
|
||||
},
|
||||
};
|
||||
const config = await loadCliConfig(settings, argv, undefined, []);
|
||||
expect(config.getSandbox()?.image).toBe('pkg-default-image');
|
||||
});
|
||||
});
|
||||
|
||||
describe('loadCliConfig runtimeOutputDir', () => {
|
||||
const originalArgv = process.argv;
|
||||
const originalRuntimeEnv = process.env['QWEN_RUNTIME_DIR'];
|
||||
|
|
|
|||
|
|
@ -514,7 +514,7 @@ export async function parseArguments(): Promise<CliArgs> {
|
|||
})
|
||||
.deprecateOption(
|
||||
'sandbox-image',
|
||||
'Use the "tools.sandbox" setting in settings.json instead. This flag will be removed in a future version.',
|
||||
'Use the "tools.sandboxImage" setting in settings.json instead. This flag will be removed in a future version.',
|
||||
)
|
||||
.deprecateOption(
|
||||
'checkpointing',
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ export async function loadSandboxConfig(
|
|||
const image =
|
||||
argv.sandboxImage ??
|
||||
process.env['QWEN_SANDBOX_IMAGE'] ??
|
||||
settings.tools?.sandboxImage ??
|
||||
packageJson?.config?.sandboxImageUri;
|
||||
|
||||
return command && image ? { command, image } : undefined;
|
||||
|
|
|
|||
|
|
@ -110,6 +110,16 @@ describe('SettingsSchema', () => {
|
|||
).toBeDefined();
|
||||
});
|
||||
|
||||
it('should have sandboxImage setting under tools', () => {
|
||||
expect(getSettingsSchema().tools.properties.sandboxImage).toBeDefined();
|
||||
expect(getSettingsSchema().tools.properties.sandboxImage.type).toBe(
|
||||
'string',
|
||||
);
|
||||
expect(getSettingsSchema().tools.properties.sandboxImage.default).toBe(
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
it('should have unique categories', () => {
|
||||
const categories = new Set();
|
||||
|
||||
|
|
|
|||
|
|
@ -1014,6 +1014,16 @@ const SETTINGS_SCHEMA = {
|
|||
'Sandbox execution environment (can be a boolean or a path string).',
|
||||
showInDialog: false,
|
||||
},
|
||||
sandboxImage: {
|
||||
type: 'string',
|
||||
label: 'Sandbox Image',
|
||||
category: 'Tools',
|
||||
requiresRestart: true,
|
||||
default: undefined as string | undefined,
|
||||
description:
|
||||
'Sandbox image URI used by Docker/Podman when --sandbox-image and QWEN_SANDBOX_IMAGE are not set.',
|
||||
showInDialog: false,
|
||||
},
|
||||
shell: {
|
||||
type: 'object',
|
||||
label: 'Shell',
|
||||
|
|
|
|||
|
|
@ -438,6 +438,10 @@
|
|||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"sandboxImage": {
|
||||
"description": "Sandbox image URI used by Docker/Podman when --sandbox-image and QWEN_SANDBOX_IMAGE are not set.",
|
||||
"type": "string"
|
||||
},
|
||||
"shell": {
|
||||
"description": "Settings for shell execution.",
|
||||
"type": "object",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue