mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-04-28 11:41:04 +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
|
|
@ -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',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue