From cbeaebfe17cfe33b56847da2bd94845612f1e4da Mon Sep 17 00:00:00 2001 From: juhuan <46651841+juhuan@users.noreply.github.com> Date: Wed, 8 Jul 2026 00:35:06 +0800 Subject: [PATCH] feat(sdk): expose sandbox, safe_mode, insecure, worktree CLI flags Add boolean options to both Python and TS SDKs: - sandbox (--sandbox): run in sandbox mode - safe_mode (--safe-mode): disable all customizations for troubleshooting - insecure (--insecure): skip TLS certificate verification - worktree (--worktree): enable Git worktree mode --- .../sdk-python/src/qwen_code_sdk/transport.py | 12 ++++ .../sdk-python/src/qwen_code_sdk/types.py | 12 ++++ .../sdk-python/tests/unit/test_transport.py | 32 +++++++++++ .../sdk-typescript/src/query/createQuery.ts | 4 ++ .../src/transport/ProcessTransport.ts | 16 ++++++ .../src/types/queryOptionsSchema.ts | 4 ++ packages/sdk-typescript/src/types/types.ts | 34 +++++++++++ .../test/unit/ProcessTransport.test.ts | 57 +++++++++++++++++++ 8 files changed, 171 insertions(+) diff --git a/packages/sdk-python/src/qwen_code_sdk/transport.py b/packages/sdk-python/src/qwen_code_sdk/transport.py index 8542364943f..1e230d0c818 100644 --- a/packages/sdk-python/src/qwen_code_sdk/transport.py +++ b/packages/sdk-python/src/qwen_code_sdk/transport.py @@ -240,4 +240,16 @@ def build_cli_arguments(options: QueryOptions) -> list[str]: elif options.session_id: args.extend(["--session-id", options.session_id]) + if options.sandbox: + args.append("--sandbox") + + if options.safe_mode: + args.append("--safe-mode") + + if options.insecure: + args.append("--insecure") + + if options.worktree: + args.append("--worktree") + return args diff --git a/packages/sdk-python/src/qwen_code_sdk/types.py b/packages/sdk-python/src/qwen_code_sdk/types.py index 3d8ec72038e..73d38fa55f7 100644 --- a/packages/sdk-python/src/qwen_code_sdk/types.py +++ b/packages/sdk-python/src/qwen_code_sdk/types.py @@ -114,6 +114,10 @@ class QueryOptionsDict(TypedDict, total=False): timeout: TimeoutOptionsDict mcp_servers: dict[str, dict[str, Any]] stderr: Callable[[str], None] + sandbox: bool + safe_mode: bool + insecure: bool + worktree: bool @dataclass @@ -139,6 +143,10 @@ class QueryOptions: timeout: TimeoutOptions = TimeoutOptions() mcp_servers: dict[str, dict[str, Any]] | None = None stderr: Callable[[str], None] | None = None + sandbox: bool = False + safe_mode: bool = False + insecure: bool = False + worktree: bool = False @classmethod def from_mapping(cls, value: Mapping[str, Any] | None) -> QueryOptions: @@ -183,6 +191,10 @@ def from_mapping(cls, value: Mapping[str, Any] | None) -> QueryOptions: Callable[[str], None] | None, _as_optional_callable(data, "stderr"), ), + sandbox=_as_optional_bool(data, "sandbox") or False, + safe_mode=_as_optional_bool(data, "safe_mode") or False, + insecure=_as_optional_bool(data, "insecure") or False, + worktree=_as_optional_bool(data, "worktree") or False, ) diff --git a/packages/sdk-python/tests/unit/test_transport.py b/packages/sdk-python/tests/unit/test_transport.py index 340d1c1e662..2a541a0a9ea 100644 --- a/packages/sdk-python/tests/unit/test_transport.py +++ b/packages/sdk-python/tests/unit/test_transport.py @@ -82,6 +82,38 @@ def test_cli_argument_precedence_prefers_resume_then_continue_then_session_id() assert "--session-id" not in args +def test_build_cli_arguments_includes_boolean_flags() -> None: + args = build_cli_arguments( + QueryOptions( + sandbox=True, + safe_mode=True, + insecure=True, + worktree=True, + ) + ) + + assert "--sandbox" in args + assert "--safe-mode" in args + assert "--insecure" in args + assert "--worktree" in args + + +def test_build_cli_arguments_omits_false_boolean_flags() -> None: + args = build_cli_arguments( + QueryOptions( + sandbox=False, + safe_mode=False, + insecure=False, + worktree=False, + ) + ) + + assert "--sandbox" not in args + assert "--safe-mode" not in args + assert "--insecure" not in args + assert "--worktree" not in args + + def test_prepare_spawn_info_uses_runtime_for_python_scripts(tmp_path: Path) -> None: script_path = tmp_path / "fake-qwen.py" script_path.write_text("print('ok')\n", encoding="utf-8") diff --git a/packages/sdk-typescript/src/query/createQuery.ts b/packages/sdk-typescript/src/query/createQuery.ts index 42d332b15f1..f493de231a0 100644 --- a/packages/sdk-typescript/src/query/createQuery.ts +++ b/packages/sdk-typescript/src/query/createQuery.ts @@ -70,6 +70,10 @@ export function query({ includePartialMessages: options.includePartialMessages, resume: options.resume, sessionId, + sandbox: options.sandbox, + safeMode: options.safeMode, + insecure: options.insecure, + worktree: options.worktree, }); const queryOptions: QueryOptions = { diff --git a/packages/sdk-typescript/src/transport/ProcessTransport.ts b/packages/sdk-typescript/src/transport/ProcessTransport.ts index 1a63e96d550..52a3420f05e 100644 --- a/packages/sdk-typescript/src/transport/ProcessTransport.ts +++ b/packages/sdk-typescript/src/transport/ProcessTransport.ts @@ -335,6 +335,22 @@ export class ProcessTransport implements Transport { args.push('--session-id', this.options.sessionId); } + if (this.options.sandbox) { + args.push('--sandbox'); + } + + if (this.options.safeMode) { + args.push('--safe-mode'); + } + + if (this.options.insecure) { + args.push('--insecure'); + } + + if (this.options.worktree) { + args.push('--worktree'); + } + return args; } diff --git a/packages/sdk-typescript/src/types/queryOptionsSchema.ts b/packages/sdk-typescript/src/types/queryOptionsSchema.ts index 702ea1c632d..94ec18f401e 100644 --- a/packages/sdk-typescript/src/types/queryOptionsSchema.ts +++ b/packages/sdk-typescript/src/types/queryOptionsSchema.ts @@ -181,6 +181,10 @@ export const QueryOptionsSchema = z includePartialMessages: z.boolean().optional(), resume: z.string().optional(), sessionId: z.string().optional(), + sandbox: z.boolean().optional(), + safeMode: z.boolean().optional(), + insecure: z.boolean().optional(), + worktree: z.boolean().optional(), timeout: TimeoutConfigSchema.optional(), }) .strict(); diff --git a/packages/sdk-typescript/src/types/types.ts b/packages/sdk-typescript/src/types/types.ts index 3a7e26ee0aa..d7cb803a902 100644 --- a/packages/sdk-typescript/src/types/types.ts +++ b/packages/sdk-typescript/src/types/types.ts @@ -46,6 +46,10 @@ export type TransportOptions = { * When resume is provided, this should match the resume ID. */ sessionId?: string; + sandbox?: boolean; + safeMode?: boolean; + insecure?: boolean; + worktree?: boolean; }; export interface QuerySystemPromptPreset { @@ -465,6 +469,36 @@ export interface QueryOptions { */ sessionId?: string; + /** + * Run in sandbox mode. + * Equivalent to CLI's `--sandbox` flag. + * @default false + */ + sandbox?: boolean; + + /** + * Disable all customizations (context files, hooks, extensions, skills, MCP servers) + * for troubleshooting. + * Equivalent to CLI's `--safe-mode` flag. + * @default false + */ + safeMode?: boolean; + + /** + * Skip TLS certificate verification for API connections. + * Equivalent to CLI's `--insecure` flag. + * WARNING: Removes protection against man-in-the-middle attacks. + * @default false + */ + insecure?: boolean; + + /** + * Enable Git worktree mode. + * Equivalent to CLI's `--worktree` flag. + * @default false + */ + worktree?: boolean; + /** * Timeout configuration for various SDK operations. * All values are in milliseconds. diff --git a/packages/sdk-typescript/test/unit/ProcessTransport.test.ts b/packages/sdk-typescript/test/unit/ProcessTransport.test.ts index 65bf2cbe261..5dd91c92cf5 100644 --- a/packages/sdk-typescript/test/unit/ProcessTransport.test.ts +++ b/packages/sdk-typescript/test/unit/ProcessTransport.test.ts @@ -352,6 +352,63 @@ describe('ProcessTransport', () => { ); }); + it('should include boolean flags when set to true', () => { + mockPrepareSpawnInfo.mockReturnValue({ + command: 'qwen', + args: [], + type: 'native', + originalInput: 'qwen', + }); + mockSpawn.mockReturnValue(mockChildProcess); + + const options: TransportOptions = { + pathToQwenExecutable: 'qwen', + sandbox: true, + safeMode: true, + insecure: true, + worktree: true, + }; + + new ProcessTransport(options); + + expect(mockSpawn).toHaveBeenCalledWith( + 'qwen', + expect.arrayContaining([ + '--sandbox', + '--safe-mode', + '--insecure', + '--worktree', + ]), + expect.any(Object), + ); + }); + + it('should omit boolean flags when set to false', () => { + mockPrepareSpawnInfo.mockReturnValue({ + command: 'qwen', + args: [], + type: 'native', + originalInput: 'qwen', + }); + mockSpawn.mockReturnValue(mockChildProcess); + + const options: TransportOptions = { + pathToQwenExecutable: 'qwen', + sandbox: false, + safeMode: false, + insecure: false, + worktree: false, + }; + + new ProcessTransport(options); + + const spawnCall = mockSpawn.mock.calls[0]?.[1] as string[]; + expect(spawnCall).not.toContain('--sandbox'); + expect(spawnCall).not.toContain('--safe-mode'); + expect(spawnCall).not.toContain('--insecure'); + expect(spawnCall).not.toContain('--worktree'); + }); + it('should throw if aborted before initialization', () => { mockPrepareSpawnInfo.mockReturnValue({ command: 'qwen',