src — workspace

Module: src-workspace Cohesion: 0.80 Members: 0

src — workspace

The src/workspace module is a critical component for managing project contexts and ensuring secure file system interactions within the application. It comprises two distinct but complementary sub-modules: WorkspaceIsolation for enforcing file access security, and WorkspaceManager for detecting, tracking, and providing isolated data storage for different projects (workspaces).

1. Workspace Isolation (workspace-isolation.ts)

The WorkspaceIsolation module is designed to enhance the security of file operations by strictly controlling access to the file system. Its primary goal is to prevent unauthorized access to files outside the current project's workspace, mitigate path traversal vulnerabilities, and block access to sensitive system files.

1.1. Purpose

In an environment where code might execute user-provided scripts or interact with the file system based on user input, robust security is paramount. WorkspaceIsolation acts as a gatekeeper, ensuring that all file system operations initiated by the application adhere to a predefined security policy.

Key security features include:

1.2. WorkspaceIsolation Class

The WorkspaceIsolation class is the core of this module, responsible for enforcing the security policy. It extends EventEmitter to signal important state changes or blocked access attempts.

1.2.1. Configuration (WorkspaceIsolationConfig)

The behavior of WorkspaceIsolation is controlled by the WorkspaceIsolationConfig interface:

export interface WorkspaceIsolationConfig {
  enabled: boolean; class="hl-cmt">// Global toggle for isolation (default: true)
  workspaceRoot: string; class="hl-cmt">// The absolute path to the current workspace
  additionalAllowedPaths: string[]; class="hl-cmt">// Custom paths to add to the whitelist
  logBlockedAccess: boolean; class="hl-cmt">// Whether to log blocked attempts (default: true)
  strictMode: boolean; class="hl-cmt">// If true, even whitelisted paths are blocked (default: false)
}

The constructor normalizes the workspaceRoot and initializes internal Sets for systemWhitelist and blockedPaths for efficient lookups.

1.2.2. Key Methods

This is the primary method for checking any file path before interaction. It performs a series of checks:

  1. If isolation is disabled, it immediately returns valid: true.
  2. Resolves the filePath to an absolute path.
  3. Checks if the resolved path is in the BLOCKED_PATHS list (or a sub-path of one).
  4. Checks if the resolved path is isWithinWorkspace or isWhitelisted.
  5. If the file exists, it resolves its fs.realpathSync to detect symlink escapes and re-validates the real path.
  6. If any check fails, it logs the attempt via logBlockedAccess and returns valid: false with a reason and error message.
    graph TD
        A[validatePath(filePath)] --> B{Isolation Enabled?}
        B -- No --> C[Return valid: true]
        B -- Yes --> D[Resolve filePath]
        D --> E{isBlockedPath(resolved)?}
        E -- Yes --> F[Log & Return valid: false, reason: 'blocked_path']
        E -- No --> G{isWithinWorkspace(resolved) OR isWhitelisted(resolved)?}
        G -- No --> H[Log & Return valid: false, reason: 'outside_workspace']
        G -- Yes --> I{File Exists?}
        I -- Yes --> J[Resolve realPath (fs.realpathSync)]
        J --> K{isBlockedPath(realPath)?}
        K -- Yes --> L[Log & Return valid: false, reason: 'blocked_path_via_symlink']
        K -- No --> M{isWithinWorkspace(realPath) OR isWhitelisted(realPath)?}
        M -- No --> N[Log & Return valid: false, reason: 'symlink_escape']
        M -- Yes --> O[Return valid: true]
        I -- No --> O

A convenience method that calls validatePath and, if the path is invalid, throws an Error. This is useful for operations where an invalid path should immediately halt execution.

Toggles the isolation feature on or off. Disabling isolation logs a warning. Emits isolation:toggled.

Dynamically adds a path to the systemWhitelist.

Updates the workspaceRoot and emits workspace:root-changed.

1.2.3. Security Policies

1.2.4. Events

The WorkspaceIsolation class emits the following events:

1.3. Usage

WorkspaceIsolation is typically accessed via a singleton instance to ensure a consistent security policy across the application.

This function provides the singleton instance. The first call initializes it, and subsequent calls can optionally update its configuration (e.g., workspaceRoot, enabled).

A convenience function to initialize the singleton based on CLI options, often used at application startup.

A global convenience function that calls getWorkspaceIsolation().validatePath().

A global convenience function that calls getWorkspaceIsolation().isSafe().

1.4. Integration Points

The WorkspaceIsolation singleton is primarily used by file system interaction layers. For example, the UnifiedVfsRouter (if present) would use getWorkspaceIsolation().validatePath() before performing any read/write operations to ensure they are secure.

class="hl-cmt">// Example usage in another module (e.g., a VFS layer)
import { validateWorkspacePath, resolveOrThrow } from '../workspace/workspace-isolation.js';

