mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-05 09:33:07 +00:00
Merge pull request #304 from firstfloris/fix/sandbox-cloud-storage-compatibility
fix: auto-disable sandbox mode for cloud storage paths
This commit is contained in:
@@ -16,6 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Options } from '@anthropic-ai/claude-agent-sdk';
|
import type { Options } from '@anthropic-ai/claude-agent-sdk';
|
||||||
|
import os from 'os';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { resolveModelString } from '@automaker/model-resolver';
|
import { resolveModelString } from '@automaker/model-resolver';
|
||||||
import { DEFAULT_MODELS, CLAUDE_MODEL_MAP, type McpServerConfig } from '@automaker/types';
|
import { DEFAULT_MODELS, CLAUDE_MODEL_MAP, type McpServerConfig } from '@automaker/types';
|
||||||
@@ -47,6 +48,128 @@ export function validateWorkingDirectory(cwd: string): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Known cloud storage path patterns where sandbox mode is incompatible.
|
||||||
|
*
|
||||||
|
* The Claude CLI sandbox feature uses filesystem isolation that conflicts with
|
||||||
|
* cloud storage providers' virtual filesystem implementations. This causes the
|
||||||
|
* Claude process to exit with code 1 when sandbox is enabled for these paths.
|
||||||
|
*
|
||||||
|
* Affected providers (macOS paths):
|
||||||
|
* - Dropbox: ~/Library/CloudStorage/Dropbox-*
|
||||||
|
* - Google Drive: ~/Library/CloudStorage/GoogleDrive-*
|
||||||
|
* - OneDrive: ~/Library/CloudStorage/OneDrive-*
|
||||||
|
* - iCloud Drive: ~/Library/Mobile Documents/
|
||||||
|
* - Box: ~/Library/CloudStorage/Box-*
|
||||||
|
*
|
||||||
|
* @see https://github.com/anthropics/claude-code/issues/XXX (TODO: file upstream issue)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* macOS-specific cloud storage patterns that appear under ~/Library/
|
||||||
|
* These are specific enough to use with includes() safely.
|
||||||
|
*/
|
||||||
|
const MACOS_CLOUD_STORAGE_PATTERNS = [
|
||||||
|
'/Library/CloudStorage/', // Dropbox, Google Drive, OneDrive, Box on macOS
|
||||||
|
'/Library/Mobile Documents/', // iCloud Drive on macOS
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic cloud storage folder names that need to be anchored to the home directory
|
||||||
|
* to avoid false positives (e.g., /home/user/my-project-about-dropbox/).
|
||||||
|
*/
|
||||||
|
const HOME_ANCHORED_CLOUD_FOLDERS = [
|
||||||
|
'Google Drive', // Google Drive on some systems
|
||||||
|
'Dropbox', // Dropbox on Linux/alternative installs
|
||||||
|
'OneDrive', // OneDrive on Linux/alternative installs
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a path is within a cloud storage location.
|
||||||
|
*
|
||||||
|
* Cloud storage providers use virtual filesystem implementations that are
|
||||||
|
* incompatible with the Claude CLI sandbox feature, causing process crashes.
|
||||||
|
*
|
||||||
|
* Uses two detection strategies:
|
||||||
|
* 1. macOS-specific patterns (under ~/Library/) - checked via includes()
|
||||||
|
* 2. Generic folder names - anchored to home directory to avoid false positives
|
||||||
|
*
|
||||||
|
* @param cwd - The working directory path to check
|
||||||
|
* @returns true if the path is in a cloud storage location
|
||||||
|
*/
|
||||||
|
export function isCloudStoragePath(cwd: string): boolean {
|
||||||
|
const resolvedPath = path.resolve(cwd);
|
||||||
|
|
||||||
|
// Check macOS-specific patterns (these are specific enough to use includes)
|
||||||
|
if (MACOS_CLOUD_STORAGE_PATTERNS.some((pattern) => resolvedPath.includes(pattern))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check home-anchored patterns to avoid false positives
|
||||||
|
// e.g., /home/user/my-project-about-dropbox/ should NOT match
|
||||||
|
const home = os.homedir();
|
||||||
|
for (const folder of HOME_ANCHORED_CLOUD_FOLDERS) {
|
||||||
|
const cloudPath = path.join(home, folder);
|
||||||
|
// Check if resolved path starts with the cloud storage path followed by a separator
|
||||||
|
// This ensures we match ~/Dropbox/project but not ~/Dropbox-archive or ~/my-dropbox-tool
|
||||||
|
if (resolvedPath === cloudPath || resolvedPath.startsWith(cloudPath + path.sep)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of sandbox compatibility check
|
||||||
|
*/
|
||||||
|
export interface SandboxCheckResult {
|
||||||
|
/** Whether sandbox should be enabled */
|
||||||
|
enabled: boolean;
|
||||||
|
/** If disabled, the reason why */
|
||||||
|
disabledReason?: 'cloud_storage' | 'user_setting';
|
||||||
|
/** Human-readable message for logging/UI */
|
||||||
|
message?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if sandbox mode should be enabled for a given configuration.
|
||||||
|
*
|
||||||
|
* Sandbox mode is automatically disabled for cloud storage paths because the
|
||||||
|
* Claude CLI sandbox feature is incompatible with virtual filesystem
|
||||||
|
* implementations used by cloud storage providers (Dropbox, Google Drive, etc.).
|
||||||
|
*
|
||||||
|
* @param cwd - The working directory
|
||||||
|
* @param enableSandboxMode - User's sandbox mode setting
|
||||||
|
* @returns SandboxCheckResult with enabled status and reason if disabled
|
||||||
|
*/
|
||||||
|
export function checkSandboxCompatibility(
|
||||||
|
cwd: string,
|
||||||
|
enableSandboxMode?: boolean
|
||||||
|
): SandboxCheckResult {
|
||||||
|
// User has explicitly disabled sandbox mode
|
||||||
|
if (enableSandboxMode === false) {
|
||||||
|
return {
|
||||||
|
enabled: false,
|
||||||
|
disabledReason: 'user_setting',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for cloud storage incompatibility (applies when enabled or undefined)
|
||||||
|
if (isCloudStoragePath(cwd)) {
|
||||||
|
return {
|
||||||
|
enabled: false,
|
||||||
|
disabledReason: 'cloud_storage',
|
||||||
|
message: `Sandbox mode auto-disabled: Project is in a cloud storage location (${cwd}). The Claude CLI sandbox feature is incompatible with cloud storage filesystems. To use sandbox mode, move your project to a local directory.`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sandbox is compatible and enabled (true or undefined defaults to enabled)
|
||||||
|
return {
|
||||||
|
enabled: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tool presets for different use cases
|
* Tool presets for different use cases
|
||||||
*/
|
*/
|
||||||
@@ -381,7 +504,7 @@ export function createSuggestionsOptions(config: CreateSdkOptionsConfig): Option
|
|||||||
* - Full tool access for code modification
|
* - Full tool access for code modification
|
||||||
* - Standard turns for interactive sessions
|
* - Standard turns for interactive sessions
|
||||||
* - Model priority: explicit model > session model > chat default
|
* - Model priority: explicit model > session model > chat default
|
||||||
* - Sandbox mode controlled by enableSandboxMode setting
|
* - Sandbox mode controlled by enableSandboxMode setting (auto-disabled for cloud storage)
|
||||||
* - When autoLoadClaudeMd is true, uses preset mode and settingSources for CLAUDE.md loading
|
* - When autoLoadClaudeMd is true, uses preset mode and settingSources for CLAUDE.md loading
|
||||||
*/
|
*/
|
||||||
export function createChatOptions(config: CreateSdkOptionsConfig): Options {
|
export function createChatOptions(config: CreateSdkOptionsConfig): Options {
|
||||||
@@ -397,6 +520,9 @@ export function createChatOptions(config: CreateSdkOptionsConfig): Options {
|
|||||||
// Build MCP-related options
|
// Build MCP-related options
|
||||||
const mcpOptions = buildMcpOptions(config);
|
const mcpOptions = buildMcpOptions(config);
|
||||||
|
|
||||||
|
// Check sandbox compatibility (auto-disables for cloud storage paths)
|
||||||
|
const sandboxCheck = checkSandboxCompatibility(config.cwd, config.enableSandboxMode);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...getBaseOptions(),
|
...getBaseOptions(),
|
||||||
model: getModelForUseCase('chat', effectiveModel),
|
model: getModelForUseCase('chat', effectiveModel),
|
||||||
@@ -406,7 +532,7 @@ export function createChatOptions(config: CreateSdkOptionsConfig): Options {
|
|||||||
...(mcpOptions.shouldRestrictTools && { allowedTools: [...TOOL_PRESETS.chat] }),
|
...(mcpOptions.shouldRestrictTools && { allowedTools: [...TOOL_PRESETS.chat] }),
|
||||||
// Apply MCP bypass options if configured
|
// Apply MCP bypass options if configured
|
||||||
...mcpOptions.bypassOptions,
|
...mcpOptions.bypassOptions,
|
||||||
...(config.enableSandboxMode && {
|
...(sandboxCheck.enabled && {
|
||||||
sandbox: {
|
sandbox: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
autoAllowBashIfSandboxed: true,
|
autoAllowBashIfSandboxed: true,
|
||||||
@@ -425,7 +551,7 @@ export function createChatOptions(config: CreateSdkOptionsConfig): Options {
|
|||||||
* - Full tool access for code modification and implementation
|
* - Full tool access for code modification and implementation
|
||||||
* - Extended turns for thorough feature implementation
|
* - Extended turns for thorough feature implementation
|
||||||
* - Uses default model (can be overridden)
|
* - Uses default model (can be overridden)
|
||||||
* - Sandbox mode controlled by enableSandboxMode setting
|
* - Sandbox mode controlled by enableSandboxMode setting (auto-disabled for cloud storage)
|
||||||
* - When autoLoadClaudeMd is true, uses preset mode and settingSources for CLAUDE.md loading
|
* - When autoLoadClaudeMd is true, uses preset mode and settingSources for CLAUDE.md loading
|
||||||
*/
|
*/
|
||||||
export function createAutoModeOptions(config: CreateSdkOptionsConfig): Options {
|
export function createAutoModeOptions(config: CreateSdkOptionsConfig): Options {
|
||||||
@@ -438,6 +564,9 @@ export function createAutoModeOptions(config: CreateSdkOptionsConfig): Options {
|
|||||||
// Build MCP-related options
|
// Build MCP-related options
|
||||||
const mcpOptions = buildMcpOptions(config);
|
const mcpOptions = buildMcpOptions(config);
|
||||||
|
|
||||||
|
// Check sandbox compatibility (auto-disables for cloud storage paths)
|
||||||
|
const sandboxCheck = checkSandboxCompatibility(config.cwd, config.enableSandboxMode);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...getBaseOptions(),
|
...getBaseOptions(),
|
||||||
model: getModelForUseCase('auto', config.model),
|
model: getModelForUseCase('auto', config.model),
|
||||||
@@ -447,7 +576,7 @@ export function createAutoModeOptions(config: CreateSdkOptionsConfig): Options {
|
|||||||
...(mcpOptions.shouldRestrictTools && { allowedTools: [...TOOL_PRESETS.fullAccess] }),
|
...(mcpOptions.shouldRestrictTools && { allowedTools: [...TOOL_PRESETS.fullAccess] }),
|
||||||
// Apply MCP bypass options if configured
|
// Apply MCP bypass options if configured
|
||||||
...mcpOptions.bypassOptions,
|
...mcpOptions.bypassOptions,
|
||||||
...(config.enableSandboxMode && {
|
...(sandboxCheck.enabled && {
|
||||||
sandbox: {
|
sandbox: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
autoAllowBashIfSandboxed: true,
|
autoAllowBashIfSandboxed: true,
|
||||||
|
|||||||
@@ -1,15 +1,161 @@
|
|||||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||||
|
import os from 'os';
|
||||||
|
|
||||||
describe('sdk-options.ts', () => {
|
describe('sdk-options.ts', () => {
|
||||||
let originalEnv: NodeJS.ProcessEnv;
|
let originalEnv: NodeJS.ProcessEnv;
|
||||||
|
let homedirSpy: ReturnType<typeof vi.spyOn>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
originalEnv = { ...process.env };
|
originalEnv = { ...process.env };
|
||||||
vi.resetModules();
|
vi.resetModules();
|
||||||
|
// Spy on os.homedir and set default return value
|
||||||
|
homedirSpy = vi.spyOn(os, 'homedir').mockReturnValue('/Users/test');
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
process.env = originalEnv;
|
process.env = originalEnv;
|
||||||
|
homedirSpy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isCloudStoragePath', () => {
|
||||||
|
it('should detect Dropbox paths on macOS', async () => {
|
||||||
|
const { isCloudStoragePath } = await import('@/lib/sdk-options.js');
|
||||||
|
expect(isCloudStoragePath('/Users/test/Library/CloudStorage/Dropbox-Personal/project')).toBe(
|
||||||
|
true
|
||||||
|
);
|
||||||
|
expect(isCloudStoragePath('/Users/test/Library/CloudStorage/Dropbox/project')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should detect Google Drive paths on macOS', async () => {
|
||||||
|
const { isCloudStoragePath } = await import('@/lib/sdk-options.js');
|
||||||
|
expect(
|
||||||
|
isCloudStoragePath('/Users/test/Library/CloudStorage/GoogleDrive-user@gmail.com/project')
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should detect OneDrive paths on macOS', async () => {
|
||||||
|
const { isCloudStoragePath } = await import('@/lib/sdk-options.js');
|
||||||
|
expect(isCloudStoragePath('/Users/test/Library/CloudStorage/OneDrive-Personal/project')).toBe(
|
||||||
|
true
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should detect iCloud Drive paths on macOS', async () => {
|
||||||
|
const { isCloudStoragePath } = await import('@/lib/sdk-options.js');
|
||||||
|
expect(
|
||||||
|
isCloudStoragePath('/Users/test/Library/Mobile Documents/com~apple~CloudDocs/project')
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should detect home-anchored Dropbox paths', async () => {
|
||||||
|
const { isCloudStoragePath } = await import('@/lib/sdk-options.js');
|
||||||
|
expect(isCloudStoragePath('/Users/test/Dropbox')).toBe(true);
|
||||||
|
expect(isCloudStoragePath('/Users/test/Dropbox/project')).toBe(true);
|
||||||
|
expect(isCloudStoragePath('/Users/test/Dropbox/nested/deep/project')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should detect home-anchored Google Drive paths', async () => {
|
||||||
|
const { isCloudStoragePath } = await import('@/lib/sdk-options.js');
|
||||||
|
expect(isCloudStoragePath('/Users/test/Google Drive')).toBe(true);
|
||||||
|
expect(isCloudStoragePath('/Users/test/Google Drive/project')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should detect home-anchored OneDrive paths', async () => {
|
||||||
|
const { isCloudStoragePath } = await import('@/lib/sdk-options.js');
|
||||||
|
expect(isCloudStoragePath('/Users/test/OneDrive')).toBe(true);
|
||||||
|
expect(isCloudStoragePath('/Users/test/OneDrive/project')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false for local paths', async () => {
|
||||||
|
const { isCloudStoragePath } = await import('@/lib/sdk-options.js');
|
||||||
|
expect(isCloudStoragePath('/Users/test/projects/myapp')).toBe(false);
|
||||||
|
expect(isCloudStoragePath('/home/user/code/project')).toBe(false);
|
||||||
|
expect(isCloudStoragePath('/var/www/app')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false for relative paths not in cloud storage', async () => {
|
||||||
|
const { isCloudStoragePath } = await import('@/lib/sdk-options.js');
|
||||||
|
expect(isCloudStoragePath('./project')).toBe(false);
|
||||||
|
expect(isCloudStoragePath('../other-project')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Tests for false positive prevention - paths that contain cloud storage names but aren't cloud storage
|
||||||
|
it('should NOT flag paths that merely contain "dropbox" in the name', async () => {
|
||||||
|
const { isCloudStoragePath } = await import('@/lib/sdk-options.js');
|
||||||
|
// Projects with dropbox-like names
|
||||||
|
expect(isCloudStoragePath('/home/user/my-project-about-dropbox')).toBe(false);
|
||||||
|
expect(isCloudStoragePath('/Users/test/projects/dropbox-clone')).toBe(false);
|
||||||
|
expect(isCloudStoragePath('/Users/test/projects/Dropbox-backup-tool')).toBe(false);
|
||||||
|
// Dropbox folder that's NOT in the home directory
|
||||||
|
expect(isCloudStoragePath('/var/shared/Dropbox/project')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should NOT flag paths that merely contain "Google Drive" in the name', async () => {
|
||||||
|
const { isCloudStoragePath } = await import('@/lib/sdk-options.js');
|
||||||
|
expect(isCloudStoragePath('/Users/test/projects/google-drive-api-client')).toBe(false);
|
||||||
|
expect(isCloudStoragePath('/home/user/Google Drive API Tests')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should NOT flag paths that merely contain "OneDrive" in the name', async () => {
|
||||||
|
const { isCloudStoragePath } = await import('@/lib/sdk-options.js');
|
||||||
|
expect(isCloudStoragePath('/Users/test/projects/onedrive-sync-tool')).toBe(false);
|
||||||
|
expect(isCloudStoragePath('/home/user/OneDrive-migration-scripts')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle different home directories correctly', async () => {
|
||||||
|
// Change the mocked home directory
|
||||||
|
homedirSpy.mockReturnValue('/home/linuxuser');
|
||||||
|
const { isCloudStoragePath } = await import('@/lib/sdk-options.js');
|
||||||
|
|
||||||
|
// Should detect Dropbox under the Linux home directory
|
||||||
|
expect(isCloudStoragePath('/home/linuxuser/Dropbox/project')).toBe(true);
|
||||||
|
// Should NOT detect Dropbox under the old home directory (since home changed)
|
||||||
|
expect(isCloudStoragePath('/Users/test/Dropbox/project')).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('checkSandboxCompatibility', () => {
|
||||||
|
it('should return enabled=false when user disables sandbox', async () => {
|
||||||
|
const { checkSandboxCompatibility } = await import('@/lib/sdk-options.js');
|
||||||
|
const result = checkSandboxCompatibility('/Users/test/project', false);
|
||||||
|
expect(result.enabled).toBe(false);
|
||||||
|
expect(result.disabledReason).toBe('user_setting');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return enabled=false for cloud storage paths even when sandbox enabled', async () => {
|
||||||
|
const { checkSandboxCompatibility } = await import('@/lib/sdk-options.js');
|
||||||
|
const result = checkSandboxCompatibility(
|
||||||
|
'/Users/test/Library/CloudStorage/Dropbox-Personal/project',
|
||||||
|
true
|
||||||
|
);
|
||||||
|
expect(result.enabled).toBe(false);
|
||||||
|
expect(result.disabledReason).toBe('cloud_storage');
|
||||||
|
expect(result.message).toContain('cloud storage');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return enabled=true for local paths when sandbox enabled', async () => {
|
||||||
|
const { checkSandboxCompatibility } = await import('@/lib/sdk-options.js');
|
||||||
|
const result = checkSandboxCompatibility('/Users/test/projects/myapp', true);
|
||||||
|
expect(result.enabled).toBe(true);
|
||||||
|
expect(result.disabledReason).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return enabled=true when enableSandboxMode is undefined for local paths', async () => {
|
||||||
|
const { checkSandboxCompatibility } = await import('@/lib/sdk-options.js');
|
||||||
|
const result = checkSandboxCompatibility('/Users/test/project', undefined);
|
||||||
|
expect(result.enabled).toBe(true);
|
||||||
|
expect(result.disabledReason).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return enabled=false for cloud storage paths when enableSandboxMode is undefined', async () => {
|
||||||
|
const { checkSandboxCompatibility } = await import('@/lib/sdk-options.js');
|
||||||
|
const result = checkSandboxCompatibility(
|
||||||
|
'/Users/test/Library/CloudStorage/Dropbox-Personal/project',
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
expect(result.enabled).toBe(false);
|
||||||
|
expect(result.disabledReason).toBe('cloud_storage');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('TOOL_PRESETS', () => {
|
describe('TOOL_PRESETS', () => {
|
||||||
@@ -224,13 +370,27 @@ describe('sdk-options.ts', () => {
|
|||||||
expect(options.sandbox).toBeUndefined();
|
expect(options.sandbox).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not set sandbox when enableSandboxMode is not provided', async () => {
|
it('should enable sandbox by default when enableSandboxMode is not provided', async () => {
|
||||||
const { createChatOptions } = await import('@/lib/sdk-options.js');
|
const { createChatOptions } = await import('@/lib/sdk-options.js');
|
||||||
|
|
||||||
const options = createChatOptions({
|
const options = createChatOptions({
|
||||||
cwd: '/test/path',
|
cwd: '/test/path',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
expect(options.sandbox).toEqual({
|
||||||
|
enabled: true,
|
||||||
|
autoAllowBashIfSandboxed: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should auto-disable sandbox for cloud storage paths', async () => {
|
||||||
|
const { createChatOptions } = await import('@/lib/sdk-options.js');
|
||||||
|
|
||||||
|
const options = createChatOptions({
|
||||||
|
cwd: '/Users/test/Library/CloudStorage/Dropbox-Personal/project',
|
||||||
|
enableSandboxMode: true,
|
||||||
|
});
|
||||||
|
|
||||||
expect(options.sandbox).toBeUndefined();
|
expect(options.sandbox).toBeUndefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -285,13 +445,48 @@ describe('sdk-options.ts', () => {
|
|||||||
expect(options.sandbox).toBeUndefined();
|
expect(options.sandbox).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not set sandbox when enableSandboxMode is not provided', async () => {
|
it('should enable sandbox by default when enableSandboxMode is not provided', async () => {
|
||||||
const { createAutoModeOptions } = await import('@/lib/sdk-options.js');
|
const { createAutoModeOptions } = await import('@/lib/sdk-options.js');
|
||||||
|
|
||||||
const options = createAutoModeOptions({
|
const options = createAutoModeOptions({
|
||||||
cwd: '/test/path',
|
cwd: '/test/path',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
expect(options.sandbox).toEqual({
|
||||||
|
enabled: true,
|
||||||
|
autoAllowBashIfSandboxed: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should auto-disable sandbox for cloud storage paths', async () => {
|
||||||
|
const { createAutoModeOptions } = await import('@/lib/sdk-options.js');
|
||||||
|
|
||||||
|
const options = createAutoModeOptions({
|
||||||
|
cwd: '/Users/test/Library/CloudStorage/Dropbox-Personal/project',
|
||||||
|
enableSandboxMode: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(options.sandbox).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should auto-disable sandbox for cloud storage paths even when enableSandboxMode is not provided', async () => {
|
||||||
|
const { createAutoModeOptions } = await import('@/lib/sdk-options.js');
|
||||||
|
|
||||||
|
const options = createAutoModeOptions({
|
||||||
|
cwd: '/Users/test/Library/CloudStorage/Dropbox-Personal/project',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(options.sandbox).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should auto-disable sandbox for iCloud paths', async () => {
|
||||||
|
const { createAutoModeOptions } = await import('@/lib/sdk-options.js');
|
||||||
|
|
||||||
|
const options = createAutoModeOptions({
|
||||||
|
cwd: '/Users/test/Library/Mobile Documents/com~apple~CloudDocs/project',
|
||||||
|
enableSandboxMode: true,
|
||||||
|
});
|
||||||
|
|
||||||
expect(options.sandbox).toBeUndefined();
|
expect(options.sandbox).toBeUndefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user