async function readFileSecurely(filePath: string): Promise<string> {
  try {
    const resolvedPath = resolveOrThrow(filePath, &#39;read file&#39;);
    class="hl-cmt">// Proceed with fs.readFile(resolvedPath, ...)
    return "file content"; class="hl-cmt">// Placeholder
  } catch (error) {
    console.error(`Security error: ${error.message}`);
    throw error;
  }
}

function checkPathSafety(filePath: string): boolean {
  const result = validateWorkspacePath(filePath, &#39;safety check&#39;);
  if (!result.valid) {
    console.warn(`Path "${filePath}" is not safe: ${result.error}`);
  }
  return result.valid;
}

2. Workspace Management (workspace-manager.ts)

The WorkspaceManager module is responsible for identifying the current project context (the "workspace"), providing information about it, and managing isolated data directories for sessions, checkpoints, and memory specific to that workspace.

2.1. Purpose

Modern development often involves working on multiple projects. WorkspaceManager provides the infrastructure to:

2.2. WorkspaceManager Class

The WorkspaceManager class is the central component for handling workspace-related operations. It also extends EventEmitter to signal workspace changes.

2.2.1. Configuration (WorkspaceConfig)

The WorkspaceConfig interface allows customization of workspace detection and data isolation:

export interface WorkspaceConfig {
  useLocalStorage: boolean; class="hl-cmt">// Prefer .grok/ in workspace root (default: true)
  autoCreateLocal: boolean; class="hl-cmt">// Create .grok/ if not found (default: false)
  rootMarkers: WorkspaceRootMarker[]; class="hl-cmt">// List of files/dirs to detect root
  maxSearchDepth: number; class="hl-cmt">// Max directories to search upwards (default: 10)
  isolateSessions: boolean; class="hl-cmt">// Isolate sessions per workspace (default: true)
  isolateCheckpoints: boolean; class="hl-cmt">// Isolate checkpoints per workspace (default: true)
  isolateMemory: boolean; class="hl-cmt">// Isolate AI memory per workspace (default: true)
}

The constructor initializes the configuration and ensures the global data directory (~/.codebuddy/workspaces/) exists.

2.2.2. Core Concepts

2.2.3. Key Methods

Identifies the project root by searching for rootMarkers with a defined ROOT_MARKER_PRIORITY.

Detects the workspace root, creates a WorkspaceInfo object (including a unique ID, name, detected marker, and projectType), caches it, sets it as the currentWorkspace, and saves it to the recent workspaces list. Emits workspace:initialized.

Returns the absolute path to the primary data directory for a given workspace, respecting useLocalStorage and the presence of a local .grok/ directory.

These methods return the appropriate directory for storing sessions, checkpoints, or AI memory, respectively. They respect the isolate* configuration flags and ensure the directories exist.

Creates the .grok/ directory and its standard subdirectories (sessions, checkpoints, memory) within the workspace root. It also attempts to add .grok/ to the project's .gitignore file.

Manage the state.json file within a workspace's data directory, which can store arbitrary workspace-specific settings or metadata (e.g., currentSessionId, lastModel).

Retrieves a list of recently accessed workspaces, filtering out any that no longer exist on the file system.

Periodically cleans up global workspace data directories that haven't been accessed in over 90 days, preventing accumulation of stale data.

2.2.4. Workspace Info and State

2.2.5. Events

The WorkspaceManager class emits the following events:

2.3. Usage

Like WorkspaceIsolation, WorkspaceManager is typically accessed via a singleton instance.

Provides the singleton instance of WorkspaceManager.

A global convenience function to initialize the workspace based on the current working directory. This is often called at application startup.

Returns the WorkspaceInfo object for the currently active workspace.

Global convenience functions to retrieve the appropriate isolated data directories for the current workspace.

2.4. Integration Points

WorkspaceManager is a foundational service. Other parts of the application would use it to:

class="hl-cmt">// Example usage in a session management module
import { getWorkspaceSessionsDir, initializeCurrentWorkspace } from &#39;../workspace/workspace-manager.js&#39;;

async function setupApplication() {
  const workspace = await initializeCurrentWorkspace();
  console.log(`Initialized workspace: ${workspace.name} at ${workspace.rootPath}`);

  const sessionsDir = getWorkspaceSessionsDir();
  console.log(`Sessions will be stored in: ${sessionsDir}`);
  class="hl-cmt">// Now, the session manager can use sessionsDir for its operations.
}

3. Module Architecture Overview

The src/workspace module provides a robust foundation for managing project contexts and securing file system access.

graph TD
    subgraph Application
        AppCode[Application Logic]
    end

    subgraph Workspace Module
        direction LR
        WM[WorkspaceManager]
        WI[WorkspaceIsolation]
        getWM(getWorkspaceManager)
        getWI(getWorkspaceIsolation)
    end

    AppCode -- 1. Initializes --> getWM
    AppCode -- 2. Initializes --> getWI
    AppCode -- 3. Gets Workspace Info --> getWM
    AppCode -- 4. Gets Data Paths --> getWM
    AppCode -- 5. Validates File Paths --> getWI

    getWM --> WM
    getWI --> WI

    WM -- Provides workspaceRoot --> WI
    WM -- Manages:
        - Project Detection
        - Workspace Context
        - Isolated Data Paths
        - Recent Workspaces
    --> AppCode

    WI -- Enforces:
        - File Access Security
        - Path Traversal Prevention
        - Symlink Protection
        - Whitelist/Blacklist
    --> AppCode

    style WM fill:#f9f,stroke:#333,stroke-width:2px
    style WI fill:#ccf,stroke:#333,stroke-width:2px

This diagram illustrates how Application Code interacts with the singleton accessors (getWorkspaceManager, getWorkspaceIsolation) to obtain instances of WorkspaceManager and WorkspaceIsolation. The WorkspaceManager is responsible for identifying the project root, which is then used by WorkspaceIsolation to define its security boundary. Both modules provide essential services to the rest of the application: WorkspaceManager for context and data organization, and WorkspaceIsolation for security enforcement.