diff --git a/.gitignore b/.gitignore index d10ca6a..1be2f5c 100644 --- a/.gitignore +++ b/.gitignore @@ -48,9 +48,8 @@ apps/web/dist/ # OS .DS_Store Thumbs.db -/.stoneforge/.worktrees/ -# Pi / agent cache -.pi/ + +# Local runtime state .atl/ -.sisyphus/ -.pi-lens/ +.pi/ +swap-pane diff --git a/AGENTS.md b/AGENTS.md index 5047fef..54befb4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -90,6 +90,13 @@ Before completion, report: Do not claim completion without verification evidence. +## Git branch policy + +- **Default working branch:** `dev` — all commits and pushes target `dev` unless the user explicitly requests otherwise. +- `main` is the stable/production branch; merge to `main` only when explicitly instructed. +- After committing, push to `origin/dev`. +- If `dev` does not exist locally, create it from `main` or fetch it from origin. + ## Git workflow ### Branching strategy diff --git a/CHANGELOG.md b/CHANGELOG.md index 32f2077..30d55a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **User Settings** - Theme selection, git identity, and preference management - **SSH Key Management** - Ed25519 key generation with secure storage - **Tool Types** - Built-in development tools (code-server, jupyter-notebook) with custom type support +- **Config Profiles** - User-owned profile CRUD with includes, mounts, path validation, cycle detection, and default profile selection - **Comprehensive Documentation** - Architecture, API, deployment, and development guides ### Changed diff --git a/apps/api/alembic/versions/0013_add_config_profiles.py b/apps/api/alembic/versions/0013_add_config_profiles.py new file mode 100644 index 0000000..d042419 --- /dev/null +++ b/apps/api/alembic/versions/0013_add_config_profiles.py @@ -0,0 +1,104 @@ +"""add config profiles, includes, mounts, and tool instance profile selection + +Revision ID: 0013_add_config_profiles +Revises: 0012_default_port_req +Create Date: 2026-05-24 12:00:00.000000 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision: str = "0013_add_config_profiles" +down_revision: Union[str, None] = "0012_default_port_req" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # Create config_profiles table + op.create_table( + "config_profiles", + sa.Column("id", postgresql.UUID(as_uuid=True), nullable=False), + sa.Column("user_id", postgresql.UUID(as_uuid=True), nullable=False), + sa.Column("name", sa.String(length=255), nullable=False), + sa.Column("description", sa.Text(), nullable=True), + sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("NOW()"), nullable=False), + sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.text("NOW()"), nullable=False), + sa.ForeignKeyConstraint(["user_id"], ["users.id"], ondelete="CASCADE"), + sa.PrimaryKeyConstraint("id"), + sa.UniqueConstraint("user_id", "name", name="uq_config_profiles_user_name"), + ) + op.create_index("idx_config_profiles_user", "config_profiles", ["user_id"]) + + # Create config_includes table + op.create_table( + "config_includes", + sa.Column("id", postgresql.UUID(as_uuid=True), nullable=False), + sa.Column("profile_id", postgresql.UUID(as_uuid=True), nullable=False), + sa.Column("included_profile_id", postgresql.UUID(as_uuid=True), nullable=False), + sa.Column("order_index", sa.Integer(), nullable=False, server_default="0"), + sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("NOW()"), nullable=False), + sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.text("NOW()"), nullable=False), + sa.ForeignKeyConstraint(["profile_id"], ["config_profiles.id"], ondelete="CASCADE"), + sa.ForeignKeyConstraint(["included_profile_id"], ["config_profiles.id"], ondelete="CASCADE"), + sa.PrimaryKeyConstraint("id"), + sa.UniqueConstraint("profile_id", "included_profile_id", name="uq_config_includes_pair"), + ) + op.create_index("idx_config_includes_profile", "config_includes", ["profile_id"]) + op.create_index("idx_config_includes_included", "config_includes", ["included_profile_id"]) + + # Create config_mounts table + op.create_table( + "config_mounts", + sa.Column("id", postgresql.UUID(as_uuid=True), nullable=False), + sa.Column("profile_id", postgresql.UUID(as_uuid=True), nullable=False), + sa.Column("mount_path", sa.String(length=1024), nullable=False), + sa.Column("content", sa.Text(), nullable=True), + sa.Column("source_profile_id", postgresql.UUID(as_uuid=True), nullable=True), + sa.Column("order_index", sa.Integer(), nullable=False, server_default="0"), + sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("NOW()"), nullable=False), + sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.text("NOW()"), nullable=False), + sa.ForeignKeyConstraint(["profile_id"], ["config_profiles.id"], ondelete="CASCADE"), + sa.ForeignKeyConstraint(["source_profile_id"], ["config_profiles.id"], ondelete="SET NULL"), + sa.PrimaryKeyConstraint("id"), + ) + op.create_index("idx_config_mounts_profile", "config_mounts", ["profile_id"]) + + # Add selected_profile_id to tool_instances + op.add_column( + "tool_instances", + sa.Column("selected_profile_id", postgresql.UUID(as_uuid=True), nullable=True), + ) + op.create_foreign_key( + "fk_tool_instances_selected_profile", + "tool_instances", + "config_profiles", + ["selected_profile_id"], + ["id"], + ondelete="SET NULL", + ) + op.create_index("idx_tool_instances_selected_profile", "tool_instances", ["selected_profile_id"]) + + +def downgrade() -> None: + # Remove selected_profile_id from tool_instances + op.drop_index("idx_tool_instances_selected_profile", table_name="tool_instances") + op.drop_constraint("fk_tool_instances_selected_profile", "tool_instances", type_="foreignkey") + op.drop_column("tool_instances", "selected_profile_id") + + # Drop config_mounts + op.drop_index("idx_config_mounts_profile", table_name="config_mounts") + op.drop_table("config_mounts") + + # Drop config_includes + op.drop_index("idx_config_includes_included", table_name="config_includes") + op.drop_index("idx_config_includes_profile", table_name="config_includes") + op.drop_table("config_includes") + + # Drop config_profiles + op.drop_index("idx_config_profiles_user", table_name="config_profiles") + op.drop_table("config_profiles") diff --git a/apps/api/alembic/versions/0014_add_profile_resolver_fields.py b/apps/api/alembic/versions/0014_add_profile_resolver_fields.py new file mode 100644 index 0000000..8d923fb --- /dev/null +++ b/apps/api/alembic/versions/0014_add_profile_resolver_fields.py @@ -0,0 +1,119 @@ +"""add profile resolver fields to config profiles and mounts + +Revision ID: 0014_add_profile_resolver_fields +Revises: 0013_add_config_profiles +Create Date: 2026-05-24 14:00:00.000000 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision: str = "0014_add_profile_resolver_fields" +down_revision: Union[str, None] = "0013_add_config_profiles" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # Add fields to config_profiles + op.add_column( + "config_profiles", + sa.Column("project_id", postgresql.UUID(as_uuid=True), nullable=True), + ) + op.add_column( + "config_profiles", + sa.Column("tool_type_id", postgresql.UUID(as_uuid=True), nullable=True), + ) + op.add_column( + "config_profiles", + sa.Column("environment_variables", sa.JSON(), nullable=True), + ) + op.add_column( + "config_profiles", + sa.Column("start_command", sa.Text(), nullable=True), + ) + op.add_column( + "config_profiles", + sa.Column("working_directory", sa.Text(), nullable=True), + ) + op.add_column( + "config_profiles", + sa.Column("port", sa.Integer(), nullable=True), + ) + op.add_column( + "config_profiles", + sa.Column("is_default", sa.Boolean(), nullable=False, server_default="false"), + ) + + # Add foreign keys for project and tool_type + op.create_foreign_key( + "fk_config_profiles_project", + "config_profiles", + "projects", + ["project_id"], + ["id"], + ondelete="CASCADE", + ) + op.create_foreign_key( + "fk_config_profiles_tool_type", + "config_profiles", + "tool_types", + ["tool_type_id"], + ["id"], + ondelete="CASCADE", + ) + + # Create indices + op.create_index("idx_config_profiles_project", "config_profiles", ["project_id"]) + op.create_index("idx_config_profiles_tool_type", "config_profiles", ["tool_type_id"]) + + # Alter config_mounts: rename mount_path to target_path, add mode, change content to files JSON + op.alter_column("config_mounts", "mount_path", new_column_name="target_path") + op.add_column( + "config_mounts", + sa.Column("mode", sa.String(length=10), nullable=False, server_default="rw"), + ) + op.add_column( + "config_mounts", + sa.Column("files", sa.JSON(), nullable=True), + ) + # Drop the source_profile foreign key if it exists + op.drop_constraint( + "config_mounts_source_profile_id_fkey", + "config_mounts", + type_="foreignkey", + ) + op.drop_column("config_mounts", "content") + op.drop_column("config_mounts", "source_profile_id") + + +def downgrade() -> None: + # Restore config_mounts + op.add_column( + "config_mounts", + sa.Column("source_profile_id", postgresql.UUID(as_uuid=True), nullable=True), + ) + op.add_column( + "config_mounts", + sa.Column("content", sa.Text(), nullable=True), + ) + op.drop_column("config_mounts", "files") + op.drop_column("config_mounts", "mode") + op.alter_column("config_mounts", "target_path", new_column_name="mount_path") + + # Restore config_profiles + op.drop_index("idx_config_profiles_tool_type", table_name="config_profiles") + op.drop_index("idx_config_profiles_project", table_name="config_profiles") + op.drop_constraint("fk_config_profiles_tool_type", "config_profiles", type_="foreignkey") + op.drop_constraint("fk_config_profiles_project", "config_profiles", type_="foreignkey") + op.drop_column("config_profiles", "is_default") + op.drop_column("config_profiles", "port") + op.drop_column("config_profiles", "working_directory") + op.drop_column("config_profiles", "start_command") + op.drop_column("config_profiles", "environment_variables") + op.drop_column("config_profiles", "tool_type_id") + op.drop_column("config_profiles", "project_id") diff --git a/apps/api/src/api/config_folders.py b/apps/api/src/api/config_folders.py new file mode 100644 index 0000000..ebdf7c8 --- /dev/null +++ b/apps/api/src/api/config_folders.py @@ -0,0 +1,284 @@ +"""Config folder API endpoints.""" + +import logging +import uuid + +from fastapi import APIRouter, Depends, HTTPException, status +from pydantic import Field +from sqlalchemy import select +from sqlalchemy.ext.asyncio import AsyncSession + +from src.auth.dependencies import get_current_user_id, get_db_session +from src.models.config_folder import ConfigFolder +from src.schemas.config_folder import ( + ConfigFolderCreate, + ConfigFolderUpdate, + ConfigFolderResponse, + ProjectOverrideCreate, +) + +logger = logging.getLogger(__name__) + +router = APIRouter(prefix="/config-folders", tags=["config-folders"]) + + +@router.get("", summary="List config folders", description="Get all config folders for the current user.") +async def list_config_folders( + user_id: uuid.UUID = Depends(get_current_user_id), + session: AsyncSession = Depends(get_db_session), +) -> dict: + """List config folders for the current user.""" + query = select(ConfigFolder).where(ConfigFolder.user_id == user_id) + result = await session.execute(query) + folders = result.scalars().all() + + return { + "folders": [ + { + "id": str(f.id), + "user_id": str(f.user_id), + "name": f.name, + "description": f.description, + "mount_path": f.mount_path, + "files": f.files, + "project_overrides": f.project_overrides, + "is_active": f.is_active, + "created_at": f.created_at.isoformat() if f.created_at else None, + "updated_at": f.updated_at.isoformat() if f.updated_at else None, + } + for f in folders + ] + } + + +@router.post("", summary="Create config folder", description="Create a new config folder.", status_code=status.HTTP_201_CREATED) +async def create_config_folder( + data: ConfigFolderCreate, + user_id: uuid.UUID = Depends(get_current_user_id), + session: AsyncSession = Depends(get_db_session), +) -> dict: + """Create a config folder.""" + # Check for duplicate name + existing = await session.scalar( + select(ConfigFolder).where( + ConfigFolder.user_id == user_id, + ConfigFolder.name == data.name, + ) + ) + if existing: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, + detail=f"config folder with name '{data.name}' already exists" + ) + + folder = ConfigFolder( + user_id=user_id, + name=data.name, + description=data.description, + mount_path=data.mount_path, + files=data.files, + ) + session.add(folder) + await session.commit() + await session.refresh(folder) + + return { + "id": str(folder.id), + "user_id": str(folder.user_id), + "name": folder.name, + "description": folder.description, + "mount_path": folder.mount_path, + "files": folder.files, + "project_overrides": folder.project_overrides, + "is_active": folder.is_active, + "created_at": folder.created_at.isoformat() if folder.created_at else None, + "updated_at": folder.updated_at.isoformat() if folder.updated_at else None, + } + + +@router.put("/{folder_id}", summary="Update config folder", description="Update an existing config folder.") +async def update_config_folder( + folder_id: uuid.UUID, + data: ConfigFolderUpdate, + user_id: uuid.UUID = Depends(get_current_user_id), + session: AsyncSession = Depends(get_db_session), +) -> dict: + """Update a config folder.""" + folder = await session.get(ConfigFolder, folder_id) + if folder is None or folder.user_id != user_id: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="config folder not found") + + if data.name is not None: + folder.name = data.name + if data.description is not None: + folder.description = data.description + if data.mount_path is not None: + folder.mount_path = data.mount_path + if data.files is not None: + folder.files = data.files + if data.is_active is not None: + folder.is_active = data.is_active + + await session.commit() + await session.refresh(folder) + + return { + "id": str(folder.id), + "user_id": str(folder.user_id), + "name": folder.name, + "description": folder.description, + "mount_path": folder.mount_path, + "files": folder.files, + "project_overrides": folder.project_overrides, + "is_active": folder.is_active, + "created_at": folder.created_at.isoformat() if folder.created_at else None, + "updated_at": folder.updated_at.isoformat() if folder.updated_at else None, + } + + +@router.delete("/{folder_id}", summary="Delete config folder", description="Delete a config folder.", status_code=status.HTTP_204_NO_CONTENT) +async def delete_config_folder( + folder_id: uuid.UUID, + user_id: uuid.UUID = Depends(get_current_user_id), + session: AsyncSession = Depends(get_db_session), +) -> None: + """Delete a config folder.""" + folder = await session.get(ConfigFolder, folder_id) + if folder is None or folder.user_id != user_id: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="config folder not found") + + await session.delete(folder) + await session.commit() + + +class ProjectOverrideWithId(ProjectOverrideCreate): + project_id: uuid.UUID = Field(description="Project ID for the override") + + +@router.get("/{folder_id}", summary="Get config folder by ID", description="Get a single config folder by its ID.") +async def get_config_folder( + folder_id: uuid.UUID, + user_id: uuid.UUID = Depends(get_current_user_id), + session: AsyncSession = Depends(get_db_session), +) -> dict: + """Get a config folder by ID.""" + folder = await session.get(ConfigFolder, folder_id) + if folder is None or folder.user_id != user_id: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="config folder not found") + + return { + "id": str(folder.id), + "user_id": str(folder.user_id), + "name": folder.name, + "description": folder.description, + "mount_path": folder.mount_path, + "files": folder.files, + "project_overrides": folder.project_overrides, + "is_active": folder.is_active, + "created_at": folder.created_at.isoformat() if folder.created_at else None, + "updated_at": folder.updated_at.isoformat() if folder.updated_at else None, + } + + +@router.post("/{folder_id}/overrides", summary="Add project override", description="Add a project override to a config folder.") +async def add_project_override( + folder_id: uuid.UUID, + data: ProjectOverrideWithId, + user_id: uuid.UUID = Depends(get_current_user_id), + session: AsyncSession = Depends(get_db_session), +) -> dict: + """Add a project override to a config folder.""" + folder = await session.get(ConfigFolder, folder_id) + if folder is None or folder.user_id != user_id: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="config folder not found") + + # Initialize project_overrides if None + if folder.project_overrides is None: + folder.project_overrides = {} + + # Add/update override + override_data = {} + if data.mount_path is not None: + override_data["mount_path"] = data.mount_path + if data.files is not None: + override_data["files"] = data.files + + # Use a copy to trigger SQLAlchemy change detection on JSONB + current_overrides = dict(folder.project_overrides or {}) + current_overrides[str(data.project_id)] = override_data + folder.project_overrides = current_overrides + + await session.commit() + await session.refresh(folder) + + return { + "id": str(folder.id), + "project_overrides": folder.project_overrides, + } + + +@router.put("/{folder_id}/overrides/{project_id}", summary="Update project override", description="Update a project override.") +async def update_project_override( + folder_id: uuid.UUID, + project_id: uuid.UUID, + data: ProjectOverrideCreate, + user_id: uuid.UUID = Depends(get_current_user_id), + session: AsyncSession = Depends(get_db_session), +) -> dict: + """Update a project override.""" + folder = await session.get(ConfigFolder, folder_id) + if folder is None or folder.user_id != user_id: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="config folder not found") + + # Initialize project_overrides if None + if folder.project_overrides is None: + folder.project_overrides = {} + + # Update override + current_overrides = dict(folder.project_overrides or {}) + override_data = current_overrides.get(str(project_id), {}) + if data.mount_path is not None: + override_data["mount_path"] = data.mount_path + if data.files is not None: + override_data["files"] = data.files + + current_overrides[str(project_id)] = override_data + folder.project_overrides = current_overrides + + # Mark the field as modified to ensure SQLAlchemy detects the change + from sqlalchemy.orm.attributes import flag_modified + flag_modified(folder, "project_overrides") + + await session.commit() + await session.refresh(folder) + + return { + "id": str(folder.id), + "project_overrides": folder.project_overrides, + } + + +@router.delete("/{folder_id}/overrides/{project_id}", summary="Remove project override", description="Remove a project override.") +async def remove_project_override( + folder_id: uuid.UUID, + project_id: uuid.UUID, + user_id: uuid.UUID = Depends(get_current_user_id), + session: AsyncSession = Depends(get_db_session), +) -> None: + """Remove a project override.""" + folder = await session.get(ConfigFolder, folder_id) + if folder is None or folder.user_id != user_id: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="config folder not found") + + # Remove override if exists + current_overrides = dict(folder.project_overrides or {}) + if str(project_id) in current_overrides: + del current_overrides[str(project_id)] + folder.project_overrides = current_overrides + await session.commit() + await session.refresh(folder) + + return { + "id": str(folder.id), + "project_overrides": folder.project_overrides or {}, + } diff --git a/apps/api/src/api/config_profiles.py b/apps/api/src/api/config_profiles.py index 36dbbda..593cef4 100644 --- a/apps/api/src/api/config_profiles.py +++ b/apps/api/src/api/config_profiles.py @@ -1,1008 +1,299 @@ """Config profile API endpoints.""" import logging -import os -import subprocess import uuid -from typing import Any -from fastapi import APIRouter, Depends, HTTPException, Query, status -from pydantic import BaseModel, Field, field_validator, model_validator +from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import selectinload -from src.api.shared_validators import validate_env_vars as _validate_env_vars from src.auth.dependencies import get_current_user_id, get_db_session -from src.models.config_profile import ConfigProfile, ConfigProfileInclude -from src.models.project import Project +from src.models.config_include import ConfigInclude +from src.models.config_mount import ConfigMount +from src.models.config_profile import ConfigProfile from src.models.tool_type import ToolType -from src.services.config_profile_resolver import ( - ConfigProfileCycleError, - check_include_cycle, - resolve_profile, - resolved_profile_to_dict, +from src.models.user_config import UserConfig +from src.schemas.config_profile import ( + ConfigIncludeCreate, + ConfigIncludeUpdate, + ConfigMountCreate, + ConfigMountUpdate, + ConfigProfileCreate, + ConfigProfileUpdate, + DefaultProfilesUpdate, +) +from src.services.config_profiles import ( + check_duplicate_include, + check_duplicate_mount_path, + check_duplicate_name, + get_default_profile_for_tool_type, + get_default_profiles, + get_owned_profile, + include_to_dict, + list_includes_for_profile, + list_mounts_for_profile, + mount_to_dict, + profile_to_dict, + set_default_profiles, + validate_includes_no_cycle, ) -from src.utils.git_url_parser import parse_git_url logger = logging.getLogger(__name__) router = APIRouter(prefix="/config-profiles", tags=["config-profiles"]) -MAX_PROFILE_SIZE_MB = 10 -MAX_PROFILE_SIZE_BYTES = MAX_PROFILE_SIZE_MB * 1024 * 1024 -def _validate_uuid(v: str | None) -> str | None: - if v is None: - return v - try: - uuid.UUID(v) - except ValueError: - raise ValueError(f"Invalid UUID: {v}") - return v - - -def _calculate_profile_size(data: dict) -> int: - """Calculate approximate serialized size of profile data.""" - total = 0 - for key, value in data.get("env_vars", {}).items(): - total += len(key.encode("utf-8")) + len(str(value).encode("utf-8")) - for key, value in data.get("runtime_hints", {}).items(): - total += len(key.encode("utf-8")) + len(str(value).encode("utf-8")) - for mount in data.get("mounts", []): - total += len(str(mount.get("target", "")).encode("utf-8")) - total += len(str(mount.get("mode", "")).encode("utf-8")) - for path, content in mount.get("files", {}).items(): - total += len(path.encode("utf-8")) + len(content.encode("utf-8")) - for path, content in data.get("files", {}).items(): - total += len(path.encode("utf-8")) + len(content.encode("utf-8")) - return total - - -class GitMountMapping(BaseModel): - source_path: str = Field( - description="Path within repository (supports glob patterns)" - ) - target_path: str = Field(description="Absolute path inside container") - - @field_validator("source_path") - @classmethod - def validate_source_path(cls, v: str) -> str: - if v.startswith("/"): - raise ValueError("source_path must be relative (no leading /)") - if ".." in v: - raise ValueError("source_path cannot contain path traversal (..)") - return v - - @field_validator("target_path") - @classmethod - def validate_target_path(cls, v: str) -> str: - if ".." in v: - raise ValueError("target_path cannot contain path traversal (..)") - return v - - -class GitMountItem(BaseModel): - remote_url: str = Field(description="Git remote URL (HTTPS or SSH)") - source_path: str | None = Field( - default=None, description="Path within repository (legacy single mapping)" - ) - target_path: str | None = Field( - default=None, - description="Absolute path inside container (legacy single mapping)", - ) - branch: str | None = Field(default=None, description="Optional branch or tag name") - mappings: list[GitMountMapping] | None = Field( - default=None, description="Multiple source/target mappings from the same repo" - ) - - @field_validator("remote_url") - @classmethod - def validate_remote_url(cls, v: str) -> str: - if not v.startswith(("http://", "https://", "git@", "ssh://")): - raise ValueError( - "remote_url must be a valid git URL (https://, git@, or ssh://)" - ) - return v - - @field_validator("source_path") - @classmethod - def validate_source_path(cls, v: str | None) -> str | None: - if v is None: - return v - if v.startswith("/"): - raise ValueError("source_path must be relative (no leading /)") - if ".." in v: - raise ValueError("source_path cannot contain path traversal (..)") - return v - - @field_validator("target_path") - @classmethod - def validate_target_path(cls, v: str | None) -> str | None: - if v is None: - return v - if ".." in v: - raise ValueError("target_path cannot contain path traversal (..)") - return v - - @model_validator(mode="after") - def check_mappings_or_legacy(self): - has_legacy = self.source_path is not None and self.target_path is not None - has_mappings = self.mappings is not None and len(self.mappings) > 0 - if not has_legacy and not has_mappings: - raise ValueError( - "Git mount must have either 'mappings' (non-empty array) or both 'source_path' and 'target_path'" - ) - return self - - -class MountItem(BaseModel): - target: str = Field(description="Absolute mount target path") - mode: str = Field(default="rw", description="Mount mode: ro or rw") - files: dict = Field( - default_factory=dict, description="Files as {relative_path: content}" - ) - - @field_validator("target") - @classmethod - def validate_target(cls, v: str) -> str: - if not v.startswith("/"): - raise ValueError("Mount target must be absolute (start with /)") - return v - - @field_validator("mode") - @classmethod - def validate_mode(cls, v: str) -> str: - if v not in ("ro", "rw"): - raise ValueError("Mount mode must be 'ro' or 'rw'") - return v - - @field_validator("files") - @classmethod - def validate_files(cls, v: dict) -> dict: - for path in v.keys(): - if ".." in path or not path: - raise ValueError(f"Invalid file path: {path}") - if path.startswith("/"): - raise ValueError( - f"Mount file paths must be relative (got: {path}). " - f"The mount target defines the absolute container path." - ) - return v - - -class ConfigProfileCreate(BaseModel): - name: str = Field(description="Profile name (unique per user)") - description: str | None = Field(default=None, description="Optional description") - project_id: str | None = Field(default=None, description="Optional project ID") - tool_type_id: str | None = Field(default=None, description="Optional tool type ID") - env_vars: dict = Field(default_factory=dict, description="Environment variables") - runtime_hints: dict = Field(default_factory=dict, description="Runtime hints") - mounts: list[MountItem] = Field( - default_factory=list, description="Mount definitions" - ) - files: dict = Field( - default_factory=dict, description="Files as {relative_path: content}" - ) - git_mounts: list[GitMountItem] = Field( - default_factory=list, description="Git repository mounts" - ) - is_default: bool = Field( - default=False, description="Whether this is the default profile for its scope" - ) - - @field_validator("project_id", "tool_type_id") - @classmethod - def validate_uuids(cls, v: str | None) -> str | None: - return _validate_uuid(v) - - @field_validator("files") - @classmethod - def validate_files(cls, v: dict) -> dict: - for path in v.keys(): - if ".." in path or not path: - raise ValueError(f"Invalid file path: {path}") - if path.startswith("/"): - raise ValueError( - f"File paths must be relative (got: {path}). " - f"Use Mounts for absolute container paths." - ) - return v - - @field_validator("env_vars") - @classmethod - def validate_env_vars(cls, v: dict) -> dict: - result = _validate_env_vars(v) - if result is None: - raise ValueError("env_vars must be a JSON object") - return result - - @field_validator("runtime_hints") - @classmethod - def validate_runtime_hints(cls, v: dict) -> dict: - if not isinstance(v, dict): - raise ValueError("runtime_hints must be a JSON object") - return v - - @field_validator("mounts") - @classmethod - def validate_mounts(cls, v: list) -> list: - if not isinstance(v, list): - raise ValueError("mounts must be a JSON array") - return v - - -class ConfigProfileUpdate(BaseModel): - name: str | None = Field(default=None, description="Profile name") - description: str | None = Field(default=None, description="Optional description") - project_id: str | None = Field(default=None, description="Optional project ID") - tool_type_id: str | None = Field(default=None, description="Optional tool type ID") - env_vars: dict | None = Field(default=None, description="Environment variables") - runtime_hints: dict | None = Field(default=None, description="Runtime hints") - mounts: list[MountItem] | None = Field( - default=None, description="Mount definitions" - ) - files: dict | None = Field( - default=None, description="Files as {relative_path: content}" - ) - git_mounts: list[GitMountItem] | None = Field( - default=None, description="Git repository mounts" - ) - is_default: bool | None = Field( - default=None, description="Whether this is the default profile" - ) - - @field_validator("project_id", "tool_type_id") - @classmethod - def validate_uuids(cls, v: str | None) -> str | None: - return _validate_uuid(v) - - @field_validator("files") - @classmethod - def validate_files(cls, v: dict | None) -> dict | None: - if v is None: - return v - for path in v.keys(): - if ".." in path or path.startswith("/") or not path: - raise ValueError(f"Invalid file path: {path}") - return v - - -class ConfigProfileIncludeUpdate(BaseModel): - includes: list[str] = Field(description="Ordered list of included profile IDs") - - @field_validator("includes") - @classmethod - def validate_includes(cls, v: list) -> list: - for item in v: - try: - uuid.UUID(item) - except ValueError: - raise ValueError(f"Invalid UUID in includes: {item}") - return v - - -class ConfigProfileResponse(BaseModel): - id: str - user_id: str - name: str - description: str | None - project_id: str | None - tool_type_id: str | None - env_vars: dict - runtime_hints: dict - mounts: list - files: dict - git_mounts: list - is_default: bool - includes: list[dict] - created_at: str - updated_at: str - - -async def _get_profile_with_includes( - session: AsyncSession, profile_id: uuid.UUID -) -> ConfigProfile | None: - """Fetch a profile with includes eagerly loaded.""" - result = await session.execute( - select(ConfigProfile) - .where(ConfigProfile.id == profile_id) - .options(selectinload(ConfigProfile.includes)) - ) - return result.scalar_one_or_none() - - -async def _check_access( - session: AsyncSession, - user_id: uuid.UUID, - project_id: uuid.UUID | None = None, - tool_type_id: uuid.UUID | None = None, -) -> None: - """Verify user has access to referenced project and tool type.""" - if project_id is not None: - project = await session.get(Project, project_id) - if project is None: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Project not found" - ) - # Add ownership check if needed; for now just verify existence - if tool_type_id is not None: - tool_type = await session.get(ToolType, tool_type_id) - if tool_type is None: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Tool type not found" - ) - - -async def _validate_git_mounts( - session: AsyncSession, - user_id: uuid.UUID, - git_mounts: list[Any], - project_id: uuid.UUID | None = None, -) -> None: - """Validate git mount URLs. - - Simply checks that remote_url looks like a valid git URL. - Actual clone validation happens at instance startup time. - """ - for mount in git_mounts: - remote_url = mount.get("remote_url") - if not remote_url: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="Git mount missing remote_url", - ) - - if not remote_url.startswith(("http://", "https://", "git@", "ssh://")): - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail=f"Invalid git URL: {remote_url}", - ) - - -def _profile_to_response( - profile: ConfigProfile, includes: list[ConfigProfileInclude] | None = None +@router.get("") +async def list_config_profiles( + tool_type_id: str | None = None, + user_id: uuid.UUID = Depends(get_current_user_id), + session: AsyncSession = Depends(get_db_session), ) -> dict: + query = select(ConfigProfile).where(ConfigProfile.user_id == user_id) + if tool_type_id: + tool_type = await session.get(ToolType, uuid.UUID(tool_type_id)) + if tool_type is None: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="tool type not found") + result = await session.execute(query.order_by(ConfigProfile.name)) + return {"profiles": [profile_to_dict(p) for p in result.scalars().all()]} + + +@router.post("", status_code=status.HTTP_201_CREATED) +async def create_config_profile( + data: ConfigProfileCreate, + user_id: uuid.UUID = Depends(get_current_user_id), + session: AsyncSession = Depends(get_db_session), +) -> dict: + await check_duplicate_name(session, user_id, data.name) + profile = ConfigProfile(user_id=user_id, name=data.name, description=data.description) + session.add(profile) + await session.commit() + await session.refresh(profile) + return profile_to_dict(profile) + + +@router.get("/defaults") +async def get_default_profiles_endpoint( + user_id: uuid.UUID = Depends(get_current_user_id), + session: AsyncSession = Depends(get_db_session), +) -> dict: + return await get_default_profiles(session, user_id) + + +@router.put("/defaults") +async def set_default_profiles_endpoint( + data: DefaultProfilesUpdate, + user_id: uuid.UUID = Depends(get_current_user_id), + session: AsyncSession = Depends(get_db_session), +) -> dict: + return await set_default_profiles(session, user_id, data.default_profiles) + + +@router.get("/defaults/{tool_type_id}") +async def get_default_profile_for_tool_type_endpoint( + tool_type_id: str, + user_id: uuid.UUID = Depends(get_current_user_id), + session: AsyncSession = Depends(get_db_session), +) -> dict: + return await get_default_profile_for_tool_type(session, user_id, tool_type_id) + + +@router.get("/{profile_id}") +async def get_config_profile( + profile_id: uuid.UUID, + user_id: uuid.UUID = Depends(get_current_user_id), + session: AsyncSession = Depends(get_db_session), +) -> dict: + profile = await session.get( + ConfigProfile, + profile_id, + options=[selectinload(ConfigProfile.includes), selectinload(ConfigProfile.mounts)], + ) + if profile is None or profile.user_id != user_id: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="config profile not found") + includes_data = [] + for inc in profile.includes: + included_profile = await session.get(ConfigProfile, inc.included_profile_id) + includes_data.append(include_to_dict(inc, included_profile.name if included_profile else None)) return { - "id": str(profile.id), - "user_id": str(profile.user_id), - "name": profile.name, - "description": profile.description, - "project_id": str(profile.project_id) if profile.project_id else None, - "tool_type_id": str(profile.tool_type_id) if profile.tool_type_id else None, - "env_vars": profile.env_vars or {}, - "runtime_hints": profile.runtime_hints or {}, - "mounts": profile.mounts or [], - "git_mounts": profile.git_mounts or [], - "files": profile.files or {}, - "is_default": profile.is_default, - "includes": [ - { - "id": str(inc.id), - "included_profile_id": str(inc.included_profile_id), - "order_index": inc.order_index, - } - for inc in (includes or profile.includes) - ], - "created_at": profile.created_at.isoformat() if profile.created_at else None, - "updated_at": profile.updated_at.isoformat() if profile.updated_at else None, + **profile_to_dict(profile), + "includes": includes_data, + "mounts": [mount_to_dict(m) for m in profile.mounts], } -@router.get("", response_model=list[ConfigProfileResponse]) -async def list_config_profiles( - project_id: str | None = Query(None, description="Filter by project compatibility"), - tool_type_id: str | None = Query( - None, description="Filter by tool type compatibility" - ), - current_user_id: uuid.UUID = Depends(get_current_user_id), - session: AsyncSession = Depends(get_db_session), -): - """List config profiles, optionally filtered by compatibility.""" - user_uuid = current_user_id - query = ( - select(ConfigProfile) - .where(ConfigProfile.user_id == user_uuid) - .options(selectinload(ConfigProfile.includes)) - ) - - if project_id or tool_type_id: - # Compatibility filter: include portable profiles and matching scoped profiles - project_uuid = uuid.UUID(project_id) if project_id else None - tool_uuid = uuid.UUID(tool_type_id) if tool_type_id else None - - from sqlalchemy import or_ - - conditions: list = [] - # Portable profiles (no project, no tool) - conditions.append( - (ConfigProfile.project_id.is_(None)) - & (ConfigProfile.tool_type_id.is_(None)) - ) - if project_uuid: - # Profiles matching this project (with or without tool) - conditions.append(ConfigProfile.project_id == project_uuid) - if tool_uuid: - # Profiles matching this tool (with or without project) - conditions.append(ConfigProfile.tool_type_id == tool_uuid) - if project_uuid and tool_uuid: - # Exact match - conditions.append( - (ConfigProfile.project_id == project_uuid) - & (ConfigProfile.tool_type_id == tool_uuid) - ) - - query = query.where(or_(*conditions)) - - result = await session.execute(query) - profiles = result.scalars().all() - return [_profile_to_response(p) for p in profiles] - - -@router.post( - "", response_model=ConfigProfileResponse, status_code=status.HTTP_201_CREATED -) -async def create_config_profile( - data: ConfigProfileCreate, - current_user_id: uuid.UUID = Depends(get_current_user_id), - session: AsyncSession = Depends(get_db_session), -): - """Create a new config profile.""" - user_uuid = current_user_id - - # Check for duplicate name - existing = await session.execute( - select(ConfigProfile) - .where( - ConfigProfile.user_id == user_uuid, - ConfigProfile.name == data.name, - ) - .options(selectinload(ConfigProfile.includes)) - ) - if existing.scalar_one_or_none() is not None: - raise HTTPException( - status_code=status.HTTP_409_CONFLICT, - detail=f"Profile with name '{data.name}' already exists", - ) - - # Validate references - project_uuid = uuid.UUID(data.project_id) if data.project_id else None - tool_uuid = uuid.UUID(data.tool_type_id) if data.tool_type_id else None - await _check_access(session, user_uuid, project_uuid, tool_uuid) - - # Validate git mounts reference existing repositories - if data.git_mounts: - git_mounts_data = [ - m.model_dump() if hasattr(m, "model_dump") else m for m in data.git_mounts - ] - await _validate_git_mounts(session, user_uuid, git_mounts_data, project_uuid) - - # Check size - size = _calculate_profile_size(data.model_dump()) - if size > MAX_PROFILE_SIZE_BYTES: - raise HTTPException( - status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE, - detail=f"Profile size exceeds {MAX_PROFILE_SIZE_MB}MB limit", - ) - - profile = ConfigProfile( - user_id=user_uuid, - name=data.name, - description=data.description, - project_id=project_uuid, - tool_type_id=tool_uuid, - env_vars=data.env_vars, - runtime_hints=data.runtime_hints, - mounts=[m.model_dump() for m in data.mounts], - git_mounts=[m.model_dump() for m in data.git_mounts], - files=data.files, - is_default=data.is_default, - ) - session.add(profile) - await session.commit() - - # Re-fetch with includes to avoid lazy loading issues - result = await session.execute( - select(ConfigProfile) - .where(ConfigProfile.id == profile.id) - .options(selectinload(ConfigProfile.includes)) - ) - profile = result.scalar_one() - - logger.debug("Created config profile %s for user %s", profile.id, user_uuid) - return _profile_to_response(profile) - - -@router.get("/{profile_id}", response_model=ConfigProfileResponse) -async def get_config_profile( - profile_id: str, - current_user_id: uuid.UUID = Depends(get_current_user_id), - session: AsyncSession = Depends(get_db_session), -): - """Get a config profile by ID.""" - profile = await _get_profile_with_includes(session, uuid.UUID(profile_id)) - if profile is None: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Profile not found" - ) - if profile.user_id != current_user_id: - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, detail="Not authorized" - ) - return _profile_to_response(profile) - - -@router.put("/{profile_id}", response_model=ConfigProfileResponse) +@router.put("/{profile_id}") async def update_config_profile( - profile_id: str, + profile_id: uuid.UUID, data: ConfigProfileUpdate, - current_user_id: uuid.UUID = Depends(get_current_user_id), + user_id: uuid.UUID = Depends(get_current_user_id), session: AsyncSession = Depends(get_db_session), -): - """Update a config profile.""" - profile = await _get_profile_with_includes(session, uuid.UUID(profile_id)) - if profile is None: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Profile not found" - ) - if profile.user_id != current_user_id: - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, detail="Not authorized" - ) - - update_data = data.model_dump(exclude_unset=True) - - # Handle name uniqueness - if "name" in update_data: - existing = await session.execute( - select(ConfigProfile).where( - ConfigProfile.user_id == profile.user_id, - ConfigProfile.name == update_data["name"], - ConfigProfile.id != profile.id, - ) - ) - if existing.scalar_one_or_none() is not None: - raise HTTPException( - status_code=status.HTTP_409_CONFLICT, - detail=f"Profile with name '{update_data['name']}' already exists", - ) - - # Validate references - project_uuid = ( - uuid.UUID(update_data["project_id"]) - if "project_id" in update_data and update_data["project_id"] - else (profile.project_id if "project_id" not in update_data else None) - ) - tool_uuid = ( - uuid.UUID(update_data["tool_type_id"]) - if "tool_type_id" in update_data and update_data["tool_type_id"] - else (profile.tool_type_id if "tool_type_id" not in update_data else None) - ) - await _check_access(session, profile.user_id, project_uuid, tool_uuid) - - # Validate git mounts reference existing repositories - if "git_mounts" in update_data and update_data["git_mounts"] is not None: - git_mounts_data = [ - m.model_dump() if hasattr(m, "model_dump") else m - for m in update_data["git_mounts"] - ] - await _validate_git_mounts( - session, profile.user_id, git_mounts_data, project_uuid - ) - - # Check size - current_data = _profile_to_response(profile) - merged = {**current_data, **update_data} - size = _calculate_profile_size(merged) - if size > MAX_PROFILE_SIZE_BYTES: - raise HTTPException( - status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE, - detail=f"Profile size exceeds {MAX_PROFILE_SIZE_MB}MB limit", - ) - - # Apply updates - for field_name, value in update_data.items(): - if field_name in ("project_id", "tool_type_id"): - value = uuid.UUID(value) if value else None - elif field_name == "mounts" and value is not None: - value = [m.model_dump() if not isinstance(m, dict) else m for m in value] - elif field_name == "git_mounts" and value is not None: - value = [m.model_dump() if not isinstance(m, dict) else m for m in value] - setattr(profile, field_name, value) - +) -> dict: + profile = await get_owned_profile(profile_id, user_id, session) + if data.name is not None: + await check_duplicate_name(session, user_id, data.name, exclude_id=profile_id) + profile.name = data.name + if data.description is not None: + profile.description = data.description await session.commit() - - # Re-fetch with includes to avoid lazy loading issues - result = await session.execute( - select(ConfigProfile) - .where(ConfigProfile.id == profile.id) - .options(selectinload(ConfigProfile.includes)) - ) - profile = result.scalar_one() - - logger.debug("Updated config profile %s", profile.id) - return _profile_to_response(profile) + await session.refresh(profile) + return profile_to_dict(profile) @router.delete("/{profile_id}", status_code=status.HTTP_204_NO_CONTENT) async def delete_config_profile( - profile_id: str, - current_user_id: uuid.UUID = Depends(get_current_user_id), + profile_id: uuid.UUID, + user_id: uuid.UUID = Depends(get_current_user_id), session: AsyncSession = Depends(get_db_session), -): - """Delete a config profile.""" - profile = await _get_profile_with_includes(session, uuid.UUID(profile_id)) - if profile is None: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Profile not found" - ) - if profile.user_id != current_user_id: - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, detail="Not authorized" - ) - +) -> None: + profile = await get_owned_profile(profile_id, user_id, session) await session.delete(profile) await session.commit() - logger.debug("Deleted config profile %s", profile_id) - return None -@router.put("/{profile_id}/includes", response_model=ConfigProfileResponse) -async def update_profile_includes( - profile_id: str, - data: ConfigProfileIncludeUpdate, - current_user_id: uuid.UUID = Depends(get_current_user_id), +@router.get("/{profile_id}/includes") +async def list_profile_includes( + profile_id: uuid.UUID, + user_id: uuid.UUID = Depends(get_current_user_id), session: AsyncSession = Depends(get_db_session), -): - """Update the ordered includes for a config profile.""" - profile = await _get_profile_with_includes(session, uuid.UUID(profile_id)) - if profile is None: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Profile not found" - ) - if profile.user_id != current_user_id: - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, detail="Not authorized" - ) +) -> dict: + await get_owned_profile(profile_id, user_id, session) + return await list_includes_for_profile(session, profile_id) - # Validate all included profiles exist and belong to the user - included_uuids = [uuid.UUID(inc_id) for inc_id in data.includes] - for inc_uuid in included_uuids: - inc_profile = await session.get(ConfigProfile, inc_uuid) - if inc_profile is None: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail=f"Included profile not found: {inc_uuid}", - ) - if inc_profile.user_id != current_user_id: - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, - detail=f"Not authorized to include profile: {inc_uuid}", - ) - if inc_uuid == profile.id: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="Profile cannot include itself", - ) - # Check for cycles - cycle = await check_include_cycle(session, profile.id, None) - if cycle is None and included_uuids: - # Check each new include would not create a cycle - for inc_uuid in included_uuids: - cycle = await check_include_cycle(session, profile.id, inc_uuid) - if cycle is not None: - break - - if cycle is not None: - cycle_str = " -> ".join(str(c) for c in cycle) - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail=f"Include cycle detected: {cycle_str}", - ) - - # Remove existing includes - result = await session.execute( - select(ConfigProfileInclude).where( - ConfigProfileInclude.profile_id == profile.id - ) +@router.post("/{profile_id}/includes", status_code=status.HTTP_201_CREATED) +async def add_profile_include( + profile_id: uuid.UUID, + data: ConfigIncludeCreate, + user_id: uuid.UUID = Depends(get_current_user_id), + session: AsyncSession = Depends(get_db_session), +) -> dict: + profile = await get_owned_profile(profile_id, user_id, session) + included_profile_id = uuid.UUID(data.included_profile_id) + if included_profile_id == profile_id: + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="a profile cannot include itself") + included_profile = await session.get(ConfigProfile, included_profile_id) + if included_profile is None: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="included profile not found") + if included_profile.user_id != user_id: + raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="included profile does not belong to user") + await check_duplicate_include(session, profile_id, included_profile_id) + await validate_includes_no_cycle(session, profile_id, included_profile_id) + include = ConfigInclude( + profile_id=profile_id, + included_profile_id=included_profile_id, + order_index=data.order_index, ) - for existing in result.scalars().all(): - await session.delete(existing) - await session.flush() + session.add(include) + await session.commit() + await session.refresh(include) + return include_to_dict(include, included_profile.name) - # Add new includes - for order_index, inc_uuid in enumerate(included_uuids): - include = ConfigProfileInclude( - profile_id=profile.id, - included_profile_id=inc_uuid, - order_index=order_index, - ) - session.add(include) - await session.flush() +@router.put("/{profile_id}/includes/{include_id}") +async def update_profile_include( + profile_id: uuid.UUID, + include_id: uuid.UUID, + data: ConfigIncludeUpdate, + user_id: uuid.UUID = Depends(get_current_user_id), + session: AsyncSession = Depends(get_db_session), +) -> dict: + await get_owned_profile(profile_id, user_id, session) + include = await session.get(ConfigInclude, include_id) + if include is None or include.profile_id != profile_id: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="include not found") + include.order_index = data.order_index + await session.commit() + await session.refresh(include) + included_profile = await session.get(ConfigProfile, include.included_profile_id) + return include_to_dict(include, included_profile.name if included_profile else None) + + +@router.delete("/{profile_id}/includes/{include_id}", status_code=status.HTTP_204_NO_CONTENT) +async def remove_profile_include( + profile_id: uuid.UUID, + include_id: uuid.UUID, + user_id: uuid.UUID = Depends(get_current_user_id), + session: AsyncSession = Depends(get_db_session), +) -> None: + await get_owned_profile(profile_id, user_id, session) + include = await session.get(ConfigInclude, include_id) + if include is None or include.profile_id != profile_id: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="include not found") + await session.delete(include) await session.commit() - # Re-fetch profile (includes loaded separately due to SQLite async issue) - result = await session.execute( - select(ConfigProfile).where(ConfigProfile.id == profile.id) - ) - profile = result.scalar_one() - - inc_result = await session.execute( - select(ConfigProfileInclude).where( - ConfigProfileInclude.profile_id == profile.id - ) - ) - direct_includes = inc_result.scalars().all() - - logger.debug("Updated includes for config profile %s", profile.id) - return _profile_to_response(profile, list(direct_includes)) -@router.get("/{profile_id}/preview") -async def preview_config_profile( - profile_id: str, - current_user_id: uuid.UUID = Depends(get_current_user_id), +@router.get("/{profile_id}/mounts") +async def list_profile_mounts( + profile_id: uuid.UUID, + user_id: uuid.UUID = Depends(get_current_user_id), session: AsyncSession = Depends(get_db_session), -): - """Preview the resolved output of a config profile.""" - profile = await _get_profile_with_includes(session, uuid.UUID(profile_id)) - if profile is None: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Profile not found" - ) - if profile.user_id != current_user_id: - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, detail="Not authorized" - ) - - try: - resolved = await resolve_profile(session, profile.id) - except ConfigProfileCycleError as exc: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail=str(exc), - ) - - return resolved_profile_to_dict(resolved) +) -> dict: + await get_owned_profile(profile_id, user_id, session) + return await list_mounts_for_profile(session, profile_id) -@router.get("/defaults/resolve") -async def resolve_default_profile( - project_id: str = Query(..., description="Project ID"), - tool_type_id: str = Query(..., description="Tool type ID"), - current_user_id: uuid.UUID = Depends(get_current_user_id), +@router.post("/{profile_id}/mounts", status_code=status.HTTP_201_CREATED) +async def add_profile_mount( + profile_id: uuid.UUID, + data: ConfigMountCreate, + user_id: uuid.UUID = Depends(get_current_user_id), session: AsyncSession = Depends(get_db_session), -): - """Resolve the default config profile for a project/tool combination. - - Selects by specificity: - 1. project+tool explicit default - 2. project explicit default - 3. tool explicit default - 4. global/user explicit default - 5. first created compatible profile - 6. none (returns null) - """ - user_uuid = current_user_id - project_uuid = uuid.UUID(project_id) - tool_uuid = uuid.UUID(tool_type_id) - - # Fetch all compatible profiles ordered by created_at - query = ( - select(ConfigProfile) - .where(ConfigProfile.user_id == user_uuid) - .where( - (ConfigProfile.project_id.is_(None) & ConfigProfile.tool_type_id.is_(None)) - | (ConfigProfile.project_id == project_uuid) - | (ConfigProfile.tool_type_id == tool_uuid) - | ( - (ConfigProfile.project_id == project_uuid) - & (ConfigProfile.tool_type_id == tool_uuid) - ) - ) - .order_by(ConfigProfile.created_at) +) -> dict: + profile = await get_owned_profile(profile_id, user_id, session) + await check_duplicate_mount_path(session, profile_id, data.target_path) + mount = ConfigMount( + profile_id=profile_id, + target_path=data.target_path, + mode=data.mode, + files=data.files, + order_index=data.order_index, ) - result = await session.execute(query) - profiles = result.scalars().all() - - if not profiles: - return {"profile_id": None, "profile_name": None} - - # Check explicit defaults by specificity - explicit_defaults = [p for p in profiles if p.is_default] - - # Most specific: project+tool - for p in explicit_defaults: - if p.project_id == project_uuid and p.tool_type_id == tool_uuid: - return {"profile_id": str(p.id), "profile_name": p.name} - - # Next: project only - for p in explicit_defaults: - if p.project_id == project_uuid and p.tool_type_id is None: - return {"profile_id": str(p.id), "profile_name": p.name} - - # Next: tool only - for p in explicit_defaults: - if p.project_id is None and p.tool_type_id == tool_uuid: - return {"profile_id": str(p.id), "profile_name": p.name} - - # Next: global/user (no project, no tool) - for p in explicit_defaults: - if p.project_id is None and p.tool_type_id is None: - return {"profile_id": str(p.id), "profile_name": p.name} - - # Fall back to first created compatible profile - first = profiles[0] - return {"profile_id": str(first.id), "profile_name": first.name} + session.add(mount) + await session.commit() + await session.refresh(mount) + return mount_to_dict(mount) -class ValidateGitUrlRequest(BaseModel): - url: str = Field(description="Git remote URL to validate") - ssh_key_id: str | None = Field( - default=None, description="Optional SSH key ID for private repos" - ) - - -class ValidateGitUrlResponse(BaseModel): - valid: bool - suggested_url: str | None = None - branches: list[str] | None = None - default_branch: str | None = None - error: str | None = None - error_code: str | None = None - - -@router.post("/validate-git-url", response_model=ValidateGitUrlResponse) -async def validate_git_url( - data: ValidateGitUrlRequest, - current_user_id: uuid.UUID = Depends(get_current_user_id), +@router.put("/{profile_id}/mounts/{mount_id}") +async def update_profile_mount( + profile_id: uuid.UUID, + mount_id: uuid.UUID, + data: ConfigMountUpdate, + user_id: uuid.UUID = Depends(get_current_user_id), session: AsyncSession = Depends(get_db_session), -) -> ValidateGitUrlResponse: - """Validate a git remote URL and list available branches. +) -> dict: + await get_owned_profile(profile_id, user_id, session) + mount = await session.get(ConfigMount, mount_id) + if mount is None or mount.profile_id != profile_id: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="mount not found") + if data.target_path is not None: + await check_duplicate_mount_path(session, profile_id, data.target_path, exclude_id=mount_id) + mount.target_path = data.target_path + if data.files is not None: + mount.files = data.files + if data.order_index is not None: + mount.order_index = data.order_index + await session.commit() + await session.refresh(mount) + return mount_to_dict(mount) - Parses the URL, suggests corrections for browser URLs, and runs - git ls-remote to verify reachability and enumerate branches. - """ - parse_result = parse_git_url(data.url) - original_url = data.url.strip() - url_to_check = parse_result.get("base_url") or original_url - if not url_to_check: - return ValidateGitUrlResponse( - valid=False, - error=parse_result.get("message", "Invalid URL"), - error_code=parse_result.get("error_code", "INVALID_URL"), - ) - - # If the URL needed parsing, return suggestion without checking remote - if parse_result.get("needs_parsing") and url_to_check != original_url: - return ValidateGitUrlResponse( - valid=False, - suggested_url=url_to_check, - error=parse_result.get("message"), - error_code=parse_result.get("error_code", "URL_NEEDS_PARSING"), - ) - - # Optional SSH key for private repos - env = None - key_path = None - if data.ssh_key_id: - from src.models.ssh_key import SSHKey - from src.services.ssh_keys import _get_fernet - - try: - ssh_key_uuid = uuid.UUID(data.ssh_key_id) - except ValueError: - return ValidateGitUrlResponse( - valid=False, - error="Invalid SSH key ID format", - error_code="INVALID_SSH_KEY", - ) - - ssh_key = await session.get(SSHKey, ssh_key_uuid) - if ssh_key is None or ssh_key.user_id != current_user_id: - return ValidateGitUrlResponse( - valid=False, - error="SSH key not found or not authorized", - error_code="SSH_KEY_NOT_FOUND", - ) - - import tempfile - - fernet = _get_fernet() - private_key = fernet.decrypt(ssh_key.private_key_encrypted.encode()).decode() - fd, key_path = tempfile.mkstemp(prefix="ssh_key_") - try: - os.write(fd, private_key.encode()) - finally: - os.close(fd) - os.chmod(key_path, 0o600) - env = { - "GIT_SSH_COMMAND": f"ssh -i {key_path} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" - } - - try: - result = subprocess.run( - ["git", "ls-remote", "--heads", url_to_check], - capture_output=True, - text=True, - timeout=30, - env={**os.environ, **env} if env else None, - ) - except subprocess.TimeoutExpired: - if key_path and os.path.exists(key_path): - os.unlink(key_path) - return ValidateGitUrlResponse( - valid=False, - error="Remote repository check timed out", - error_code="TIMEOUT", - ) - except FileNotFoundError: - if key_path and os.path.exists(key_path): - os.unlink(key_path) - return ValidateGitUrlResponse( - valid=False, - error="git command not found on server", - error_code="GIT_NOT_FOUND", - ) - finally: - if key_path and os.path.exists(key_path): - os.unlink(key_path) - - if result.returncode != 0: - stderr = result.stderr.strip() - if ( - "could not resolve" in stderr.lower() - or "unable to access" in stderr.lower() - ): - error_msg = "Could not reach repository. Check the URL and network access." - error_code = "UNREACHABLE" - elif ( - "authentication" in stderr.lower() or "permission denied" in stderr.lower() - ): - error_msg = ( - "Authentication failed. Provide an SSH key for private repositories." - ) - error_code = "AUTH_FAILED" - else: - error_msg = f"Repository not accessible: {stderr[:200]}" - error_code = "REMOTE_ERROR" - return ValidateGitUrlResponse( - valid=False, - error=error_msg, - error_code=error_code, - ) - - # Parse branches from ls-remote output - branches: list[str] = [] - default_branch = "main" - for line in result.stdout.strip().split("\n"): - if not line.strip(): - continue - parts = line.split() - if len(parts) == 2: - ref = parts[1] - # refs/heads/branch-name - if ref.startswith("refs/heads/"): - branch_name = ref[len("refs/heads/") :] - branches.append(branch_name) - if branch_name in ("main", "master"): - default_branch = branch_name - - if not branches: - return ValidateGitUrlResponse( - valid=False, - error="No branches found in remote repository", - error_code="NO_BRANCHES", - ) - - return ValidateGitUrlResponse( - valid=True, - suggested_url=url_to_check if url_to_check != original_url else None, - branches=branches, - default_branch=default_branch, - ) +@router.delete("/{profile_id}/mounts/{mount_id}", status_code=status.HTTP_204_NO_CONTENT) +async def remove_profile_mount( + profile_id: uuid.UUID, + mount_id: uuid.UUID, + user_id: uuid.UUID = Depends(get_current_user_id), + session: AsyncSession = Depends(get_db_session), +) -> None: + await get_owned_profile(profile_id, user_id, session) + mount = await session.get(ConfigMount, mount_id) + if mount is None or mount.profile_id != profile_id: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="mount not found") + await session.delete(mount) + await session.commit() diff --git a/apps/api/src/api/git_repositories.py b/apps/api/src/api/git_repositories.py index 0b3892d..9d71e73 100644 --- a/apps/api/src/api/git_repositories.py +++ b/apps/api/src/api/git_repositories.py @@ -1,677 +1,84 @@ +"""Git repository API endpoints.""" + import logging -import os -import shutil -import subprocess import uuid -from datetime import datetime from fastapi import APIRouter, Depends, HTTPException, Response, status -from pydantic import BaseModel, ConfigDict -from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession -from src.auth.dependencies import ( - _get_owned_project, - _get_user, - get_current_user_id, - get_db_session, +from src.auth.dependencies import get_current_user, get_db_session, get_owned_project +from src.models.project import Project +from src.models.user import User +from src.schemas.git_repository import ( + BranchCreateRequest, + CheckoutRequest, + CommitRequest, + CommitResponse, + FetchResponse, + FileContentResponse, + FileListResponse, + FileUpdateRequest, + FileUpdateResponse, + GitRepositoryCreate, + GitRepositoryResponse, + MergeRequest, + MergeResponse, + PullResponse, + PushResponse, + StatusResponse, + URLParseRequest, + URLParseResponse, ) -from src.config import Settings -from src.models.git_repository import GitRepository -from src.models.ssh_key import SSHKey -from src.utils.git_files import ( - commit_file, - get_file_content, - list_branches, - list_tree, -) -from src.utils.git_control import ( - checkout_branch, - commit_changes, - create_branch, - delete_branch, - fetch, - get_status, - merge, - pull, - push, -) -from src.utils.git_history import get_commit_detail, get_commit_history +from src.services.git import control as git_control +from src.services.git import files as git_files +from src.services.git.repository import create_repository, delete_repository, list_repositories from src.utils.git_url_parser import parse_git_url -from src.services.ssh_keys import _get_fernet router = APIRouter(prefix="/projects", tags=["git-repositories"]) - logger = logging.getLogger(__name__) -def _get_repo_path(user_id: uuid.UUID, project_id: uuid.UUID, name: str) -> str: - """Generate the filesystem path for a repository. - - Args: - user_id: UUID of the repository owner. - project_id: UUID of the project. - name: Repository name. - - Returns: - Absolute path to the repository directory. - """ - base = Settings().repo_base_path or "/data/repos" - return os.path.join(base, str(user_id), str(project_id), f"{name}.git") - - -def _build_provider_clone_url(owner: str, repo: str) -> str: - """Build the SSH clone URL for the fixed git provider.""" - return f"git@git.commumedia.org:{owner}/{repo}.git" - - -def _prepare_ssh_env(ssh_key: SSHKey | None) -> dict | None: - """Prepare environment variables for git commands with SSH authentication. - - Returns a dict of extra env vars, or None if no SSH key provided. - The caller is responsible for cleaning up the temporary key file. - """ - if ssh_key is None: - return None - - import tempfile - - # Decrypt private key - fernet = _get_fernet() - private_key = fernet.decrypt(ssh_key.private_key_encrypted.encode()).decode() - - # Write to temp file with restricted permissions - fd, key_path = tempfile.mkstemp(prefix="ssh_key_") - try: - os.write(fd, private_key.encode()) - finally: - os.close(fd) - os.chmod(key_path, 0o600) - - # Return env vars and the key path for cleanup - env = { - "GIT_SSH_COMMAND": f"ssh -i {key_path} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" - } - return env, key_path - - -def _preflight_remote_repository( - remote_url: str, ssh_key: SSHKey | None = None -) -> None: - """Verify a remote repository is reachable before cloning.""" - env = None - key_path = None - - if ssh_key is not None: - ssh_result = _prepare_ssh_env(ssh_key) - if ssh_result: - env, key_path = ssh_result - - try: - result = subprocess.run( - ["git", "ls-remote", remote_url], - capture_output=True, - text=True, - timeout=60, - env={**os.environ, **env} if env else None, - ) - except subprocess.TimeoutExpired: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="remote repository check timed out", - ) - except FileNotFoundError: - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail="git command not found", - ) - finally: - if key_path and os.path.exists(key_path): - os.unlink(key_path) - - if result.returncode != 0: - logger.error( - "Preflight check failed for %s: stderr=%s", remote_url, result.stderr - ) - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail=f"repository not found or inaccessible: {result.stderr}", - ) - - -def _clone_working_repository( - remote_url: str, repo_path: str, ssh_key: SSHKey | None = None -) -> None: - env = None - key_path = None - - if ssh_key is not None: - ssh_result = _prepare_ssh_env(ssh_key) - if ssh_result: - env, key_path = ssh_result - - try: - result = subprocess.run( - ["git", "clone", remote_url, repo_path], - capture_output=True, - text=True, - timeout=300, - env={**os.environ, **env} if env else None, - ) - except subprocess.TimeoutExpired: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, detail="clone operation timed out" - ) - except FileNotFoundError: - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail="git command not found", - ) - finally: - if key_path and os.path.exists(key_path): - os.unlink(key_path) - - if result.returncode != 0: - logger.error("Clone failed for %s: stderr=%s", remote_url, result.stderr) - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail=f"failed to clone repository: {result.stderr}", - ) - - -def _init_working_repository(repo_path: str) -> None: - try: - result = subprocess.run( - ["git", "init", "-b", "main", repo_path], - capture_output=True, - text=True, - ) - except FileNotFoundError: - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail="git command not found", - ) - - if result.returncode == 0: - return - - fallback = subprocess.run( - ["git", "init", repo_path], - capture_output=True, - text=True, - ) - if fallback.returncode != 0: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail=f"failed to initialize repository: {fallback.stderr}", - ) - - ref_result = subprocess.run( - ["git", "-C", repo_path, "symbolic-ref", "HEAD", "refs/heads/main"], - capture_output=True, - text=True, - ) - if ref_result.returncode != 0: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail=f"failed to set initial branch: {ref_result.stderr}", - ) - - -class GitRepositoryCreate(BaseModel): - name: str - remote_url: str | None = None - force_original_url: bool = False - ssh_key_id: str | None = None - - -class URLParseRequest(BaseModel): - url: str - - -class URLParseResponse(BaseModel): - original_url: str - base_url: str | None - is_valid_clone_url: bool - needs_parsing: bool - host: str | None - message: str - error_code: str | None - - -class GitRepositoryResponse(BaseModel): - model_config = ConfigDict(from_attributes=True) - - id: uuid.UUID - name: str - path: str - project_id: uuid.UUID | None - owner_id: uuid.UUID - is_mirror: bool - remote_url: str | None - last_push: datetime | None - ssh_key_id: uuid.UUID | None - created_at: datetime - updated_at: datetime - - -@router.get( - "/repositories", - response_model=list[GitRepositoryResponse], - summary="List all user repositories", - description="List all git repositories owned by the user, including external repositories not tied to any project.", -) -async def list_user_repositories( - user_id: uuid.UUID = Depends(get_current_user_id), - session: AsyncSession = Depends(get_db_session), -) -> list[GitRepository]: - """List all repositories owned by the user. - - Args: - user_id: ID of the authenticated user. - session: Database session. - - Returns: - List of all repositories owned by the user. - """ - result = await session.execute( - select(GitRepository).where(GitRepository.owner_id == user_id) - ) - return list(result.scalars().all()) - - -@router.post( - "/repositories/parse-url", - response_model=URLParseResponse, - summary="Parse a git URL", - description="Parse a git URL and detect if it's a browser URL that needs correction.", -) -async def parse_repository_url(data: URLParseRequest) -> URLParseResponse: - """Parse a git URL and detect if it's a browser URL that needs correction. - - Args: - data: Request containing the URL to parse. - - Returns: - Parsed URL information including whether it needs parsing and suggested corrections. - """ - result = parse_git_url(data.url) - return URLParseResponse(**result) - - -@router.post( - "/repositories", - response_model=GitRepositoryResponse, - status_code=status.HTTP_201_CREATED, - summary="Create an external repository", - description="Create a new external git repository (not tied to any project). Can clone from remote URL.", -) -async def create_external_repository( - data: GitRepositoryCreate, - user_id: uuid.UUID = Depends(get_current_user_id), - session: AsyncSession = Depends(get_db_session), -) -> GitRepository: - """Create a new external git repository. - - External repositories are not tied to any project and can be used - across all projects for config profile git mounts. - - Args: - data: Repository creation data including name and optional remote URL. - user_id: ID of the authenticated user. - session: Database session. - - Returns: - The newly created external repository. - """ - _user = await _get_user(session, user_id) - - # Check for duplicate name (external repos only) - existing = await session.execute( - select(GitRepository).where( - GitRepository.project_id.is_(None), - GitRepository.owner_id == user_id, - GitRepository.name == data.name, - ) - ) - if existing.scalar_one_or_none(): - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="repository name already exists", - ) - - # Validate and potentially correct the URL - remote_url = data.remote_url - if remote_url and not data.force_original_url: - parse_result = parse_git_url(remote_url) - if parse_result["needs_parsing"] and parse_result["base_url"]: - raise HTTPException( - status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, - detail={ - "message": "The provided URL appears to be a browser URL, not a git clone URL", - "suggested_url": parse_result["base_url"], - "original_url": remote_url, - "error_code": "URL_NEEDS_PARSING", - }, - ) - if parse_result["base_url"]: - remote_url = parse_result["base_url"] - - # Validate SSH key if provided - ssh_key_id = None - ssh_key = None - if data.ssh_key_id: - try: - ssh_key_id = uuid.UUID(data.ssh_key_id) - except ValueError: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="invalid ssh_key_id format", - ) - - ssh_key = await session.get(SSHKey, ssh_key_id) - if ssh_key is None: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="ssh key not found" - ) - if ssh_key.user_id != user_id: - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, - detail="ssh key does not belong to user", - ) - - if remote_url: - _preflight_remote_repository(remote_url, ssh_key) - - # Create external repo with no project - repo = GitRepository( - name=data.name, - path="", # Will be set after clone - project_id=None, - owner_id=user_id, - remote_url=remote_url, - ssh_key_id=ssh_key_id, - ) - session.add(repo) - await session.flush() - - # Set path and optionally clone - repo_path = f"/data/repos/external/{user_id}/{repo.id}" - repo.path = repo_path - - if remote_url: - try: - _clone_working_repository(remote_url, repo_path, ssh_key) - repo.is_mirror = False - except Exception as exc: - await session.rollback() - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Failed to clone repository: {exc}", - ) - else: - # Initialize empty repo - os.makedirs(repo_path, exist_ok=True) - subprocess.run(["git", "init", repo_path], check=True, capture_output=True) - repo.is_mirror = False - - await session.commit() - return repo - - -@router.get( - "/{project_id}/repositories", - response_model=list[GitRepositoryResponse], - summary="List repositories", - description="List all git repositories in a project.", -) -async def list_repositories( +@router.get("/{project_id}/repositories", response_model=list[GitRepositoryResponse]) +async def list_repositories_endpoint( project_id: uuid.UUID, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), session: AsyncSession = Depends(get_db_session), -) -> list[GitRepository]: - """List all repositories in a project. - - Args: - project_id: UUID of the project. - user_id: ID of the authenticated user. - session: Database session. - - Returns: - List of repositories in the project. - """ - _user = await _get_user(session, user_id) - _project = await _get_owned_project(project_id, user_id, session) - - result = await session.execute( - select(GitRepository).where(GitRepository.project_id == project_id) - ) - return list(result.scalars().all()) +): + return await list_repositories(session, project_id) -@router.delete( - "/{project_id}/repositories/{repo_id}", - status_code=status.HTTP_204_NO_CONTENT, - summary="Delete a repository", - description="Delete a git repository from the project and remove it from disk.", -) -async def delete_repository( +@router.post("/{project_id}/repositories", response_model=GitRepositoryResponse, status_code=status.HTTP_201_CREATED) +async def create_repository_endpoint( + project_id: uuid.UUID, + data: GitRepositoryCreate, + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), + session: AsyncSession = Depends(get_db_session), +): + return await create_repository(session, project_id, data, user) + + +@router.delete("/{project_id}/repositories/{repo_id}", status_code=status.HTTP_204_NO_CONTENT) +async def delete_repository_endpoint( project_id: uuid.UUID, repo_id: uuid.UUID, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), session: AsyncSession = Depends(get_db_session), -) -> Response: - """Delete a repository. - - Args: - project_id: UUID of the project. - repo_id: UUID of the repository to delete. - user_id: ID of the authenticated user. - session: Database session. - - Returns: - Empty response with 204 status code. - """ - _user = await _get_user(session, user_id) - _project = await _get_owned_project(project_id, user_id, session) - - repo = await session.get(GitRepository, repo_id) - if repo is None or repo.project_id != project_id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found" - ) - - # Remove from disk - if os.path.exists(repo.path): - shutil.rmtree(repo.path) - - await session.delete(repo) - await session.commit() +): + await delete_repository(session, repo_id, project_id) return Response(status_code=status.HTTP_204_NO_CONTENT) -@router.post( - "/{project_id}/repositories", - response_model=GitRepositoryResponse, - status_code=status.HTTP_201_CREATED, - summary="Create a repository", - description="Create a new git repository in a project. Can clone from remote or initialize a working repository.", -) -async def create_repository( - project_id: uuid.UUID, - data: GitRepositoryCreate, - user_id: uuid.UUID = Depends(get_current_user_id), - session: AsyncSession = Depends(get_db_session), -) -> GitRepository: - """Create a new git repository. - - Args: - project_id: UUID of the project. - data: Repository creation data including name and optional remote URL. - user_id: ID of the authenticated user. - session: Database session. - - Returns: - The newly created repository. - """ - _user = await _get_user(session, user_id) - _project = await _get_owned_project(project_id, user_id, session) - - # Check for duplicate name - existing = await session.execute( - select(GitRepository).where( - GitRepository.project_id == project_id, - GitRepository.name == data.name, - ) - ) - if existing.scalar_one_or_none(): - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="repository name already exists", - ) - - # Validate and potentially correct the URL - remote_url = data.remote_url - if remote_url and not data.force_original_url: - parse_result = parse_git_url(remote_url) - if parse_result["needs_parsing"] and parse_result["base_url"]: - raise HTTPException( - status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, - detail={ - "message": "The provided URL appears to be a browser URL, not a git clone URL", - "suggested_url": parse_result["base_url"], - "original_url": remote_url, - "error_code": "URL_NEEDS_PARSING", - }, - ) - # Use base_url if it was extracted (for URLs without .git suffix) - if parse_result["base_url"]: - remote_url = parse_result["base_url"] - - # Validate SSH key if provided - ssh_key_id = None - ssh_key = None - if data.ssh_key_id: - try: - ssh_key_id = uuid.UUID(data.ssh_key_id) - except ValueError: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="invalid ssh_key_id format", - ) - - ssh_key = await session.get(SSHKey, ssh_key_id) - if ssh_key is None: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="ssh key not found" - ) - if ssh_key.user_id != user_id and ssh_key.project_id != project_id: - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, - detail="ssh key does not belong to user or project", - ) - - if remote_url: - _preflight_remote_repository(remote_url, ssh_key) - - repo_path = _get_repo_path(user_id, project_id, data.name) - - # Ensure parent directory exists - os.makedirs(os.path.dirname(repo_path), exist_ok=True) - - if remote_url: - _clone_working_repository(remote_url, repo_path, ssh_key) - else: - _init_working_repository(repo_path) - - repo = GitRepository( - name=data.name, - path=repo_path, - project_id=project_id, - owner_id=user_id, - is_mirror=False, - remote_url=remote_url, - ssh_key_id=ssh_key_id, - ) - session.add(repo) - await session.commit() - await session.refresh(repo) - return repo +@router.post("/repositories/parse-url", response_model=URLParseResponse) +async def parse_repository_url(data: URLParseRequest) -> URLParseResponse: + return URLParseResponse(**parse_git_url(data.url)) -class UpdateSSHKeyRequest(BaseModel): - ssh_key_id: str | None = None +# History - -@router.patch( - "/{project_id}/repositories/{repo_id}/ssh-key", - response_model=GitRepositoryResponse, - summary="Update repository SSH key", - description="Update the SSH key associated with a repository.", -) -async def update_repository_ssh_key( - project_id: uuid.UUID, - repo_id: uuid.UUID, - data: UpdateSSHKeyRequest, - user_id: uuid.UUID = Depends(get_current_user_id), - session: AsyncSession = Depends(get_db_session), -) -> GitRepository: - """Update the SSH key for a repository. - - Args: - project_id: UUID of the project. - repo_id: UUID of the repository. - data: Update data containing the new SSH key ID. - user_id: ID of the authenticated user. - session: Database session. - - Returns: - The updated repository. - """ - _user = await _get_user(session, user_id) - _project = await _get_owned_project(project_id, user_id, session) - - repo = await session.get(GitRepository, repo_id) - if repo is None or repo.project_id != project_id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found" - ) - - # Validate SSH key if provided - if data.ssh_key_id: - try: - ssh_key_id = uuid.UUID(data.ssh_key_id) - except ValueError: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="invalid ssh_key_id format", - ) - - ssh_key = await session.get(SSHKey, ssh_key_id) - if ssh_key is None: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="ssh key not found" - ) - if ssh_key.user_id != user_id and ssh_key.project_id != project_id: - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, - detail="ssh key does not belong to user or project", - ) - - repo.ssh_key_id = ssh_key_id - else: - repo.ssh_key_id = None - - await session.commit() - await session.refresh(repo) - return repo - - -@router.get( - "/{project_id}/repositories/{repo_id}/history", - summary="Get repository history", - description="Get commit history for a repository with optional branch filtering.", -) +@router.get("/{project_id}/repositories/{repo_id}/history") async def get_repository_history( project_id: uuid.UUID, repo_id: uuid.UUID, @@ -679,945 +86,205 @@ async def get_repository_history( branch: str | None = None, limit: int = 100, offset: int = 0, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), session: AsyncSession = Depends(get_db_session), ) -> dict: - """Get commit history for a repository. - - Args: - project_id: UUID of the project. - repo_id: UUID of the repository. - view: View type for history display (default: graph). - branch: Optional branch name to filter commits. - limit: Maximum number of commits to return (default: 100). - offset: Number of commits to skip (default: 0). - user_id: ID of the authenticated user. - session: Database session. - - Returns: - Dictionary containing commit history data. - """ - _user = await _get_user(session, user_id) - _project = await _get_owned_project(project_id, user_id, session) - - repo = await session.get(GitRepository, repo_id) - if repo is None or repo.project_id != project_id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found" - ) - - if not os.path.exists(repo.path): - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found on disk" - ) - + from src.utils.git_history import get_commit_history + from src.services.git.repository import get_repo_and_validate, ensure_repo_on_disk + repo = await get_repo_and_validate(session, repo_id, project_id) + ensure_repo_on_disk(repo) try: - history = get_commit_history( - repo.path, branch=branch, limit=limit, offset=offset - ) - return history + return get_commit_history(repo.path, branch=branch, limit=limit, offset=offset) except RuntimeError as e: + logger.warning("Git history failed for %s: %s", repo.path, str(e)) raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) - ) + status_code=status.HTTP_400_BAD_REQUEST, + detail=f"Git history unavailable: {str(e)}", + ) from e -@router.get( - "/{project_id}/repositories/{repo_id}/commits/{commit_hash}", - summary="Get commit details", - description="Get detailed information about a specific commit.", -) +@router.get("/{project_id}/repositories/{repo_id}/commits/{commit_hash}") async def get_repository_commit( project_id: uuid.UUID, repo_id: uuid.UUID, commit_hash: str, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), session: AsyncSession = Depends(get_db_session), ) -> dict: - """Get detailed information about a specific commit. - - Args: - project_id: UUID of the project. - repo_id: UUID of the repository. - commit_hash: Hash of the commit to retrieve. - user_id: ID of the authenticated user. - session: Database session. - - Returns: - Dictionary containing commit details. - """ - _user = await _get_user(session, user_id) - _project = await _get_owned_project(project_id, user_id, session) - - repo = await session.get(GitRepository, repo_id) - if repo is None or repo.project_id != project_id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found" - ) - - if not os.path.exists(repo.path): - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found on disk" - ) - + from src.utils.git_history import get_commit_detail + from src.services.git.repository import get_repo_and_validate, ensure_repo_on_disk + repo = await get_repo_and_validate(session, repo_id, project_id) + ensure_repo_on_disk(repo) try: - detail = get_commit_detail(repo.path, commit_hash) - return detail - except (RuntimeError, ValueError) as e: - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) + return get_commit_detail(repo.path, commit_hash) + except RuntimeError as e: + logger.warning("Git commit detail failed for %s %s: %s", repo.path, commit_hash, str(e)) + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=f"Commit detail unavailable: {str(e)}", + ) from e -# File browsing endpoints +# File browsing - -class FileListResponse(BaseModel): - path: str - branch: str - entries: list[dict] - - -class FileContentResponse(BaseModel): - path: str - branch: str - content: str - size: int - encoding: str - language: str | None - is_binary: bool - last_commit: dict | None - - -class BranchesResponse(BaseModel): - branches: list[dict] - default_branch: str - - -class FileUpdateRequest(BaseModel): - path: str - branch: str - content: str - commit_message: str - - -class FileUpdateResponse(BaseModel): - commit_hash: str - message: str - branch: str - - -@router.get( - "/{project_id}/repositories/{repo_id}/files", - response_model=FileListResponse, - summary="List repository files", - description="List files and directories in a repository path.", -) +@router.get("/{project_id}/repositories/{repo_id}/files", response_model=FileListResponse) async def list_repository_files( project_id: uuid.UUID, repo_id: uuid.UUID, branch: str = "main", path: str = "", - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), session: AsyncSession = Depends(get_db_session), ) -> FileListResponse: - """List files and directories in a repository path. - - Args: - project_id: UUID of the project. - repo_id: UUID of the repository. - branch: Branch name to browse (default: main). - path: Directory path within the repository (default: root). - user_id: ID of the authenticated user. - session: Database session. - - Returns: - List of files and directories in the specified path. - """ - _user = await _get_user(session, user_id) - _project = await _get_owned_project(project_id, user_id, session) - - repo = await session.get(GitRepository, repo_id) - if repo is None or repo.project_id != project_id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found" - ) - - if not os.path.exists(repo.path): - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found on disk" - ) - - try: - entries = list_tree(repo.path, branch=branch, path=path) - return FileListResponse( - path=path, - branch=branch, - entries=[ - { - "name": e.name, - "type": e.type, - "path": e.path, - "size": e.size, - "mode": e.mode, - "last_commit": e.last_commit, - } - for e in entries - ], - ) - except RuntimeError as e: - logger.error( - "Failed to list files for repo %s (path=%s, branch=%s): %s", - repo_id, - path, - branch, - str(e), - exc_info=True, - ) - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) + return await git_files.list_files(session, project_id, repo_id, branch, path) -@router.get( - "/{project_id}/repositories/{repo_id}/files/content", - response_model=FileContentResponse, - summary="Get file content", - description="Get the content of a file in a repository.", -) +@router.get("/{project_id}/repositories/{repo_id}/files/content", response_model=FileContentResponse) async def get_repository_file_content( project_id: uuid.UUID, repo_id: uuid.UUID, branch: str, path: str, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), session: AsyncSession = Depends(get_db_session), ) -> FileContentResponse: - """Get the content of a file. - - Args: - project_id: UUID of the project. - repo_id: UUID of the repository. - branch: Branch name where the file is located. - path: File path within the repository. - user_id: ID of the authenticated user. - session: Database session. - - Returns: - File content and metadata. - """ - _user = await _get_user(session, user_id) - _project = await _get_owned_project(project_id, user_id, session) - - repo = await session.get(GitRepository, repo_id) - if repo is None or repo.project_id != project_id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found" - ) - - if not os.path.exists(repo.path): - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found on disk" - ) - - try: - file_content = get_file_content(repo.path, branch=branch, path=path) - return FileContentResponse( - path=file_content.path, - branch=file_content.branch, - content=file_content.content, - size=file_content.size, - encoding=file_content.encoding, - language=file_content.language, - is_binary=file_content.is_binary, - last_commit=file_content.last_commit, - ) - except FileNotFoundError: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="file not found" - ) - except RuntimeError as e: - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) + return await git_files.get_file(session, project_id, repo_id, branch, path) -@router.get( - "/{project_id}/repositories/{repo_id}/branches", - response_model=BranchesResponse, - summary="List branches", - description="List all branches in the repository.", -) -async def get_repository_branches( - project_id: uuid.UUID, - repo_id: uuid.UUID, - user_id: uuid.UUID = Depends(get_current_user_id), - session: AsyncSession = Depends(get_db_session), -) -> BranchesResponse: - """List all branches in the repository. - - Args: - project_id: UUID of the project. - repo_id: UUID of the repository. - user_id: ID of the authenticated user. - session: Database session. - - Returns: - List of branches and the default branch name. - """ - _user = await _get_user(session, user_id) - _project = await _get_owned_project(project_id, user_id, session) - - repo = await session.get(GitRepository, repo_id) - if repo is None or repo.project_id != project_id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found" - ) - - # Try local repo first (.git subdir for normal repos, HEAD for bare) - is_valid_git_repo = os.path.isdir( - os.path.join(repo.path, ".git") - ) or os.path.isfile(os.path.join(repo.path, "HEAD")) - - if is_valid_git_repo: - try: - branches, default_branch = list_branches(repo.path) - return BranchesResponse( - branches=[ - { - "name": b.name, - "is_default": b.is_default, - "last_commit": b.last_commit, - } - for b in branches - ], - default_branch=default_branch, - ) - except RuntimeError as e: - logger.error( - "Failed to list branches for repo %s: %s", - repo_id, - str(e), - exc_info=True, - ) - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) - ) from e - - # Local repo missing/corrupt — try remote if available - if repo.remote_url: - ssh_key = None - if repo.ssh_key_id: - ssh_key = await session.get(SSHKey, repo.ssh_key_id) - - ssh_result = _prepare_ssh_env(ssh_key) - env = None - key_path = None - if ssh_result: - env, key_path = ssh_result - - try: - result = subprocess.run( - ["git", "ls-remote", "--heads", repo.remote_url], - capture_output=True, - text=True, - timeout=30, - env={**os.environ, **env} if env else None, - ) - if result.returncode == 0: - remote_branches = [] - default_branch = "main" - for line in result.stdout.strip().split("\n"): - if line: - parts = line.split("\t") - if len(parts) == 2: - ref = parts[1] - if ref.startswith("refs/heads/"): - branch_name = ref[len("refs/heads/") :] - remote_branches.append(branch_name) - if branch_name in ("main", "master"): - default_branch = branch_name - if remote_branches: - return BranchesResponse( - branches=[ - { - "name": b, - "is_default": b == default_branch, - "last_commit": None, - } - for b in remote_branches - ], - default_branch=default_branch, - ) - else: - logger.warning( - "ls-remote returned %d for repo %s: %s", - result.returncode, - repo_id, - result.stderr, - ) - except subprocess.TimeoutExpired: - logger.warning("ls-remote timed out for repo %s", repo_id) - except Exception as e: - logger.warning("ls-remote failed for repo %s: %s", repo_id, str(e)) - finally: - if key_path and os.path.exists(key_path): - os.unlink(key_path) - - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail="repository not found on disk — re-clone or re-create the repository", - ) - - -@router.post( - "/{project_id}/repositories/{repo_id}/files/content", - response_model=FileUpdateResponse, - summary="Update file content", - description="Update a file and create a commit.", -) +@router.post("/{project_id}/repositories/{repo_id}/files/content", response_model=FileUpdateResponse) async def update_repository_file( project_id: uuid.UUID, repo_id: uuid.UUID, data: FileUpdateRequest, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), session: AsyncSession = Depends(get_db_session), ) -> FileUpdateResponse: - """Update a file and create a commit. - - Args: - project_id: UUID of the project. - repo_id: UUID of the repository. - data: File update data including path, branch, content, and commit message. - user_id: ID of the authenticated user. - session: Database session. - - Returns: - Commit information for the file update. - """ - _user = await _get_user(session, user_id) - _project = await _get_owned_project(project_id, user_id, session) - - repo = await session.get(GitRepository, repo_id) - if repo is None or repo.project_id != project_id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found" - ) - - if not os.path.exists(repo.path): - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found on disk" - ) - - # Get user info for commit - user = await _get_user(session, user_id) - author_name = user.name or "Unknown" - author_email = user.email or "unknown@example.com" - - try: - commit_hash = commit_file( - repo_path=repo.path, - branch=data.branch, - path=data.path, - content=data.content, - commit_message=data.commit_message, - author_name=author_name, - author_email=author_email, - ) - return FileUpdateResponse( - commit_hash=commit_hash, - message=data.commit_message, - branch=data.branch, - ) - except RuntimeError as e: - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) + return await git_files.update_file(session, project_id, repo_id, data, user) -# Git Control Endpoints +# Branches - -class StatusResponse(BaseModel): - branch: str - modified: list[str] - added: list[str] - deleted: list[str] - untracked: list[str] - renamed: list[str] - ahead: int - behind: int - - -@router.get( - "/{project_id}/repositories/{repo_id}/status", - response_model=StatusResponse, - summary="Get repository status", - description="Get the working directory status including modified, added, and deleted files.", -) -async def get_repository_status( +@router.get("/{project_id}/repositories/{repo_id}/branches") +async def get_repository_branches( project_id: uuid.UUID, repo_id: uuid.UUID, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), session: AsyncSession = Depends(get_db_session), -) -> StatusResponse: - """Get the working directory status. - - Args: - project_id: UUID of the project. - repo_id: UUID of the repository. - user_id: ID of the authenticated user. - session: Database session. - - Returns: - Repository status including branch, modified files, and ahead/behind counts. - """ - _user = await _get_user(session, user_id) - _project = await _get_owned_project(project_id, user_id, session) - - repo = await session.get(GitRepository, repo_id) - if repo is None or repo.project_id != project_id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found" - ) - - if not os.path.exists(repo.path): - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found on disk" - ) - - try: - status_result = get_status(repo.path) - return StatusResponse( - branch=status_result.branch, - modified=status_result.modified, - added=status_result.added, - deleted=status_result.deleted, - untracked=status_result.untracked, - renamed=status_result.renamed, - ahead=status_result.ahead, - behind=status_result.behind, - ) - except RuntimeError as e: - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) +) -> dict: + return await git_files.list_branches_with_validation(session, project_id, repo_id) -class BranchCreateRequest(BaseModel): - name: str - base_branch: str = "HEAD" - - -class CheckoutRequest(BaseModel): - branch: str - - -@router.post( - "/{project_id}/repositories/{repo_id}/branches", - summary="Create a branch", - description="Create a new branch in the repository.", -) +@router.post("/{project_id}/repositories/{repo_id}/branches") async def create_repository_branch( project_id: uuid.UUID, repo_id: uuid.UUID, data: BranchCreateRequest, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), session: AsyncSession = Depends(get_db_session), ) -> dict: - """Create a new branch. - - Args: - project_id: UUID of the project. - repo_id: UUID of the repository. - data: Branch creation data including name and optional base branch. - user_id: ID of the authenticated user. - session: Database session. - - Returns: - Dictionary with success message and branch name. - """ - _user = await _get_user(session, user_id) - _project = await _get_owned_project(project_id, user_id, session) - - repo = await session.get(GitRepository, repo_id) - if repo is None or repo.project_id != project_id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found" - ) - - if not os.path.exists(repo.path): - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found on disk" - ) - - try: - create_branch(repo.path, data.name, data.base_branch) - return {"message": f"Branch '{data.name}' created", "branch": data.name} - except RuntimeError as e: - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) + return await git_control.create_branch_with_validation(session, project_id, repo_id, data) -@router.delete( - "/{project_id}/repositories/{repo_id}/branches/{branch_name}", - summary="Delete a branch", - description="Delete a branch from the repository.", -) +@router.delete("/{project_id}/repositories/{repo_id}/branches/{branch_name}") async def delete_repository_branch( project_id: uuid.UUID, repo_id: uuid.UUID, branch_name: str, force: bool = False, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), session: AsyncSession = Depends(get_db_session), ) -> dict: - """Delete a branch. - - Args: - project_id: UUID of the project. - repo_id: UUID of the repository. - branch_name: Name of the branch to delete. - force: Whether to force delete the branch. - user_id: ID of the authenticated user. - session: Database session. - - Returns: - Dictionary with success message. - """ - _user = await _get_user(session, user_id) - _project = await _get_owned_project(project_id, user_id, session) - - repo = await session.get(GitRepository, repo_id) - if repo is None or repo.project_id != project_id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found" - ) - - if not os.path.exists(repo.path): - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found on disk" - ) - - try: - delete_branch(repo.path, branch_name, force) - return {"message": f"Branch '{branch_name}' deleted"} - except RuntimeError as e: - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) + return await git_control.delete_branch_with_validation(session, project_id, repo_id, branch_name, force) -@router.post( - "/{project_id}/repositories/{repo_id}/checkout", - summary="Checkout a branch", - description="Checkout a branch in the repository.", -) +@router.post("/{project_id}/repositories/{repo_id}/checkout") async def checkout_repository_branch( project_id: uuid.UUID, repo_id: uuid.UUID, data: CheckoutRequest, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), session: AsyncSession = Depends(get_db_session), ) -> dict: - """Checkout a branch. - - Args: - project_id: UUID of the project. - repo_id: UUID of the repository. - data: Checkout request containing the branch name. - user_id: ID of the authenticated user. - session: Database session. - - Returns: - Dictionary with success message and checked out branch name. - """ - _user = await _get_user(session, user_id) - _project = await _get_owned_project(project_id, user_id, session) - - repo = await session.get(GitRepository, repo_id) - if repo is None or repo.project_id != project_id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found" - ) - - if not os.path.exists(repo.path): - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found on disk" - ) - - try: - checkout_branch(repo.path, data.branch) - return {"message": f"Checked out branch '{data.branch}'", "branch": data.branch} - except RuntimeError as e: - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) + return await git_control.checkout_branch_with_validation(session, project_id, repo_id, data) -class CommitRequest(BaseModel): - message: str - files: list[str] | None = None +# Git control + +@router.get("/{project_id}/repositories/{repo_id}/status", response_model=StatusResponse) +async def get_repository_status( + project_id: uuid.UUID, + repo_id: uuid.UUID, + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), + session: AsyncSession = Depends(get_db_session), +) -> StatusResponse: + return await git_control.get_status_with_validation(session, project_id, repo_id) -class CommitResponse(BaseModel): - commit_hash: str - message: str - - -@router.post( - "/{project_id}/repositories/{repo_id}/commit", - response_model=CommitResponse, - summary="Commit changes", - description="Commit changes to the repository.", -) +@router.post("/{project_id}/repositories/{repo_id}/commit", response_model=CommitResponse) async def commit_repository_changes( project_id: uuid.UUID, repo_id: uuid.UUID, data: CommitRequest, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), session: AsyncSession = Depends(get_db_session), ) -> CommitResponse: - """Commit changes to the repository. - - Args: - project_id: UUID of the project. - repo_id: UUID of the repository. - data: Commit request containing message and optional files to commit. - user_id: ID of the authenticated user. - session: Database session. - - Returns: - Commit information including hash and message. - """ - _user = await _get_user(session, user_id) - _project = await _get_owned_project(project_id, user_id, session) - - repo = await session.get(GitRepository, repo_id) - if repo is None or repo.project_id != project_id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found" - ) - - if not os.path.exists(repo.path): - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found on disk" - ) - - # Get user info for commit - user = await _get_user(session, user_id) - author_name = user.name or "Unknown" - author_email = user.email or "unknown@example.com" - - try: - commit_hash = commit_changes( - repo_path=repo.path, - message=data.message, - author_name=author_name, - author_email=author_email, - files=data.files, - ) - return CommitResponse( - commit_hash=commit_hash, - message=data.message, - ) - except RuntimeError as e: - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) + result = await git_control.commit_changes_with_validation(session, project_id, repo_id, data, user) + return CommitResponse(commit_hash=result["commit_hash"], message=result["message"]) -class FetchResponse(BaseModel): - message: str - - -@router.post( - "/{project_id}/repositories/{repo_id}/fetch", - response_model=FetchResponse, - summary="Fetch from remote", - description="Fetch updates from the remote repository.", -) +@router.post("/{project_id}/repositories/{repo_id}/fetch", response_model=FetchResponse) async def fetch_repository( project_id: uuid.UUID, repo_id: uuid.UUID, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), session: AsyncSession = Depends(get_db_session), ) -> FetchResponse: - """Fetch from remote. - - Args: - project_id: UUID of the project. - repo_id: UUID of the repository. - user_id: ID of the authenticated user. - session: Database session. - - Returns: - Success message. - """ - _user = await _get_user(session, user_id) - _project = await _get_owned_project(project_id, user_id, session) - - repo = await session.get(GitRepository, repo_id) - if repo is None or repo.project_id != project_id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found" - ) - - if not os.path.exists(repo.path): - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found on disk" - ) - - try: - fetch(repo.path) - return FetchResponse(message="Fetched from remote") - except RuntimeError as e: - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) + return await git_control.fetch_with_validation(session, project_id, repo_id) -class PullResponse(BaseModel): - message: str - - -@router.post( - "/{project_id}/repositories/{repo_id}/pull", - response_model=PullResponse, - summary="Pull from remote", - description="Pull updates from the remote repository.", -) +@router.post("/{project_id}/repositories/{repo_id}/pull", response_model=PullResponse) async def pull_repository( project_id: uuid.UUID, repo_id: uuid.UUID, branch: str | None = None, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), session: AsyncSession = Depends(get_db_session), ) -> PullResponse: - """Pull updates from remote. - - Args: - project_id: UUID of the project. - repo_id: UUID of the repository. - branch: Optional branch name to pull. - user_id: ID of the authenticated user. - session: Database session. - - Returns: - Success message. - """ - _user = await _get_user(session, user_id) - _project = await _get_owned_project(project_id, user_id, session) - - repo = await session.get(GitRepository, repo_id) - if repo is None or repo.project_id != project_id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found" - ) - - if not os.path.exists(repo.path): - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found on disk" - ) - - try: - pull(repo.path, branch) - return PullResponse(message="Pulled from remote") - except RuntimeError as e: - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) + return await git_control.pull_with_validation(session, project_id, repo_id, branch) -class PushResponse(BaseModel): - message: str - - -@router.post( - "/{project_id}/repositories/{repo_id}/push", - response_model=PushResponse, - summary="Push to remote", - description="Push changes to the remote repository.", -) +@router.post("/{project_id}/repositories/{repo_id}/push", response_model=PushResponse) async def push_repository( project_id: uuid.UUID, repo_id: uuid.UUID, branch: str | None = None, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), session: AsyncSession = Depends(get_db_session), ) -> PushResponse: - """Push changes to remote. - - Args: - project_id: UUID of the project. - repo_id: UUID of the repository. - branch: Optional branch name to push. - user_id: ID of the authenticated user. - session: Database session. - - Returns: - Success message. - """ - _user = await _get_user(session, user_id) - _project = await _get_owned_project(project_id, user_id, session) - - repo = await session.get(GitRepository, repo_id) - if repo is None or repo.project_id != project_id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found" - ) - - if not os.path.exists(repo.path): - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found on disk" - ) - - try: - push(repo.path, branch) - return PushResponse(message="Pushed to remote") - except RuntimeError as e: - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) + return await git_control.push_with_validation(session, project_id, repo_id, branch) -class MergeRequest(BaseModel): - source_branch: str - target_branch: str | None = None - message: str | None = None - - -class MergeResponse(BaseModel): - commit_hash: str - message: str - - -@router.post( - "/{project_id}/repositories/{repo_id}/merge", - response_model=MergeResponse, - summary="Merge branches", - description="Merge one branch into another.", -) +@router.post("/{project_id}/repositories/{repo_id}/merge", response_model=MergeResponse) async def merge_repository_branches( project_id: uuid.UUID, repo_id: uuid.UUID, data: MergeRequest, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), session: AsyncSession = Depends(get_db_session), ) -> MergeResponse: - """Merge branches. - - Args: - project_id: UUID of the project. - repo_id: UUID of the repository. - data: Merge request containing source branch, optional target branch, and message. - user_id: ID of the authenticated user. - session: Database session. - - Returns: - Merge result with commit hash and message. - """ - _user = await _get_user(session, user_id) - _project = await _get_owned_project(project_id, user_id, session) - - repo = await session.get(GitRepository, repo_id) - if repo is None or repo.project_id != project_id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found" - ) - - if not os.path.exists(repo.path): - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found on disk" - ) - - try: - commit_hash = merge( - repo_path=repo.path, - source_branch=data.source_branch, - target_branch=data.target_branch, - message=data.message, - ) - return MergeResponse( - commit_hash=commit_hash, - message=data.message or f"Merge {data.source_branch}", - ) - except RuntimeError as e: - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) + return await git_control.merge_with_validation(session, project_id, repo_id, data) diff --git a/apps/api/src/api/health.py b/apps/api/src/api/health.py index e1ec443..51a4ca5 100644 --- a/apps/api/src/api/health.py +++ b/apps/api/src/api/health.py @@ -4,11 +4,18 @@ import time from datetime import datetime, timezone from typing import Any -from fastapi import APIRouter -from pydantic import BaseModel, Field +from fastapi import APIRouter, status from sqlalchemy import text +from src.config import Settings from src.database import SessionLocal +from src.schemas.health import ( + DatabaseHealth, + DatabaseHealthResponse, + DiskHealth, + HealthChecks, + HealthResponse, +) router = APIRouter() @@ -16,45 +23,6 @@ router = APIRouter() _start_time = time.time() -class DatabaseHealth(BaseModel): - """Database health check result.""" - - status: str = Field(description="Database health status", examples=["healthy"]) - response_time_ms: float = Field(description="Query response time in milliseconds", examples=[5.2]) - - -class DiskHealth(BaseModel): - """Disk space health check result.""" - - status: str = Field(description="Disk health status", examples=["healthy"]) - free_gb: float = Field(description="Free disk space in GB", examples=[45.2]) - total_gb: float = Field(description="Total disk space in GB", examples=[100.0]) - - -class HealthChecks(BaseModel): - """Individual health checks.""" - - database: DatabaseHealth | None = None - disk: DiskHealth | None = None - - -class HealthResponse(BaseModel): - """Overall health check response.""" - - status: str = Field(description="Overall health status", examples=["healthy"]) - timestamp: str = Field(description="ISO 8601 timestamp", examples=["2026-05-19T12:00:00Z"]) - version: str = Field(description="API version", examples=["0.1.0"]) - checks: HealthChecks = Field(description="Individual health checks") - uptime_seconds: float = Field(description="Server uptime in seconds", examples=[3600.0]) - - -class DatabaseHealthResponse(BaseModel): - """Database-specific health check response.""" - - status: str = Field(description="Database health status", examples=["healthy"]) - response_time_ms: float = Field(description="Query response time in milliseconds", examples=[5.2]) - - @router.get( "/health", response_model=HealthResponse, diff --git a/apps/api/src/api/projects.py b/apps/api/src/api/projects.py index d68cacb..0b776a9 100644 --- a/apps/api/src/api/projects.py +++ b/apps/api/src/api/projects.py @@ -3,47 +3,24 @@ import shutil import uuid from fastapi import APIRouter, Depends, HTTPException, Response, status -from pydantic import BaseModel, ConfigDict -from sqlalchemy import func, select +from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession -from src.auth.dependencies import ( - _get_owned_project, - _get_user, - get_current_user_id, - get_db_session, -) +from src.auth.dependencies import get_current_user, get_db_session, get_owned_project from src.models.git_repository import GitRepository from src.models.project import Project from src.models.ssh_key import SSHKey -from src.models.tool_instance import ToolInstance +from src.models.user import User +from src.schemas.project import ( + ProjectCreate, + ProjectUpdate, + ProjectResponse, + SetDefaultSSHKeyRequest, +) router = APIRouter(prefix="/projects", tags=["projects"]) -class ProjectCreate(BaseModel): - name: str - description: str | None = None - - -class ProjectUpdate(BaseModel): - name: str | None = None - description: str | None = None - - -class ProjectResponse(BaseModel): - model_config = ConfigDict(from_attributes=True) - - id: uuid.UUID - name: str - description: str | None - owner_id: uuid.UUID - default_ssh_key_id: uuid.UUID | None - - -class SetDefaultSSHKeyRequest(BaseModel): - ssh_key_id: uuid.UUID - @router.post( "", @@ -54,7 +31,7 @@ class SetDefaultSSHKeyRequest(BaseModel): ) async def create_project( data: ProjectCreate, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), session: AsyncSession = Depends(get_db_session), ) -> Project: """Create a new project. @@ -67,7 +44,6 @@ async def create_project( Returns: The newly created project. """ - user = await _get_user(session, user_id) project = Project( name=data.name, description=data.description, @@ -82,77 +58,25 @@ async def create_project( @router.get( "", + response_model=list[ProjectResponse], summary="List all projects", - description="Retrieve all projects owned by the authenticated user with repositories and workspaces.", + description="Retrieve all projects owned by the authenticated user.", ) async def list_projects( - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), session: AsyncSession = Depends(get_db_session), -) -> list[dict]: +) -> list[Project]: """List all projects for the authenticated user. - Returns projects with nested repositories and workspaces for inline display. + Args: + user_id: ID of the authenticated user. + session: Database session. + + Returns: + List of projects owned by the user. """ - user = await _get_user(session, user_id) - result = await session.execute( - select(Project) - .where(Project.owner_id == user.id) - .order_by(Project.created_at.desc()) - ) - projects = result.scalars().all() - - from src.models.workspace import Workspace - - enriched = [] - for project in projects: - repos_result = await session.execute( - select(GitRepository).where(GitRepository.project_id == project.id) - ) - repositories = [] - for repo in repos_result.scalars().all(): - ws_result = await session.execute( - select(Workspace).where(Workspace.repo_id == repo.id) - ) - workspaces = [] - for ws in ws_result.scalars().all(): - # Count instances - inst_result = await session.execute( - select(func.count()).where(ToolInstance.workspace_id == ws.id) - ) - instance_count = inst_result.scalar() or 0 - workspaces.append( - { - "id": str(ws.id), - "name": ws.name, - "branch": ws.branch, - "status": ws.status, - "instance_count": instance_count, - } - ) - - repositories.append( - { - "id": str(repo.id), - "name": repo.name, - "remote_url": repo.remote_url, - "workspaces": workspaces, - } - ) - - enriched.append( - { - "id": str(project.id), - "name": project.name, - "description": project.description, - "owner_id": str(project.owner_id), - "repositories": repositories, - "created_at": project.created_at.isoformat() - if project.created_at - else None, - } - ) - - return enriched + result = await session.execute(select(Project).where(Project.owner_id == user.id)) + return list(result.scalars().all()) @router.get( @@ -163,7 +87,8 @@ async def list_projects( ) async def get_project( project_id: uuid.UUID, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), session: AsyncSession = Depends(get_db_session), ) -> Project: """Get a specific project by ID. @@ -176,8 +101,8 @@ async def get_project( Returns: The requested project. """ - await _get_user(session, user_id) - return await _get_owned_project(project_id, user_id, session) + return project + @router.patch( @@ -189,7 +114,8 @@ async def get_project( async def update_project( project_id: uuid.UUID, data: ProjectUpdate, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), session: AsyncSession = Depends(get_db_session), ) -> Project: """Update a project. @@ -203,8 +129,6 @@ async def update_project( Returns: The updated project. """ - await _get_user(session, user_id) - project = await _get_owned_project(project_id, user_id, session) if data.name is not None: project.name = data.name @@ -224,7 +148,8 @@ async def update_project( ) async def delete_project( project_id: uuid.UUID, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), session: AsyncSession = Depends(get_db_session), ) -> Response: """Delete a project and all its repositories. @@ -237,13 +162,9 @@ async def delete_project( Returns: Empty response with 204 status code. """ - await _get_user(session, user_id) - project = await _get_owned_project(project_id, user_id, session) # Delete repositories from disk and database - result = await session.execute( - select(GitRepository).where(GitRepository.project_id == project_id) - ) + result = await session.execute(select(GitRepository).where(GitRepository.project_id == project_id)) repositories = result.scalars().all() for repo in repositories: if os.path.exists(repo.path): @@ -264,7 +185,8 @@ async def delete_project( async def set_default_ssh_key( project_id: uuid.UUID, data: SetDefaultSSHKeyRequest, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), session: AsyncSession = Depends(get_db_session), ) -> Project: """Set the default SSH key for a project. @@ -278,8 +200,6 @@ async def set_default_ssh_key( Returns: The updated project. """ - user = await _get_user(session, user_id) - project = await _get_owned_project(project_id, user_id, session) ssh_key = await session.get(SSHKey, data.ssh_key_id) if ssh_key is None or ssh_key.user_id != user.id: diff --git a/apps/api/src/api/ssh_keys.py b/apps/api/src/api/ssh_keys.py index a18a96c..1a10187 100644 --- a/apps/api/src/api/ssh_keys.py +++ b/apps/api/src/api/ssh_keys.py @@ -1,4 +1,3 @@ -import base64 import uuid from datetime import datetime @@ -6,17 +5,19 @@ from cryptography.fernet import Fernet from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey from fastapi import APIRouter, Depends, HTTPException, status -from pydantic import BaseModel, ConfigDict from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession -from src.auth.dependencies import _get_user, get_current_user_id, get_db_session +from src.auth.dependencies import get_current_user, get_db_session from src.config import Settings from src.models.ssh_key import SSHKey +from src.models.user import User +from src.schemas.ssh_key import SSHKeyCreate, SSHKeyResponse router = APIRouter(prefix="/ssh-keys", tags=["ssh-keys"]) + def _get_fernet() -> Fernet: """Generate a valid Fernet key from the session secret.""" import base64 @@ -53,36 +54,6 @@ def generate_ssh_key_pair() -> tuple[str, str]: return private_bytes.decode("utf-8"), public_bytes.decode("utf-8") -class SSHKeyCreate(BaseModel): - name: str - - -class SSHKeyResponse(BaseModel): - model_config = ConfigDict(from_attributes=True) - - id: uuid.UUID - name: str - public_key: str - created_at: datetime - - -class SignPayloadRequest(BaseModel): - payload: str - - -class SignatureResponse(BaseModel): - signature: str - - -class VerifySignatureRequest(BaseModel): - payload: str - signature: str - - -class VerifySignatureResponse(BaseModel): - valid: bool - - @router.post( "", response_model=SSHKeyResponse, @@ -92,7 +63,7 @@ class VerifySignatureResponse(BaseModel): ) async def create_ssh_key( data: SSHKeyCreate, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), session: AsyncSession = Depends(get_db_session), ) -> SSHKey: """Create a new SSH key pair. @@ -105,7 +76,6 @@ async def create_ssh_key( Returns: The newly created SSH key with public key exposed. """ - user = await _get_user(session, user_id) private_key, public_key = generate_ssh_key_pair() fernet = _get_fernet() @@ -130,7 +100,7 @@ async def create_ssh_key( description="List all SSH keys for the authenticated user.", ) async def list_ssh_keys( - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), session: AsyncSession = Depends(get_db_session), ) -> list[SSHKey]: """List all SSH keys for the authenticated user. @@ -142,7 +112,6 @@ async def list_ssh_keys( Returns: List of SSH keys owned by the user. """ - user = await _get_user(session, user_id) result = await session.execute(select(SSHKey).where(SSHKey.user_id == user.id)) return list(result.scalars().all()) @@ -155,7 +124,7 @@ async def list_ssh_keys( ) async def delete_ssh_key( key_id: uuid.UUID, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), session: AsyncSession = Depends(get_db_session), ) -> None: """Delete an SSH key. @@ -168,87 +137,9 @@ async def delete_ssh_key( Returns: None with 204 status code. """ - user = await _get_user(session, user_id) ssh_key = await session.get(SSHKey, key_id) if ssh_key is None or ssh_key.user_id != user.id: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="ssh key not found") await session.delete(ssh_key) await session.commit() - - -@router.post( - "/{key_id}/sign", - response_model=SignatureResponse, - summary="Sign payload", - description="Sign a payload using the SSH private key.", -) -async def sign_payload( - key_id: uuid.UUID, - data: SignPayloadRequest, - user_id: uuid.UUID = Depends(get_current_user_id), - session: AsyncSession = Depends(get_db_session), -) -> SignatureResponse: - """Sign a payload with an SSH key. - - Args: - key_id: UUID of the SSH key to use for signing. - data: Sign request containing the payload string. - user_id: ID of the authenticated user. - session: Database session. - - Returns: - Base64-encoded Ed25519 signature. - """ - user = await _get_user(session, user_id) - ssh_key = await session.get(SSHKey, key_id) - if ssh_key is None or ssh_key.user_id != user.id: - raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="ssh key not found") - - fernet = _get_fernet() - private_key_pem = fernet.decrypt(ssh_key.private_key_encrypted.encode()).decode() - - private_key = serialization.load_ssh_private_key( - private_key_pem.encode(), password=None - ) - - signature = private_key.sign(data.payload.encode()) - return SignatureResponse(signature=base64.b64encode(signature).decode()) - - -@router.post( - "/{key_id}/verify", - response_model=VerifySignatureResponse, - summary="Verify signature", - description="Verify a signature against a payload using the SSH public key.", -) -async def verify_signature( - key_id: uuid.UUID, - data: VerifySignatureRequest, - user_id: uuid.UUID = Depends(get_current_user_id), - session: AsyncSession = Depends(get_db_session), -) -> VerifySignatureResponse: - """Verify a signature with an SSH key's public key. - - Args: - key_id: UUID of the SSH key to use for verification. - data: Verify request containing payload and base64-encoded signature. - user_id: ID of the authenticated user. - session: Database session. - - Returns: - Whether the signature is valid. - """ - user = await _get_user(session, user_id) - ssh_key = await session.get(SSHKey, key_id) - if ssh_key is None or ssh_key.user_id != user.id: - raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="ssh key not found") - - public_key = serialization.load_ssh_public_key(ssh_key.public_key.encode()) - - try: - signature = base64.b64decode(data.signature) - public_key.verify(signature, data.payload.encode()) - return VerifySignatureResponse(valid=True) - except Exception: - return VerifySignatureResponse(valid=False) diff --git a/apps/api/src/api/terminal.py b/apps/api/src/api/terminal.py index c2f2a8d..0ee52e4 100644 --- a/apps/api/src/api/terminal.py +++ b/apps/api/src/api/terminal.py @@ -1,102 +1,65 @@ """WebSocket terminal endpoint for tool instances.""" import asyncio -import json import logging import uuid -from contextlib import suppress -from fastapi import APIRouter, Depends, HTTPException, WebSocket, status -from sqlalchemy import select +from fastapi import APIRouter, Depends, WebSocket from sqlalchemy.ext.asyncio import AsyncSession -from starlette.websockets import WebSocketDisconnect -from src.auth.dependencies import get_current_user_id, get_db_session -from src.models.terminal_session import TerminalSessionModel +from src.auth.dependencies import get_db_session from src.models.tool_instance import ToolInstance -from src.models.tool_type import ToolType -from src.services.terminal_manager import MaxSessionsExceededError, terminal_manager +from src.services.terminal_manager import terminal_manager router = APIRouter() logger = logging.getLogger(__name__) -class SessionRef: - """Mutable reference to a terminal session, allowing updates during reset.""" - - def __init__(self, session, slot_session_id: str | None = None): - self.session = session - self.slot_session_id = slot_session_id or session.session_id - - @router.websocket( "/ws/tool-instances/{instance_id}/terminal", ) -async def terminal_websocket_default( +async def terminal_websocket( websocket: WebSocket, instance_id: str, db_session: AsyncSession = Depends(get_db_session), ) -> None: - """WebSocket endpoint for terminal access (default session alias). + """WebSocket endpoint for terminal access to a tool instance. - Backward-compatible route that maps to the default session. - """ - await _handle_terminal_websocket(websocket, instance_id, None, db_session) - - -@router.websocket( - "/ws/tool-instances/{instance_id}/terminal/{session_id}", -) -async def terminal_websocket_specific( - websocket: WebSocket, - instance_id: str, - session_id: str, - db_session: AsyncSession = Depends(get_db_session), -) -> None: - """WebSocket endpoint for a specific terminal session.""" - await _handle_terminal_websocket(websocket, instance_id, session_id, db_session) - - -async def _handle_terminal_websocket( - websocket: WebSocket, - instance_id: str, - target_session_id: str | None, - db_session: AsyncSession, -) -> None: - """Shared WebSocket handler for terminal sessions. + Provides an interactive terminal session inside a running tool instance container. + Supports: + - Auto-reconnection (client reconnects, server spawns new session) + - Heartbeat ping/pong + - Binary and text input frames + - Graceful session end notifications Args: websocket: The WebSocket connection. instance_id: UUID string of the tool instance. - target_session_id: Specific session ID (slot key). None means default session. db_session: Database session. + + Returns: + None. Communicates via WebSocket messages. + """ - logger.debug( - "Terminal WebSocket connection attempt for instance %s (session=%s)", - instance_id, - target_session_id or "default", - ) + logger.info("Terminal WebSocket connection attempt for instance %s", instance_id) await websocket.accept() - logger.debug("Terminal WebSocket accepted for instance %s", instance_id) try: - # Parse instance_id instance_uuid = uuid.UUID(instance_id) except ValueError: logger.error("Invalid instance ID: %s", instance_id) await websocket.close(code=4001, reason="Invalid instance ID") return - # Authenticate user from session cookie user_id = await _get_user_from_websocket(websocket, db_session) if user_id is None: logger.warning( - "Unauthorized terminal access attempt for instance %s", instance_id + "Unauthorized terminal access attempt for instance %s", + instance_id, ) await websocket.close(code=4003, reason="Unauthorized") return - # Get instance and verify ownership instance = await db_session.get(ToolInstance, instance_uuid) if instance is None: logger.warning("Instance %s not found", instance_id) @@ -122,605 +85,50 @@ async def _handle_terminal_websocket( await websocket.close(code=4004, reason="Instance not running") return - logger.debug("Terminal auth passed for instance %s, user %s", instance_id, user_id) - - # Verify the container actually exists (may have been removed/recreated) - from src.services.docker import get_container_status - - container_status = get_container_status(instance.container_id) - if container_status["status"] == "not_found": - logger.error( - "Container %s for instance %s not found (may have been removed)", - instance.container_id, - instance_id, - ) - await websocket.close( - code=4004, reason="Container not found — restart the tool instance" - ) - return - - # Fetch tool type to get startup_command - tool_type = await db_session.get(ToolType, instance.tool_type_id) - startup_command = tool_type.startup_command if tool_type else None - if startup_command: - logger.debug( - "Using startup command for instance %s: %s", - instance_id, - startup_command, - ) - - session = None - - # Get or create terminal session + logger.info( + "Creating terminal session for instance %s (container_id=%s)", + instance_id, + instance.container_id, + ) try: - if target_session_id is None: - # Default session alias - session = await terminal_manager.get_or_create_session( - instance_uuid, - instance.container_id, - startup_command=startup_command, - ) - slot_session_id = "default" - else: - # Specific session - session = terminal_manager.get_session( - instance_id, - target_session_id, - ) - if session is None: - # Session not in memory — may have been lost on server restart. - # Try to restore from the DB row. - db_row = await db_session.get( - TerminalSessionModel, uuid.UUID(target_session_id) - ) - if ( - db_row is not None - and db_row.instance_id == instance_uuid - and db_row.status != "closed" - ): - logger.info( - "Restoring terminal session %s for instance %s from DB", - target_session_id, - instance_id, - ) - session = await terminal_manager.create_session( - instance_uuid, - instance.container_id, - startup_command=startup_command, - name=db_row.name, - session_id=target_session_id, - ) - else: - logger.warning( - "Session %s not found for instance %s", - target_session_id, - instance_id, - ) - await websocket.close(code=4004, reason="Session not found") - return - # Determine slot key for reset scoping - key = terminal_manager._find_key_by_internal_id( - instance_id, session.session_id - ) - slot_session_id = key[1] if key else target_session_id - - logger.debug( - "Terminal session ready for instance %s (session_id=%s, slot=%s)", - instance_id, - session.session_id, - slot_session_id, + session = await terminal_manager.create_session( + instance_uuid, + instance.container_id, + websocket, + ) + logger.info( + "Terminal session created successfully for instance %s", + instance_id, ) - - # Attach WebSocket to session - await terminal_manager.attach_websocket(session, websocket) - logger.debug("WebSocket attached to session for instance %s", instance_id) # Send connected status await websocket.send_json({"type": "status", "status": "connected"}) - logger.debug("Sent connected status for instance %s", instance_id) - # Use mutable session reference so loops can survive reset - session_ref = SessionRef(session, slot_session_id) - - # Start write loop and heartbeat (read is now event-driven in TerminalSession) - write_task = asyncio.create_task( - _write_loop(session_ref, websocket, instance_id) - ) - heartbeat_task = asyncio.create_task(_heartbeat_loop(websocket)) - logger.debug("Started terminal loops for instance %s", instance_id) - - # Wait for either task to complete (indicating disconnect or error) - done, pending = await asyncio.wait( - [write_task, heartbeat_task], - return_when=asyncio.FIRST_COMPLETED, - ) - - logger.debug( - "Terminal loop completed for instance %s, done=%s", - instance_id, - len(done), - ) - - # Cancel remaining tasks - for task in pending: - task.cancel() - - except WebSocketDisconnect: - logger.debug("WebSocket disconnected for instance %s", instance_id) - except Exception as exc: - logger.error( - "Terminal session error for instance %s: %s", - instance_id, - str(exc), - exc_info=True, - ) - with suppress(Exception): - await websocket.close(code=4000, reason=f"Error: {exc}") - finally: - # Detach WebSocket, don't kill session - with suppress(Exception): - if session is not None: - await terminal_manager.detach_websocket(session, websocket) - logger.debug( - "WebSocket detached from session for instance %s", instance_id + # Monitor session health and echo state + while session.is_alive() and not session.closed: + # Check echo state periodically + new_echo_state = await session.check_echo_state() + if new_echo_state is not None: + await websocket.send_json( + {"type": "set_echo_state", "enabled": new_echo_state}, ) + await asyncio.sleep(1.0) + # Session ended — determine reason and notify client + exit_reason = session.get_exit_reason() or "process_exit" + await websocket.send_json({"type": "session_ended", "reason": exit_reason}) + await websocket.close(code=1000, reason=f"Session ended: {exit_reason}") -async def _write_loop(session_ref: SessionRef, websocket, instance_id: str) -> None: - """Read input from WebSocket and send to container.""" - try: - while True: - session = session_ref.session - if not session.is_alive() or session._closed: - await asyncio.sleep(0.1) - continue - message = await websocket.receive() - if message["type"] == "websocket.receive": - if "bytes" in message: - await session.write_input(message["bytes"]) - elif "text" in message: - text = message["text"] - if text.startswith("{"): - # Control message (JSON) - try: - ctrl = json.loads(text) - msg_type = ctrl.get("type") - - if msg_type == "resize": - cols = ctrl.get("cols", 80) - rows = ctrl.get("rows", 24) - logger.debug( - "Received resize message for instance %s: %sx%s", - instance_id, - cols, - rows, - ) - await session.resize(cols, rows) - elif msg_type == "ack": - char_count = ctrl.get("chars", 0) - if char_count > 0: - session.acknowledge_data(char_count) - elif msg_type == "reset": - # Reset terminal session (scoped to current slot) - logger.debug( - "Resetting terminal session for instance %s (slot=%s)", - session.instance_id, - session_ref.slot_session_id, - ) - await websocket.send_json( - {"type": "status", "status": "resetting"} - ) - - # Reset the session scoped to its slot - new_session = await terminal_manager.reset_session( - session.instance_id, - session.container_id, - startup_command=session.startup_command, - session_id=session_ref.slot_session_id, - name=session.name, - ) - - # Update the mutable session reference - session_ref.session = new_session - - # Attach to new session - await terminal_manager.attach_websocket( - new_session, websocket - ) - await websocket.send_json( - {"type": "status", "status": "connected"} - ) - - # Continue the loop with the new session - continue - - except json.JSONDecodeError: - # Not a valid JSON control message, treat as regular input - await session.write_input(text.encode("utf-8")) - else: - await session.write_input(text.encode("utf-8")) - elif message["type"] == "websocket.disconnect": - break except Exception: + logger.exception( + "Terminal session error for instance %s", + instance_id, + ) + await websocket.close(code=4000, reason="Terminal session error") + finally: pass -async def _heartbeat_loop(websocket: WebSocket) -> None: - """Send periodic ping messages to detect disconnections.""" - try: - while True: - await asyncio.sleep(30) # Ping every 30 seconds - try: - await websocket.send_json({"type": "ping"}) - except Exception: - # WebSocket is closed or broken - break - except Exception: - pass - - -async def _get_terminal_instance( - instance_id: uuid.UUID, - user_id: uuid.UUID, - db_session: AsyncSession, -) -> ToolInstance: - """Fetch instance and validate auth, ownership, and running status. - - Args: - instance_id: UUID of the tool instance. - user_id: ID of the authenticated user. - db_session: Database session. - - Returns: - The validated ToolInstance. - - Raises: - HTTPException: If instance not found, not owned, or not running. - """ - instance = await db_session.get(ToolInstance, instance_id) - if instance is None: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Instance not found" - ) - - if instance.owner_id != user_id: - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, - detail="Not authorized to access this instance", - ) - - if instance.status != "running" or not instance.container_id: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, detail="Instance is not running" - ) - - return instance - - -@router.get( - "/instances/{instance_id}/terminal/sessions", - summary="List terminal sessions", - description="List terminal sessions for a tool instance with live WebSocket state.", -) -async def list_terminal_sessions( - instance_id: uuid.UUID, - user_id: uuid.UUID = Depends(get_current_user_id), - db_session: AsyncSession = Depends(get_db_session), -) -> dict: - """List terminal sessions for an instance. - - Args: - instance_id: UUID of the tool instance. - user_id: ID of the authenticated user. - db_session: Database session. - - Returns: - Dictionary with sessions list. - """ - await _get_terminal_instance(instance_id, user_id, db_session) - - # Query active DB rows for this instance - result = await db_session.execute( - select(TerminalSessionModel) - .where(TerminalSessionModel.instance_id == instance_id) - .where(TerminalSessionModel.status != "closed") - .order_by(TerminalSessionModel.created_at.asc()) - ) - db_rows = result.scalars().all() - - # Build response with live has_websockets flag. - # Include DB rows even without in-memory counterparts (e.g. after - # server restart) so the frontend can display tabs and reconnect. - sessions = [] - for row in db_rows: - live_session = terminal_manager.get_session(str(instance_id), str(row.id)) - sessions.append( - { - "id": str(row.id), - "name": row.name, - "status": row.status, - "has_websockets": live_session.has_websockets() - if live_session - else False, - "created_at": row.created_at.isoformat() if row.created_at else None, - "last_activity_at": row.last_activity_at.isoformat() - if row.last_activity_at - else None, - } - ) - - return {"sessions": sessions} - - -@router.post( - "/instances/{instance_id}/terminal/sessions", - summary="Create terminal session", - description="Create a new terminal session for a running tool instance.", - status_code=status.HTTP_201_CREATED, -) -async def create_terminal_session( - instance_id: uuid.UUID, - data: dict, - user_id: uuid.UUID = Depends(get_current_user_id), - db_session: AsyncSession = Depends(get_db_session), -) -> dict: - """Create a new terminal session. - - Args: - instance_id: UUID of the tool instance. - data: Request body with optional name. - user_id: ID of the authenticated user. - db_session: Database session. - - Returns: - Dictionary with new session details. - - Raises: - HTTPException: 409 if max sessions reached. - """ - instance = await _get_terminal_instance(instance_id, user_id, db_session) - assert instance.container_id is not None - - # Fetch tool type to get startup_command - tool_type = await db_session.get(ToolType, instance.tool_type_id) - startup_command = tool_type.startup_command if tool_type else None - - name = data.get("name") - - try: - session = await terminal_manager.create_session( - instance_id, - instance.container_id, - startup_command=startup_command, - name=name, - ) - except MaxSessionsExceededError: - raise HTTPException( - status_code=status.HTTP_409_CONFLICT, - detail="Maximum of 5 terminal sessions reached for this instance", - ) from None - - return { - "id": session.session_id, - "name": session.name, - "status": session.status, - "created_at": session.last_activity, - } - - -@router.delete( - "/instances/{instance_id}/terminal/sessions/{session_id}", - summary="Close terminal session", - description="Close a specific terminal session.", -) -async def close_terminal_session( - instance_id: uuid.UUID, - session_id: str, - user_id: uuid.UUID = Depends(get_current_user_id), - db_session: AsyncSession = Depends(get_db_session), -) -> dict: - """Close a terminal session. - - Args: - instance_id: UUID of the tool instance. - session_id: ID of the session to close. - user_id: ID of the authenticated user. - db_session: Database session. - - Returns: - Dictionary with closure status. - """ - await _get_terminal_instance(instance_id, user_id, db_session) - - # Find the session by internal ID to determine its slot key - key = terminal_manager._find_key_by_internal_id(str(instance_id), session_id) - if ( - key is None - and terminal_manager.get_session(str(instance_id), session_id) is not None - ): - key = (str(instance_id), session_id) - - if key is None: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Session not found" - ) - - await terminal_manager.close_session(key[0], key[1]) - - return {"status": "closed", "session_id": session_id} - - -@router.post( - "/instances/{instance_id}/terminal/sessions/{session_id}/reset", - summary="Reset terminal session", - description="Reset a specific terminal session, killing the current shell and starting fresh.", -) -async def reset_specific_terminal_session( - instance_id: uuid.UUID, - session_id: str, - user_id: uuid.UUID = Depends(get_current_user_id), - db_session: AsyncSession = Depends(get_db_session), -) -> dict: - """Reset a specific terminal session. - - Args: - instance_id: UUID of the tool instance. - session_id: ID of the session to reset. - user_id: ID of the authenticated user. - db_session: Database session. - - Returns: - Dictionary with reset session details. - """ - instance = await _get_terminal_instance(instance_id, user_id, db_session) - assert instance.container_id is not None - - # Determine slot key for reset - key = terminal_manager._find_key_by_internal_id(str(instance_id), session_id) - if ( - key is None - and terminal_manager.get_session(str(instance_id), session_id) is not None - ): - key = (str(instance_id), session_id) - - if key is None: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Session not found" - ) - - # Fetch tool type to get startup_command - tool_type = await db_session.get(ToolType, instance.tool_type_id) - startup_command = tool_type.startup_command if tool_type else None - - # Preserve name if possible - live_session = terminal_manager.get_session(str(instance_id), session_id) - name = live_session.name if live_session else None - - new_session = await terminal_manager.reset_session( - instance_id, - instance.container_id, - startup_command=startup_command, - session_id=key[1], - name=name, - ) - - return { - "id": new_session.session_id, - "name": new_session.name, - "status": new_session.status, - } - - -@router.post( - "/instances/{instance_id}/terminal/sessions/{session_id}/rename", - summary="Rename terminal session", - description="Rename a specific terminal session.", -) -async def rename_terminal_session( - instance_id: uuid.UUID, - session_id: str, - data: dict, - user_id: uuid.UUID = Depends(get_current_user_id), - db_session: AsyncSession = Depends(get_db_session), -) -> dict: - """Rename a terminal session. - - Args: - instance_id: UUID of the tool instance. - session_id: ID of the session to rename. - data: Request body with new name. - user_id: ID of the authenticated user. - db_session: Database session. - - Returns: - Dictionary with updated session details. - """ - await _get_terminal_instance(instance_id, user_id, db_session) - - new_name = data.get("name") - if not new_name or not isinstance(new_name, str): - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, detail="Name is required" - ) - - # Update in-memory session name if live - live_session = terminal_manager.get_session(str(instance_id), session_id) - if live_session: - live_session.name = new_name - - # Update DB row - db_row = await db_session.get(TerminalSessionModel, uuid.UUID(session_id)) - if db_row is None: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Session not found" - ) - - db_row.name = new_name - await db_session.commit() - - return {"id": str(db_row.id), "name": new_name} - - -@router.post( - "/instances/{instance_id}/terminal/reset", - summary="Reset terminal session (legacy alias)", - description="Reset the default terminal session for a tool instance. Preserved for backward compatibility.", -) -async def reset_terminal_session( - instance_id: uuid.UUID, - user_id: uuid.UUID = Depends(get_current_user_id), - db_session: AsyncSession = Depends(get_db_session), -) -> dict: - """Reset the default terminal session for an instance (legacy alias). - - Args: - instance_id: UUID of the tool instance. - user_id: ID of the authenticated user. - db_session: Database session. - - Returns: - Dictionary with status message. - """ - instance = await _get_terminal_instance(instance_id, user_id, db_session) - assert instance.container_id is not None - - # Fetch tool type to get startup_command - tool_type = await db_session.get(ToolType, instance.tool_type_id) - startup_command = tool_type.startup_command if tool_type else None - - try: - # Reset the default session - new_session = await terminal_manager.reset_session( - instance_id, - instance.container_id, - startup_command=startup_command, - ) - - logger.info( - "Terminal session reset for instance %s (new session_id=%s)", - instance_id, - new_session.session_id, - ) - - return { - "status": "success", - "message": "Terminal session reset successfully", - "instance_id": str(instance_id), - "session_id": new_session.session_id, - } - except Exception as exc: - logger.error( - "Failed to reset terminal session for instance %s: %s", - instance_id, - str(exc), - exc_info=True, - ) - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Failed to reset terminal session: {exc}", - ) from exc - - async def _get_user_from_websocket( websocket: WebSocket, db_session: AsyncSession, @@ -733,6 +141,7 @@ async def _get_user_from_websocket( Returns: The user's UUID if authenticated, None otherwise. + """ from src.auth.session import decode_session_cookie from src.config import Settings diff --git a/apps/api/src/api/tool_configs.py b/apps/api/src/api/tool_configs.py new file mode 100644 index 0000000..db8cb48 --- /dev/null +++ b/apps/api/src/api/tool_configs.py @@ -0,0 +1,213 @@ +"""Tool configuration API endpoints.""" + +import logging +import uuid + +from fastapi import APIRouter, Depends, HTTPException, status +from sqlalchemy import select +from sqlalchemy.ext.asyncio import AsyncSession + +from src.auth.dependencies import get_current_user_id, get_db_session +from src.models.tool_config import ToolConfig +from src.models.tool_type import ToolType +from src.schemas.tool_config import ToolConfigCreate, ToolConfigUpdate, ToolConfigResponse + +logger = logging.getLogger(__name__) + +router = APIRouter(prefix="/tool-configs", tags=["tool-configs"]) + + +@router.get("", summary="List tool configs", description="Get all tool configs for the current user.") +async def list_configs( + tool_type_id: str | None = None, + project_id: str | None = None, + user_id: uuid.UUID = Depends(get_current_user_id), + session: AsyncSession = Depends(get_db_session), +) -> list: + """List tool configs for the current user.""" + query = select(ToolConfig).where(ToolConfig.user_id == user_id) + + if tool_type_id: + query = query.where(ToolConfig.tool_type_id == uuid.UUID(tool_type_id)) + if project_id: + query = query.where(ToolConfig.project_id == uuid.UUID(project_id)) + else: + # If no project specified, get only global configs (project_id is None) + query = query.where(ToolConfig.project_id.is_(None)) + + result = await session.execute(query) + configs = result.scalars().all() + + return [ + { + "id": str(c.id), + "tool_type_id": str(c.tool_type_id), + "project_id": str(c.project_id) if c.project_id else None, + "key": c.key, + "value": c.value, + "config_type": c.config_type, + "file_path": c.file_path, + "port_override": c.port_override, + "start_command": c.start_command, + "working_directory": c.working_directory, + "environment_variables": c.environment_variables, + "volumes": c.volumes, + } + for c in configs + ] + + +@router.post("", summary="Create tool config", description="Create a new tool config.", status_code=status.HTTP_201_CREATED) +async def create_config( + data: ToolConfigCreate, + user_id: uuid.UUID = Depends(get_current_user_id), + session: AsyncSession = Depends(get_db_session), +) -> dict: + """Create a tool config.""" + # Verify tool type exists + tool_type = await session.get(ToolType, uuid.UUID(data.tool_type_id)) + if tool_type is None: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="tool type not found") + + # Check for existing config with same key + query = select(ToolConfig).where( + ToolConfig.user_id == user_id, + ToolConfig.tool_type_id == uuid.UUID(data.tool_type_id), + ToolConfig.key == data.key, + ) + if data.project_id: + query = query.where(ToolConfig.project_id == uuid.UUID(data.project_id)) + else: + query = query.where(ToolConfig.project_id.is_(None)) + + existing = await session.scalar(query) + if existing: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, + detail=f"config with key '{data.key}' already exists" + ) + + config = ToolConfig( + user_id=user_id, + tool_type_id=uuid.UUID(data.tool_type_id), + project_id=uuid.UUID(data.project_id) if data.project_id else None, + key=data.key, + value=data.value, + config_type=data.config_type, + file_path=data.file_path, + port_override=data.port_override, + start_command=data.start_command, + working_directory=data.working_directory, + environment_variables=data.environment_variables, + volumes=data.volumes, + ) + session.add(config) + await session.commit() + await session.refresh(config) + + return { + "id": str(config.id), + "tool_type_id": str(config.tool_type_id), + "project_id": str(config.project_id) if config.project_id else None, + "key": config.key, + "value": config.value, + "config_type": config.config_type, + "file_path": config.file_path, + "port_override": config.port_override, + "start_command": config.start_command, + "working_directory": config.working_directory, + "environment_variables": config.environment_variables, + "volumes": config.volumes, + } + + +@router.put("/{config_id}", summary="Update tool config", description="Update an existing tool config.") +async def update_config( + config_id: uuid.UUID, + data: ToolConfigUpdate, + user_id: uuid.UUID = Depends(get_current_user_id), + session: AsyncSession = Depends(get_db_session), +) -> dict: + """Update a tool config.""" + config = await session.get(ToolConfig, config_id) + if config is None or config.user_id != user_id: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="config not found") + + if data.key is not None: + config.key = data.key + if data.value is not None: + config.value = data.value + if data.config_type is not None: + config.config_type = data.config_type + if data.file_path is not None: + config.file_path = data.file_path + if data.port_override is not None: + config.port_override = data.port_override + if data.start_command is not None: + config.start_command = data.start_command + if data.working_directory is not None: + config.working_directory = data.working_directory + if data.environment_variables is not None: + config.environment_variables = data.environment_variables + if data.volumes is not None: + config.volumes = data.volumes + + await session.commit() + await session.refresh(config) + + return { + "id": str(config.id), + "tool_type_id": str(config.tool_type_id), + "project_id": str(config.project_id) if config.project_id else None, + "key": config.key, + "value": config.value, + "config_type": config.config_type, + "file_path": config.file_path, + "port_override": config.port_override, + "start_command": config.start_command, + "working_directory": config.working_directory, + "environment_variables": config.environment_variables, + "volumes": config.volumes, + } + + +@router.get("/defaults/{tool_type_id}", summary="Get default configs", description="Get suggested default configs for a tool type.") +async def get_default_configs( + tool_type_id: str, + user_id: uuid.UUID = Depends(get_current_user_id), + session: AsyncSession = Depends(get_db_session), +) -> dict: + """Get suggested default configs for a tool type.""" + tool_type = await session.get(ToolType, uuid.UUID(tool_type_id)) + if tool_type is None: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="tool type not found") + + # Return suggested defaults based on required_variables + defaults = [] + for var in tool_type.required_variables: + defaults.append({ + "key": var, + "value": "", + "config_type": "env", + "description": f"Required variable: {var}", + }) + + return { + "tool_type_id": tool_type_id, + "suggested_configs": defaults, + } + + +@router.delete("/{config_id}", summary="Delete tool config", description="Delete a tool config.") +async def delete_config( + config_id: uuid.UUID, + user_id: uuid.UUID = Depends(get_current_user_id), + session: AsyncSession = Depends(get_db_session), +) -> None: + """Delete a tool config.""" + config = await session.get(ToolConfig, config_id) + if config is None or config.user_id != user_id: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="config not found") + + await session.delete(config) + await session.commit() diff --git a/apps/api/src/api/tool_instances.py b/apps/api/src/api/tool_instances.py index eca5437..965c8ba 100644 --- a/apps/api/src/api/tool_instances.py +++ b/apps/api/src/api/tool_instances.py @@ -1,1283 +1,115 @@ """Tool instance API endpoints.""" -import asyncio -import glob as glob_module import logging -import os -import subprocess import uuid -from datetime import datetime import httpx -from fastapi import ( - APIRouter, - APIRouter as FastAPIRouter, - Depends, - HTTPException, - Request, - Response, - status, -) -from pydantic import BaseModel, Field -from sqlalchemy import select +from fastapi import APIRouter, Depends, HTTPException, Request, Response, status from sqlalchemy.ext.asyncio import AsyncSession -from src.auth.dependencies import ( - _get_owned_project, - _get_user, - get_current_user_id, - get_db_session, -) -from src.services.event_bus import InstanceEventBus -from src.services.lifecycle_hooks import publish_lifecycle_event -from src.models.config_profile import ConfigProfile +from src.auth.dependencies import get_current_user, get_db_session, get_owned_project from src.models.git_repository import GitRepository from src.models.project import Project -from src.models.ssh_key import SSHKey from src.models.tool_instance import ToolInstance from src.models.tool_type import ToolType -from src.services.clone import check_dirty_state, clone_repository -from src.services.config_profile_resolver import ( - ConfigProfileCycleError, - ResolvedProfile, - apply_resolved_profile, - expand_container_path, - resolve_profile, -) -from src.services.docker import ( - connect_container_to_network, - ensure_instance_directory, - execute_compose_command, - find_free_port, - get_backend_network_name, - get_container_id, - get_container_ip_on_network, - get_container_logs, - get_container_status, - is_container_on_network, - render_compose_template, - sort_volumes_by_specificity, - wait_for_container_running, - write_compose_file, - write_config_files, - write_env_file, -) -from src.services.tunnel import ( - check_tunnel_health, - recreate_tunnel, - start_tunnel, - stop_tunnel, -) -from src.services.docker_build import build_image -from src.services.manifest_compiler import ( - compile_compose, - compile_dockerfile, - compile_entrypoint, - compute_image_tag, - deep_merge, - get_manifest_home_dir, - merge_with_config, - resolve_base, -) -from src.services.permission_fixer import apply_mount_permissions, apply_ssh_permissions -from src.services.readiness_probe import execute_probe -from src.services.ssh_keys import cleanup_ssh_key_files, prepare_ssh_key_files +from src.models.user import User +from src.schemas.tool_instance import CreateInstanceRequest +from src.services import instance_lifecycle as lifecycle +from src.services.docker import container as container_svc +from src.services.docker import tunnel as tunnel_svc logger = logging.getLogger(__name__) -_event_bus = InstanceEventBus() - - -async def _resolve_git_mounts( - session: AsyncSession, - resolved: ResolvedProfile, - instance_dir: str | None = None, - working_directory: str | None = None, - home_dir: str = "/root", -) -> list[dict]: - """Convert git mounts from resolved profile to Docker volume mounts. - - Looks up repository paths, auto-clones if needed, handles branch checkout, - expands glob patterns, and prepares bind mount entries. - Logs warnings for missing repos or invalid paths (non-blocking). - """ - if not resolved.git_mounts: - return [] - - # Process all git mounts concurrently - tasks = [] - for git_mount in resolved.git_mounts: - tasks.append( - _resolve_single_git_mount( - session, git_mount, instance_dir, working_directory, home_dir - ) - ) - - results = await asyncio.gather(*tasks, return_exceptions=True) - - volume_mounts = [] - for result in results: - if isinstance(result, Exception): - logger.warning("Git mount failed: %s", result) - continue - if result: - volume_mounts.extend(result) - - return volume_mounts - - -def _normalize_git_mount(entry: dict) -> dict: - """Normalize a git mount entry to the unified mappings format. - - Converts legacy source_path + target_path into a single-entry mappings array. - """ - entry = dict(entry) - if "mappings" not in entry or not entry.get("mappings"): - source = entry.get("source_path", ".") - target = entry.get("target_path") - if target is not None: - entry["mappings"] = [{"source_path": source, "target_path": target}] - entry.pop("source_path", None) - entry.pop("target_path", None) - return entry - - -def _clone_git_repo( - remote_url: str, - branch: str | None, - clone_parent: str, -) -> str: - """Clone or pull a git repository. - - Returns the path to the cloned repo (repo-clone directory). - """ - import hashlib - - url_hash = hashlib.md5(remote_url.encode()).hexdigest()[:12] - repo_name = remote_url.split("/")[-1].replace(".git", "") or "repo" - clone_dir = os.path.join(clone_parent, "git-mounts", f"{repo_name}-{url_hash}") - repo_path = os.path.join(clone_dir, "repo-clone") - - if not os.path.exists(repo_path): - try: - os.makedirs(clone_dir, exist_ok=True) - repo_path = clone_repository( - remote_url, - None, # No SSH key for now - can be added later - clone_dir, - branch or "main", - ) - logger.debug("Cloned git mount repository %s to %s", remote_url, repo_path) - except Exception as exc: - logger.warning("Clone failed for git mount %s: %s", remote_url, exc) - raise - else: - # Repo exists - pull latest updates - try: - _pull_repository_updates(repo_path, remote_url) - logger.debug("Pulled updates for git mount %s", remote_url) - except Exception as exc: - logger.warning("Failed to pull updates for %s: %s", remote_url, exc) - - # Handle branch checkout if specified - if branch and repo_path: - success = _checkout_branch(repo_path, branch) - if success: - logger.debug("Checked out branch %s for %s", branch, remote_url) - else: - logger.warning( - "Branch %s not found in %s, using current branch", branch, remote_url - ) - - return repo_path - - -def _resolve_git_mount_mappings( - repo_path: str, - mappings: list[dict], - working_directory: str | None, - home_dir: str = "/root", -) -> list[dict]: - """Resolve mappings from an already-cloned repo to volume mount entries. - - Returns a flat list of volume mount dicts. - """ - volume_mounts = [] - - for mapping in mappings: - source_path = mapping.get("source_path", ".") - target_path = mapping.get("target_path") - - if not target_path: - logger.warning("Invalid mapping skipped: missing target_path") - continue - - # Expand ~ and $HOME in target path - target_path = expand_container_path(target_path, home_dir) - - # Resolve relative target paths against working directory - final_target = target_path - if not target_path.startswith("/"): - if not working_directory: - logger.warning( - "Git mount skipped: target_path '%s' is relative but no working_directory is configured. " - "Set working_directory in the tool config or use an absolute path.", - target_path, - ) - continue - final_target = os.path.join(working_directory, target_path) - - # Build source path and expand globs - if source_path and source_path != ".": - source_full = os.path.join(repo_path, source_path) - else: - source_full = repo_path - - # Expand glob patterns - matched_paths = _expand_glob_source(source_full, repo_path) - - if not matched_paths: - logger.warning( - "Git mount skipped: no files matched source path %s in repo", - source_path, - ) - continue - - for matched_path in matched_paths: - if not os.path.exists(matched_path): - continue - - # Determine target path for this match - if len(matched_paths) == 1: - # Single match: mount directly to target_path - mount_target = final_target - else: - # Multiple matches: append relative path to target - rel_path = os.path.relpath(matched_path, repo_path) - mount_target = os.path.join(final_target, rel_path) - - volume_mounts.append( - { - "source": matched_path, - "target": mount_target, - "type": "bind", - } - ) - logger.debug( - "Added git mount: %s -> %s", - matched_path, - mount_target, - ) - - return volume_mounts - - -async def _resolve_single_git_mount( - session: AsyncSession, - git_mount: dict, - instance_dir: str | None = None, - working_directory: str | None = None, - home_dir: str = "/root", -) -> list[dict]: - """Resolve a single git mount to volume mount entries. - - Clones directly from remote_url, no database lookup needed. - Returns a list of volume mounts (one for each matched file/directory). - """ - git_mount = _normalize_git_mount(git_mount) - remote_url = git_mount.get("remote_url") - branch = git_mount.get("branch") - mappings = git_mount.get("mappings", []) - - if not remote_url: - logger.warning("Invalid git mount skipped: missing remote_url") - return [] - - if not mappings: - logger.warning("Invalid git mount skipped: no mappings") - return [] - - if not instance_dir: - logger.warning("Git mount skipped: no instance_dir provided for cloning") - return [] - - # Clone or pull the repository - try: - repo_path = await asyncio.to_thread( - _clone_git_repo, remote_url, branch, instance_dir - ) - except Exception: - return [] - - # Resolve all mappings from the cloned repo - return _resolve_git_mount_mappings(repo_path, mappings, working_directory, home_dir) - - -def _checkout_branch(repo_path: str, branch: str) -> bool: - """Checkout a specific branch in a git repository. - - Returns True if checkout succeeded, False if it failed. - On failure, the repository remains on its current branch. - """ - import subprocess - - # First try to checkout existing branch - result = subprocess.run( - ["git", "-C", repo_path, "checkout", branch], - capture_output=True, - text=True, - ) - - if result.returncode != 0: - # Try fetching and checking out - subprocess.run( - ["git", "-C", repo_path, "fetch", "origin", branch], - capture_output=True, - text=True, - ) - result = subprocess.run( - ["git", "-C", repo_path, "checkout", "-b", branch, f"origin/{branch}"], - capture_output=True, - text=True, - ) - - if result.returncode != 0: - logger.warning( - "Failed to checkout branch %s in %s: %s", - branch, - repo_path, - result.stderr.strip(), - ) - return False - - return True - - -def _pull_repository_updates(repo_path: str, remote_url: str) -> None: - """Pull latest updates from remote repository. - - Used when starting a new container with an existing cloned repository - to ensure the latest code is mounted. - """ - import subprocess - - # Fetch latest changes - result = subprocess.run( - ["git", "-C", repo_path, "fetch", "origin"], - capture_output=True, - text=True, - ) - - if result.returncode != 0: - raise RuntimeError(f"Failed to fetch updates: {result.stderr}") - - # Pull changes for current branch - result = subprocess.run( - ["git", "-C", repo_path, "pull", "origin"], - capture_output=True, - text=True, - ) - - if result.returncode != 0: - raise RuntimeError(f"Failed to pull updates: {result.stderr}") - - -def _expand_glob_source(source_path: str, repo_path: str) -> list[str]: - """Expand glob patterns in source path. - - Returns a list of matched absolute paths. - Limits results to prevent abuse. - """ - MAX_GLOB_MATCHES = 100 - - # Check if path contains glob characters - if not any(c in source_path for c in "*?["): - # No glob pattern: return single path if it exists - return [source_path] if os.path.exists(source_path) else [] - - # Expand glob pattern - matched = glob_module.glob(source_path, recursive=True) - total_matched = len(matched) - - # Filter to only paths within the repo and limit count - results = [] - for path in matched: - abs_path = os.path.abspath(path) - if abs_path.startswith(os.path.abspath(repo_path)): - results.append(abs_path) - if len(results) >= MAX_GLOB_MATCHES: - logger.warning( - "Glob pattern matched %d files, limited to %d", - total_matched, - MAX_GLOB_MATCHES, - ) - break - - return results - - router = APIRouter(prefix="/projects", tags=["tool-instances"]) - - -class CreateInstanceRequest(BaseModel): - """Request body for creating a tool instance.""" - - model_config = {"extra": "ignore"} - - tool_type_id: str = Field(description="UUID of the tool type to instantiate") - display_name: str | None = Field( - default=None, description="Optional display name for the instance" - ) - workspace_id: str | None = Field( - default=None, description="UUID of workspace to mount (replaces clone_mode)" - ) - clone_mode: str = Field( - default="mount", description="Repository access mode: 'mount' or 'clone'" - ) - branch: str | None = Field( - default="main", description="Branch to clone (when clone_mode='clone')" - ) - new_branch: str | None = Field( - default=None, description="Create a new local branch after cloning" - ) - config_profile_id: str | None = Field( - default=None, description="Optional config profile ID for launch" - ) - ssh_key_ids: list[str] = Field( - default_factory=list, description="SSH key IDs to mount into container ~/.ssh" - ) - - -class StartInstanceRequest(BaseModel): - """Request body for starting a tool instance.""" - - model_config = {"extra": "ignore"} - - config_profile_id: str | None = Field( - default=None, description="Config profile ID to apply, or null for none" - ) - ssh_key_ids: list[str] = Field( - default_factory=list, description="SSH key IDs to mount into container ~/.ssh" - ) - - -async def _validate_config_profile( - session: AsyncSession, - profile_id: str | None, - user_id: uuid.UUID, - project_id: uuid.UUID, - tool_type_id: uuid.UUID, -) -> uuid.UUID | None: - """Validate a config profile selection. - - Args: - session: Database session. - profile_id: Profile ID string or None. - user_id: Authenticated user ID. - project_id: Project ID for compatibility check. - tool_type_id: Tool type ID for compatibility check. - - Returns: - Validated UUID or None. - - Raises: - HTTPException: If profile is not found, not owned, or incompatible. - """ - if not profile_id: - return None - - try: - profile_uuid = uuid.UUID(profile_id) - except ValueError: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail=f"Invalid config profile ID: {profile_id}", - ) - - profile = await session.get(ConfigProfile, profile_uuid) - if profile is None: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail=f"Config profile not found: {profile_id}", - ) - - if profile.user_id != user_id: - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, - detail="Not authorized to use this config profile", - ) - - # Check compatibility: profile must be portable or match project/tool - is_compatible = ( - (profile.project_id is None and profile.tool_type_id is None) - or (profile.project_id == project_id) - or (profile.tool_type_id == tool_type_id) - or (profile.project_id == project_id and profile.tool_type_id == tool_type_id) - ) - - if not is_compatible: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="Selected config profile is not compatible with this project and tool type", - ) - - return profile_uuid - - -def _sanitize_compose_file(compose_path: str) -> None: - """Remove invalid port mappings (target port 0) from compose file.""" - import yaml - from pathlib import Path - - compose_file = Path(compose_path) - if not compose_file.exists(): - return - - content = compose_file.read_text() - compose_data = yaml.safe_load(content) - - if not compose_data or "services" not in compose_data: - return - - modified = False - for service_name, service_config in compose_data["services"].items(): - if "ports" in service_config: - valid_ports = [] - for port_mapping in service_config["ports"]: - if isinstance(port_mapping, str) and ":" in port_mapping: - parts = port_mapping.split(":") - if len(parts) == 2: - host_port, container_port = parts - # Skip invalid mappings (target port 0 or empty) - if container_port == "0" or not container_port: - modified = True - continue - valid_ports.append(port_mapping) - - if valid_ports: - service_config["ports"] = valid_ports - else: - del service_config["ports"] - modified = True - break # Only check first service - - if modified: - compose_file.write_text(yaml.dump(compose_data, default_flow_style=False)) - - -def _modify_compose_file( - compose_path: str, - port_override: int | None = None, - start_command: str | None = None, - working_directory: str | None = None, - extra_volumes: list[dict] | None = None, - home_dir: str = "/root", -) -> None: - """Modify compose file with runtime overrides.""" - import yaml - from pathlib import Path - - compose_file = Path(compose_path) - content = compose_file.read_text() - compose_data = yaml.safe_load(content) - - if not compose_data or "services" not in compose_data: - return - - # Apply modifications to the first service - for service_name, service_config in compose_data["services"].items(): - if port_override and "ports" in service_config: - # Update port mapping - for i, port_mapping in enumerate(service_config["ports"]): - if isinstance(port_mapping, str) and ":" in port_mapping: - host_port, container_port = port_mapping.split(":", 1) - service_config["ports"][i] = f"{port_override}:{container_port}" - break - - if start_command: - service_config["command"] = start_command - - if working_directory: - service_config["working_dir"] = expand_container_path( - working_directory, home_dir - ) - - if extra_volumes: - if "volumes" not in service_config: - service_config["volumes"] = [] - for vol in extra_volumes: - source = vol.get("source", "") - target = expand_container_path(vol.get("target", ""), home_dir) - vol_type = vol.get("type", "bind") - if vol_type == "bind": - service_config["volumes"].append(f"{source}:{target}") - else: - service_config["volumes"].append(f"{source}:{target}:{vol_type}") - - # Sort volumes so parent paths come before child paths - if service_config.get("volumes"): - service_config["volumes"] = sort_volumes_by_specificity( - service_config["volumes"] - ) - - break # Only modify the first service - - # Write back - compose_file.write_text(yaml.dump(compose_data, default_flow_style=False)) - - -def _ensure_container_name_in_compose(compose_path: str, container_name: str) -> None: - """Ensure compose file has explicit container_name for predictable naming. - - Docker Compose auto-generates container names from the project directory - when container_name is absent. This breaks tunnel connectivity because - get_container_name(instance.name) cannot find the container. We inject - container_name into every service so the container has a predictable name. - """ - import yaml - from pathlib import Path - - compose_file = Path(compose_path) - if not compose_file.exists(): - return - - content = compose_file.read_text() - compose_data = yaml.safe_load(content) - - if not compose_data or "services" not in compose_data: - return - - modified = False - for svc_name, svc_config in compose_data["services"].items(): - if "container_name" not in svc_config: - svc_config["container_name"] = container_name.lower() - modified = True - - if modified: - compose_file.write_text(yaml.dump(compose_data, default_flow_style=False)) - logger.info( - "Injected container_name '%s' into compose file", - container_name.lower(), - ) - - -def _ensure_web_bind_address( - compose_path: str, tool_type_name: str, default_port: int -) -> None: - """Auto-inject bind address for known web tools that default to 127.0.0.1. - - Many web tools (code-server, jupyter) bind to localhost by default, - making them inaccessible from the Docker network. This function detects - known tool images and injects the correct --bind-addr or --ip flag. - """ - import yaml - from pathlib import Path - - if default_port <= 0: - return - - KNOWN_BIND_FIXES: dict[str, str] = { - "code-server": f"--bind-addr 0.0.0.0:{default_port}", - "jupyter-notebook": f"start-notebook.sh --ip=0.0.0.0 --port={default_port} --no-browser", - } - - bind_command = KNOWN_BIND_FIXES.get(tool_type_name) - if not bind_command: - return - - compose_file = Path(compose_path) - if not compose_file.exists(): - return - - content = compose_file.read_text() - compose_data = yaml.safe_load(content) - - if not compose_data or "services" not in compose_data: - return - - for service_config in compose_data["services"].values(): - image = service_config.get("image", "") - if not image: - continue - - # LSIO images already bind to 0.0.0.0 — command override breaks s6 init - if "linuxserver" in image: - existing_command = service_config.get("command", "") - if "--bind-addr" in existing_command or "--host" in existing_command: - del service_config["command"] - compose_file.write_text( - yaml.dump(compose_data, default_flow_style=False) - ) - logger.warning( - "Removed broken command override from LSIO image: %s", - existing_command, - ) - return - return - - # Check if the image matches a known tool - is_code_server = tool_type_name == "code-server" and ( - "code-server" in image or "coder" in image - ) - is_jupyter = tool_type_name == "jupyter-notebook" and ( - "jupyter" in image or "notebook" in image - ) - if not is_code_server and not is_jupyter: - continue - - existing_command = service_config.get("command", "") - if existing_command: - # Already correct — nothing to do - if bind_command in existing_command: - return - # Fix broken or outdated bind flags - if ( - "--bind-addr" in existing_command - or "--host" in existing_command - or "--ip=" in existing_command - ): - service_config["command"] = bind_command - compose_file.write_text( - yaml.dump(compose_data, default_flow_style=False) - ) - logger.warning( - "Replaced broken bind address for %s: %s → %s", - tool_type_name, - existing_command, - bind_command, - ) - return - # Some other command override exists — don't touch it - return - - # No command yet — inject the correct bind address - service_config["command"] = bind_command - compose_file.write_text(yaml.dump(compose_data, default_flow_style=False)) - logger.info("Injected bind address for %s: %s", tool_type_name, bind_command) - return - - -def _ensure_backend_network_in_compose(compose_path: str) -> None: - """Inject the backend network into the compose file so compose up attaches it. - - Instead of running 'docker network connect' after container creation (which - is prone to race conditions and silent failures), we declare the network in - the compose file itself. Docker Compose then connects the container to the - network atomically during 'docker compose up'. - """ - import yaml - from pathlib import Path - - compose_file = Path(compose_path) - if not compose_file.exists(): - return - - content = compose_file.read_text() - compose_data = yaml.safe_load(content) - - if not compose_data or "services" not in compose_data: - return - - network_name = get_backend_network_name() - modified = False - - for svc_config in compose_data["services"].values(): - existing = svc_config.get("networks", []) - if network_name not in existing: - svc_config["networks"] = existing + [network_name] - modified = True - break # Only modify first service - - # Declare the network as external at the top level - if "networks" not in compose_data: - compose_data["networks"] = {} - if network_name not in compose_data["networks"]: - compose_data["networks"][network_name] = {"external": True} - modified = True - - if modified: - compose_file.write_text(yaml.dump(compose_data, default_flow_style=False)) - logger.info("Injected backend network '%s' into compose file", network_name) - - -@router.post( - "/{project_id}/repositories/{repo_id}/instances", - summary="Create tool instance", - description="Create a new tool instance for a repository.", -) +async def _get_instance(session: AsyncSession, instance_id: uuid.UUID, repo_id: uuid.UUID) -> ToolInstance: + instance = await session.get(ToolInstance, instance_id) + if instance is None or instance.repository_id != repo_id: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="instance not found") + return instance +async def _get_repo(session: AsyncSession, repo_id: uuid.UUID, project_id: uuid.UUID) -> GitRepository: + repo = await session.get(GitRepository, repo_id) + if repo is None or repo.project_id != project_id: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="repository not found") + return repo +# ── Endpoints ────────────────────────────────────────────────────────────── + +@router.post("/{project_id}/repositories/{repo_id}/instances") async def create_instance( project_id: uuid.UUID, repo_id: uuid.UUID, data: CreateInstanceRequest, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), session: AsyncSession = Depends(get_db_session), ) -> dict: - """Create a new tool instance for a repository. - - Args: - project_id: UUID of the project. - repo_id: UUID of the repository. - tool_type_id: UUID of the tool type to instantiate. - display_name: Optional display name for the instance. - user_id: ID of the authenticated user. - session: Database session. - - Returns: - Dictionary with instance details. - """ - logger.debug( - "Creating instance: project_id=%s, repo_id=%s, tool_type_id=%s, display_name=%s", - project_id, - repo_id, - data.tool_type_id, - data.display_name, - ) - _user = await _get_user(session, user_id) - _project = await _get_owned_project(project_id, user_id, session) - - repo = await session.get(GitRepository, repo_id) - if repo is None or repo.project_id != project_id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found" - ) - - tool_type_id = uuid.UUID(data.tool_type_id) - tool_type = await session.get(ToolType, tool_type_id) + """Create a new tool instance.""" + repo = await _get_repo(session, repo_id, project_id) + tool_type = await session.get(ToolType, uuid.UUID(data.tool_type_id)) if tool_type is None: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="tool type not found" - ) + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="tool type not found") - # Validate config profile if provided - selected_profile_id = await _validate_config_profile( - session, data.config_profile_id, user_id, project_id, tool_type_id + selected_profile = None + if data.config_profile_id: + from src.models.config_profile import ConfigProfile + selected_profile = await session.get(ConfigProfile, uuid.UUID(data.config_profile_id)) + if selected_profile is None: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="config profile not found") + if selected_profile.user_id != user.id: + raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="config profile does not belong to user") + + instance = await lifecycle.create_new_instance( + session, project, repo, tool_type, user, data.display_name, selected_profile ) - - # Resolve workspace if provided - workspace = None - workspace_id = None - if data.workspace_id: - from src.models.workspace import Workspace as WorkspaceModel - - try: - workspace_id = uuid.UUID(data.workspace_id) - except ValueError: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="Invalid workspace_id format", - ) - workspace = await session.get(WorkspaceModel, workspace_id) - if workspace is None: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail="workspace not found", - ) - if workspace.repo_id != repo_id: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="workspace does not belong to this repository", - ) - - try: - # Validate clone mode requirements (legacy path) - if data.clone_mode == "clone" and not workspace: - if not repo.remote_url: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="repository does not have a remote URL for cloning", - ) - if not repo.ssh_key_id: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="repository must have an SSH key assigned for clone mode", - ) - - # Generate unique name - instance_name = f"{tool_type.name}-{repo.name}-{uuid.uuid4().hex[:8]}" - instance_display = ( - data.display_name or f"{tool_type.display_name} - {repo.name}" - ) - - # Create instance directory - instance_dir = ensure_instance_directory(instance_name) - compose_path = os.path.join(instance_dir, "docker-compose.yml") - - # Find free port - tool_port = find_free_port() - - # Determine repo path based on workspace or clone mode - if workspace: - repo_path = workspace.path - elif data.clone_mode == "clone": - # Get SSH key for cloning - ssh_key = await session.get(SSHKey, repo.ssh_key_id) - if ssh_key is None: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail="repository SSH key not found", - ) - - # Prepare SSH key for clone operation - ssh_key_path = None - try: - ssh_dir = prepare_ssh_key_files(instance_dir, ssh_key) - ssh_key_path = os.path.join(ssh_dir, "id_ed25519") - - # Clone repository - clone_path = clone_repository( - remote_url=repo.remote_url, - ssh_key_path=ssh_key_path, - instance_dir=instance_dir, - branch=data.branch or "main", - ) - repo_path = clone_path - except Exception as exc: - logger.exception("Failed to clone repository: %s", exc) - cleanup_ssh_key_files(instance_dir) - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Failed to clone repository: {exc}", - ) - else: - repo_path = repo.path - - # Verify cloned repo has files - if data.clone_mode == "clone" and repo_path: - try: - repo_contents = os.listdir(repo_path) - if not repo_contents or ( - len(repo_contents) == 1 and repo_contents[0] == ".git" - ): - logger.error("Cloned repository at %s appears empty", repo_path) - raise RuntimeError("Cloned repository is empty") - logger.debug( - "Verified cloned repo at %s has %d items", - repo_path, - len(repo_contents), - ) - except Exception as exc: - logger.exception("Failed to verify cloned repository: %s", exc) - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Cloned repository verification failed: {exc}", - ) - - # Create new local branch if requested - if data.clone_mode == "clone" and data.new_branch: - try: - result = subprocess.run( - ["git", "-C", repo_path, "checkout", "-b", data.new_branch], - capture_output=True, - text=True, - ) - if result.returncode != 0: - logger.error( - "Failed to create branch %s: %s", data.new_branch, result.stderr - ) - raise RuntimeError(f"Failed to create branch: {result.stderr}") - logger.debug( - "Created local branch %s in cloned repository", data.new_branch - ) - except Exception as exc: - logger.exception("Failed to create local branch: %s", exc) - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Failed to create local branch: {exc}", - ) - - # Handle based on definition type - if tool_type.definition_type == "dockerfile": - # Build image from Dockerfile - image_tag = f"headquarter/{instance_name}:latest".lower() - - if tool_type.dockerfile_template: - returncode, stdout, stderr = await asyncio.to_thread( - build_image, - instance_dir=instance_dir, - dockerfile=tool_type.dockerfile_template, - tag=image_tag, - build_context=tool_type.build_context, - ) - - if returncode != 0: - logger.error( - "Failed to build image for instance %s: %s", - instance_name, - stderr, - ) - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Failed to build Docker image: {stderr[:500]}", - ) - - logger.info( - "Successfully built image %s for instance %s", - image_tag, - instance_name, - ) - - # Generate compose for dockerfile-built image - # Only include ports if tool requires one (skip for terminal-only tools) - ports_section = ( - f""" ports: - - "{tool_port}:{tool_type.default_port}" -""" - if tool_type.default_port and tool_type.default_port > 0 - else "" - ) - - compose_content = f"""version: "3.8" -services: - app: - image: {image_tag} - container_name: {instance_name.lower()} - stdin_open: true - tty: true -{ports_section} volumes: - - {repo_path}:/workspace - restart: unless-stopped -""" - write_compose_file(instance_dir, compose_content) - - elif tool_type.definition_type == "manifest": - # Manifest-based: generate compose only; image built lazily on start - from src.models.tool_definition_manifest import ToolDefinitionManifest - - manifest_def = await session.get( - ToolDefinitionManifest, tool_type.manifest_id - ) - if not manifest_def: - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail="Manifest definition not found for this tool type", - ) - - manifest = dict(manifest_def.manifest) - if manifest_def.base_definition_id: - base_def = await session.get( - ToolDefinitionManifest, manifest_def.base_definition_id - ) - if base_def: - manifest = resolve_base( - deep_merge(dict(base_def.manifest), manifest) - ) - - image_tag = compute_image_tag(tool_type.name, manifest) - - variables = { - "IMAGE_TAG": image_tag, - "INSTANCE_NAME": instance_name.lower(), - "INSTANCE_DIR": instance_dir, - "REPO_PATH": repo_path, - "SSH_PATH": "", - "TOOL_PORT": tool_port, - "EXTRA_ENV": {}, - "EXTRA_VOLUMES": [], - } - compose_content = compile_compose(manifest, variables) - write_compose_file(instance_dir, compose_content) - - else: - # Render compose template (legacy) - variables = { - "REPO_PATH": repo_path, - "INSTANCE_NAME": instance_name, - "INSTANCE_ID": instance_name, - "TOOL_NAME": instance_name, - "TOOL_PORT": tool_port, - "USER_ID": str(user_id), - "PROJECT_ID": str(project_id), - } - compose_content = render_compose_template( - tool_type.compose_template, variables - ) - - # Safety check: for clone mode, ensure repo is mounted in compose file - if data.clone_mode == "clone" and repo_path: - import yaml - - compose_data = yaml.safe_load(compose_content) - repo_mounted = False - if compose_data and "services" in compose_data: - for svc in compose_data["services"].values(): - volumes = svc.get("volumes", []) - for vol in volumes: - vol_str = str(vol) - if repo_path in vol_str: - repo_mounted = True - break - if repo_mounted: - break - - if not repo_mounted: - logger.warning( - "Compose template for tool type %s does not mount repo path; adding default mount", - tool_type.name, - ) - # Add default mount to first service - if compose_data and "services" in compose_data: - for svc in compose_data["services"].values(): - if "volumes" not in svc: - svc["volumes"] = [] - svc["volumes"].append(f"{repo_path}:/workspace") - break - compose_content = yaml.dump( - compose_data, default_flow_style=False - ) - - write_compose_file(instance_dir, compose_content) - - # Create database record - instance = ToolInstance( - name=instance_name, - display_name=instance_display, - tool_type_id=tool_type_id, - repository_id=repo_id, - project_id=project_id, - owner_id=user_id, - status="pending", - compose_path=compose_path, - port=tool_port, - workspace_id=workspace_id, - clone_mode=data.clone_mode, - branch=data.new_branch - if data.new_branch - else (data.branch if data.clone_mode == "clone" else None), - selected_config_profile_id=selected_profile_id, - ssh_key_ids=data.ssh_key_ids or None, - ) - session.add(instance) - await session.commit() - await session.refresh(instance) - await publish_lifecycle_event( - event_bus=_event_bus, - session=session, - instance=instance, - event_type="instance.created", - created_by=user_id, - status="pending", - message="Instance created", - ) - - return { - "id": str(instance.id), - "name": instance.name, - "display_name": instance.display_name, - "tool_type_id": str(instance.tool_type_id), - "status": instance.status, - "clone_mode": instance.clone_mode, - "branch": instance.branch, - "selected_config_profile_id": str(instance.selected_config_profile_id) - if instance.selected_config_profile_id - else None, - "created_at": instance.created_at.isoformat(), - } - except Exception as exc: - logger.exception("Failed to create instance: %s", exc) - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Failed to create instance: {exc}", - ) - - -@router.get( - "/{project_id}/repositories/{repo_id}/instances", - summary="List instances", - description="List all tool instances for a repository.", -) + return { + "id": str(instance.id), + "name": instance.name, + "display_name": instance.display_name, + "tool_type_id": str(instance.tool_type_id), + "status": instance.status, + "config_profile_id": str(instance.selected_profile_id) if instance.selected_profile_id else None, + "created_at": instance.created_at.isoformat(), + } +@router.get("/{project_id}/repositories/{repo_id}/instances") async def list_instances( project_id: uuid.UUID, repo_id: uuid.UUID, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), session: AsyncSession = Depends(get_db_session), ) -> dict: - """List all instances for a repository. - - Args: - project_id: UUID of the project. - repo_id: UUID of the repository. - user_id: ID of the authenticated user. - session: Database session. - - Returns: - Dictionary containing list of instances. - """ - _user = await _get_user(session, user_id) - _project = await _get_owned_project(project_id, user_id, session) - - repo = await session.get(GitRepository, repo_id) - if repo is None or repo.project_id != project_id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="repository not found" - ) - + """List all tool instances for a repository.""" + await _get_repo(session, repo_id, project_id) + from sqlalchemy import select result = await session.execute( select(ToolInstance) - .where(ToolInstance.repository_id == repo_id) - .where(ToolInstance.owner_id == user_id) + .where(ToolInstance.repository_id == repo_id, ToolInstance.owner_id == user.id) .order_by(ToolInstance.created_at.desc()) ) - instances = result.scalars().all() - - instances_data = [] - for i in instances: - tool_type = await session.get(ToolType, i.tool_type_id) - instances_data.append( - { - "id": str(i.id), - "name": i.name, - "display_name": i.display_name, - "tool_type_id": str(i.tool_type_id), - "tool_type_name": tool_type.name if tool_type else "unknown", - "tool_type_interfaces": [tool_type.interface_type] if tool_type else [], - "status": i.status, - "url": i.url, - "port": i.port, - "clone_mode": i.clone_mode, - "branch": i.branch, - "ssh_key_ids": i.ssh_key_ids or [], - "created_at": i.created_at.isoformat(), - } - ) - - return {"instances": instances_data} - - -@router.get( - "/{project_id}/repositories/{repo_id}/instances/{instance_id}", - summary="Get instance", - description="Get a specific instance with real-time status from Docker.", -) + instances = [] + for i in result.scalars().all(): + tt = await session.get(ToolType, i.tool_type_id) + instances.append({ + "id": str(i.id), "name": i.name, "display_name": i.display_name, + "tool_type_id": str(i.tool_type_id), "tool_type_name": tt.name if tt else "unknown", + "tool_type_interfaces": tt.interfaces if tt else [], + "status": i.status, "url": i.url, "port": i.port, + "config_profile_id": str(i.selected_profile_id) if i.selected_profile_id else None, + "created_at": i.created_at.isoformat(), + }) + return {"instances": instances} +@router.get("/{project_id}/repositories/{repo_id}/instances/{instance_id}") async def get_instance( project_id: uuid.UUID, repo_id: uuid.UUID, instance_id: uuid.UUID, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), session: AsyncSession = Depends(get_db_session), ) -> dict: - """Get a specific instance with real-time status. - - Args: - project_id: UUID of the project. - repo_id: UUID of the repository. - instance_id: UUID of the instance. - user_id: ID of the authenticated user. - session: Database session. - - Returns: - Dictionary with instance details and current status. - """ - _user = await _get_user(session, user_id) - _project = await _get_owned_project(project_id, user_id, session) - - instance = await session.get(ToolInstance, instance_id) - if instance is None or instance.repository_id != repo_id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="instance not found" - ) - - # Get real-time status from Docker + """Get a specific instance with real-time Docker status.""" + from datetime import datetime + instance = await _get_instance(session, instance_id, repo_id) if instance.container_id: - docker_status = get_container_status(instance.container_id) + docker_status = container_svc.get_container_status(instance.container_id) if docker_status == "running" and instance.status != "running": instance.status = "running" await session.commit() @@ -1285,1544 +117,128 @@ async def get_instance( instance.status = "stopped" instance.last_stopped_at = datetime.now() await session.commit() - return { - "id": str(instance.id), - "name": instance.name, - "display_name": instance.display_name, - "tool_type_id": str(instance.tool_type_id), - "status": instance.status, - "container_id": instance.container_id, - "compose_path": instance.compose_path, - "url": instance.url, - "port": instance.port, - "clone_mode": instance.clone_mode, - "branch": instance.branch, - "selected_config_profile_id": str(instance.selected_config_profile_id) - if instance.selected_config_profile_id - else None, - "last_started_at": instance.last_started_at.isoformat() - if instance.last_started_at - else None, - "last_stopped_at": instance.last_stopped_at.isoformat() - if instance.last_stopped_at - else None, + "id": str(instance.id), "name": instance.name, "display_name": instance.display_name, + "tool_type_id": str(instance.tool_type_id), "status": instance.status, + "container_id": instance.container_id, "compose_path": instance.compose_path, + "url": instance.url, "port": instance.port, + "config_profile_id": str(instance.selected_profile_id) if instance.selected_profile_id else None, + "last_started_at": instance.last_started_at.isoformat() if instance.last_started_at else None, + "last_stopped_at": instance.last_stopped_at.isoformat() if instance.last_stopped_at else None, "created_at": instance.created_at.isoformat(), } - - -async def _prepare_manifest_instance( - session: AsyncSession, - instance: ToolInstance, - instance_dir: str, - repo_path: str, - env_vars: dict, - extra_volumes: list, - working_directory: str | None, -) -> tuple[str, str, dict, str]: - """Build image and generate compose from a manifest-based tool type. - - Returns: - Tuple of (image_tag, compose_content, resolved_manifest, home_dir) - """ - from src.models.tool_definition_manifest import ToolDefinitionManifest - - tool_type = await session.get(ToolType, instance.tool_type_id) - manifest_def = await session.get(ToolDefinitionManifest, tool_type.manifest_id) - - if not manifest_def: - raise RuntimeError(f"Manifest not found for tool type {tool_type.id}") - - manifest = dict(manifest_def.manifest) - - # Resolve base if referenced - if manifest_def.base_definition_id: - base_def = await session.get( - ToolDefinitionManifest, manifest_def.base_definition_id - ) - if base_def: - base_manifest = dict(base_def.manifest) - manifest = resolve_base(deep_merge(base_manifest, manifest)) - else: - logger.warning( - "Base definition %s not found for manifest %s", - manifest_def.base_definition_id, - manifest_def.id, - ) - - manifest = merge_with_config(manifest) - - # Resolve extra env and volumes from merge_with_config - extra_env = manifest.pop("_extra_env", {}) - extra_cfg_volumes = manifest.pop("_extra_volumes", []) - env_vars.update(extra_env) - extra_volumes.extend(extra_cfg_volumes) - - # Compute image tag - image_tag = compute_image_tag(tool_type.name, manifest) - - # Check if image already exists - check = subprocess.run( - ["docker", "images", "-q", image_tag], - capture_output=True, - text=True, - ) - image_exists = check.returncode == 0 and check.stdout.strip() - - if not image_exists: - # Compile and build - dockerfile = compile_dockerfile(manifest) - entrypoint = compile_entrypoint(manifest) - - logger.debug( - "Compiled Dockerfile for instance %s (%d chars)", - instance.id, - len(dockerfile), - ) - - build_ctx = { - "Dockerfile": dockerfile, - ".headquarter/entrypoint.sh": entrypoint, - } - - returncode, stdout, stderr = await asyncio.to_thread( - build_image, - instance_dir=instance_dir, - dockerfile=dockerfile, - tag=image_tag, - build_context=build_ctx, - ) - - if returncode != 0: - raise RuntimeError(f"Docker build failed: {stderr}") - - logger.info("Built image %s for instance %s", image_tag, instance.id) - else: - logger.info("Reusing existing image %s for instance %s", image_tag, instance.id) - - # Prepare SSH path for mount resolution - ssh_path = "" - if instance.clone_mode == "clone": - ssh_path = os.path.join(instance_dir, ".ssh") - - # Resolve git mount variables from config profile - git_mount_vars = {} - if instance.selected_config_profile_id: - resolved_profile = await resolve_profile( - session, instance.selected_config_profile_id - ) - for gm in resolved_profile.git_mounts or []: - ref = gm.get("git_mount_ref", "default") - # The actual resolution happens in _resolve_git_mounts; we store placeholder - git_mount_vars[f"GIT_MOUNT_{ref}"] = "" - - variables = { - "IMAGE_TAG": image_tag, - "INSTANCE_NAME": instance.name.lower(), - "INSTANCE_DIR": instance_dir, - "REPO_PATH": repo_path, - "SSH_PATH": ssh_path, - "TOOL_PORT": instance.port or 0, - "EXTRA_ENV": env_vars, - "EXTRA_VOLUMES": extra_volumes, - **git_mount_vars, - } - - compose_content = compile_compose(manifest, variables) - - logger.debug( - "_prepare_manifest_instance for %s: repo_path=%s compose_volumes=%s", - instance.id, - repo_path or "", - manifest.get("mounts", []), - ) - logger.debug( - "Generated compose for %s:\n%s", - instance.id, - compose_content, - ) - - # Cache - instance.image_tag = image_tag - instance.manifest_compiled_at = datetime.now() - - home_dir = get_manifest_home_dir(manifest) - return image_tag, compose_content, manifest, home_dir - - -@router.post( - "/{project_id}/repositories/{repo_id}/instances/{instance_id}/start", - summary="Start instance", - description="Start a tool instance using Docker Compose.", -) +@router.post("/{project_id}/repositories/{repo_id}/instances/{instance_id}/start") async def start_instance( project_id: uuid.UUID, repo_id: uuid.UUID, instance_id: uuid.UUID, - data: StartInstanceRequest | None = None, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), session: AsyncSession = Depends(get_db_session), ) -> dict: - """Start a tool instance. - - Args: - project_id: UUID of the project. - repo_id: UUID of the repository. - instance_id: UUID of the instance to start. - data: Optional start configuration including config profile selection. - user_id: ID of the authenticated user. - session: Database session. - - Returns: - Dictionary with status and URL of the running instance. - """ - _user = await _get_user(session, user_id) - _project = await _get_owned_project(project_id, user_id, session) - - instance = await session.get(ToolInstance, instance_id) - if instance is None or instance.repository_id != repo_id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="instance not found" - ) - - # Validate and store config profile selection - if data and data.config_profile_id is not None: - selected_profile_id = await _validate_config_profile( - session, data.config_profile_id, user_id, project_id, instance.tool_type_id - ) - instance.selected_config_profile_id = selected_profile_id - await session.commit() - - # Store SSH key selection if provided - if data and data.ssh_key_ids is not None: - instance.ssh_key_ids = data.ssh_key_ids or None - await session.commit() - - if not instance.compose_path or not os.path.exists(instance.compose_path): - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, detail="compose file not found" - ) - - instance.status = "building" - await session.commit() - logger.info("Starting instance %s (name=%s)", instance.id, instance.name) - - # Runtime overrides populated by config profiles - env_vars = {} - config_files = {} - port_override = None - start_command = None - working_directory = None - extra_volumes = [] - - # Fetch tool type early to determine home directory and container user - tool_type = await session.get(ToolType, instance.tool_type_id) - home_dir = "/root" - container_uid = 0 - container_gid = 0 - if tool_type and tool_type.definition_type == "manifest" and tool_type.manifest_id: - from src.models.tool_definition_manifest import ToolDefinitionManifest - - manifest_def = await session.get(ToolDefinitionManifest, tool_type.manifest_id) - if manifest_def: - manifest = dict(manifest_def.manifest) - # Merge with base definition if referenced (user config is often in base) - if manifest_def.base_definition_id: - base_def = await session.get( - ToolDefinitionManifest, manifest_def.base_definition_id - ) - if base_def: - manifest = resolve_base( - deep_merge(dict(base_def.manifest), manifest) - ) - home_dir = get_manifest_home_dir(manifest) - user_cfg = manifest.get("user") - if user_cfg: - container_uid = user_cfg.get("uid", 0) - container_gid = user_cfg.get("gid", 0) - logger.debug( - "Manifest user resolved for instance %s: uid=%s, gid=%s, home=%s", - instance.id, - container_uid, - container_gid, - home_dir, - ) - - # Apply selected config profile if any - instance_dir = os.path.dirname(instance.compose_path) - if instance.selected_config_profile_id is not None: - try: - resolved = await resolve_profile( - session, instance.selected_config_profile_id - ) - profile_env, profile_files, profile_mounts, profile_hints = ( - apply_resolved_profile(instance_dir, resolved, home_dir) - ) - # Profile env vars override tool config env vars - env_vars.update(profile_env) - # Profile files are written by apply_resolved_profile - config_files.update(profile_files) - # Profile mounts are added to extra volumes - extra_volumes.extend(profile_mounts) - # Git repository mounts are resolved and added - git_mount_volumes = await _resolve_git_mounts( - session, resolved, instance_dir, working_directory, home_dir - ) - extra_volumes.extend(git_mount_volumes) - # Profile runtime hints override tool config values - if profile_hints.get("start_command"): - start_command = profile_hints["start_command"] - if profile_hints.get("working_directory"): - working_directory = profile_hints["working_directory"] - if profile_hints.get("port_override"): - port_override = profile_hints["port_override"] - logger.debug( - "Applied config profile %s to instance %s (env=%d, files=%d, mounts=%d, git_mounts=%d)", - resolved.profile_name, - instance.id, - len(profile_env), - len(profile_files), - len(profile_mounts), - len(git_mount_volumes), - ) - except ConfigProfileCycleError as exc: - logger.error( - "Cycle detected in config profile for instance %s: %s", instance.id, exc - ) - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail=f"Config profile cycle detected: {exc}", - ) - else: - logger.debug("No config profile selected for instance %s", instance.id) - - # Write env file and config files - env_file_path = None - - if env_vars: - env_file_path = write_env_file(instance_dir, env_vars) - logger.debug("Wrote env file for instance %s: %s", instance.id, env_file_path) - - if config_files: - write_config_files(instance_dir, config_files) - logger.debug( - "Wrote %d config files for instance %s", len(config_files), instance.id - ) - - # Mount selected SSH keys into container home dir - if instance.ssh_key_ids: - from src.services.ssh_keys import write_ssh_config, _sanitize_filename - - # Collect all valid keys first - ssh_keys_to_mount = [] - for key_id in instance.ssh_key_ids: - ssh_key = await session.get(SSHKey, uuid.UUID(key_id)) - if ssh_key and ssh_key.user_id == user_id: - ssh_keys_to_mount.append(ssh_key) - else: - logger.warning( - "SSH key %s not found or not authorized for user %s", - key_id, - user_id, - ) - - if ssh_keys_to_mount: - # Use a single shared .ssh directory so all keys are visible - ssh_dir = os.path.join(instance_dir, "mounts", "ssh", ".ssh") - os.makedirs(ssh_dir, exist_ok=True) - - key_filenames = [] - for ssh_key in ssh_keys_to_mount: - # Use sanitized key name as filename prefix to avoid collisions - key_name = _sanitize_filename(ssh_key.name) - # If multiple keys have the same name, append a short hash - base_filename = f"id_ed25519_{key_name}" - filename = base_filename - counter = 1 - while filename in key_filenames: - filename = f"{base_filename}_{counter}" - counter += 1 - key_filenames.append(filename) - - try: - prepare_ssh_key_files( - instance_dir, - ssh_key, - subdir="mounts/ssh/.ssh", - uid=container_uid, - gid=container_gid, - key_filename=filename, - write_config=False, - ) - logger.debug( - "Prepared SSH key %s as %s for instance %s", - ssh_key.name, - filename, - instance.id, - ) - except Exception as exc: - logger.error( - "Failed to prepare SSH key %s for instance %s: %s", - ssh_key.id, - instance.id, - exc, - ) - - # Write combined SSH config with all keys - try: - write_ssh_config( - ssh_dir, - key_filenames, - uid=container_uid, - gid=container_gid, - ) - except Exception as exc: - logger.error( - "Failed to write SSH config for instance %s: %s", - instance.id, - exc, - ) - - # Mount the single .ssh directory into container home - ssh_target = os.path.join(home_dir, ".ssh") - extra_volumes.append( - { - "source": ssh_dir, - "target": ssh_target, - "type": "bind", - } - ) - logger.debug( - "Mounted %d SSH key(s) for instance %s to %s", - len(ssh_keys_to_mount), - instance.id, - ssh_target, - ) - - # ── MANIFEST-BASED FLOW ────────────────────────────────────── - resolved_manifest = None - - if tool_type and tool_type.definition_type == "manifest" and tool_type.manifest_id: - logger.info("Using manifest-based startup for instance %s", instance.id) - - # Determine repo path (workspace takes precedence) - repo_path = "" - if instance.workspace_id: - from src.models.workspace import Workspace as WorkspaceModel - - workspace = await session.get(WorkspaceModel, instance.workspace_id) - if workspace: - repo_path = workspace.path - else: - repo = await session.get(GitRepository, instance.repository_id) - repo_path = repo.path if repo else "" - if instance.clone_mode == "clone": - repo_path = os.path.join(instance_dir, "repo-clone") - - try: - ( - image_tag, - compose_content, - resolved_manifest, - _home_dir, - ) = await _prepare_manifest_instance( - session=session, - instance=instance, - instance_dir=instance_dir, - repo_path=repo_path, - env_vars=env_vars, - extra_volumes=extra_volumes, - working_directory=working_directory, - ) - write_compose_file(instance_dir, compose_content) - logger.debug( - "Generated manifest-based compose for instance %s", instance.id - ) - except Exception as exc: - logger.exception( - "Manifest compilation failed for instance %s: %s", instance.id, exc - ) - instance.status = "error" - await session.commit() - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Manifest compilation failed: {exc}", - ) - else: - # ── LEGACY FLOW ────────────────────────────────────────── - # Mount SSH key for clone-mode instances (skip for workspace-based) - if instance.clone_mode == "clone" and not instance.workspace_id: - repo = await session.get(GitRepository, instance.repository_id) - if repo and repo.ssh_key_id: - ssh_key = await session.get(SSHKey, repo.ssh_key_id) - if ssh_key: - try: - ssh_dir = prepare_ssh_key_files( - instance_dir, ssh_key, uid=0, gid=0 - ) - extra_volumes.append( - { - "source": ssh_dir, - "target": "/root/.ssh", - "type": "bind", - } - ) - logger.debug( - "Mounted SSH key for clone-mode instance %s", instance.id - ) - except Exception as exc: - logger.error( - "Failed to prepare SSH key for instance %s: %s", - instance.id, - exc, - ) - - # Modify compose file if needed (port override, start command, working dir, volumes) - if port_override or start_command or working_directory or extra_volumes: - _modify_compose_file( - instance.compose_path, - port_override, - start_command, - working_directory, - extra_volumes, - home_dir, - ) - logger.debug("Modified compose file for instance %s", instance.id) - - # Sanitize compose file to remove invalid port mappings from old instances - _sanitize_compose_file(instance.compose_path) - - # Auto-fix bind address for known web tools that default to localhost - if tool_type and tool_type.interface_type == "web": - _ensure_web_bind_address( - instance.compose_path, tool_type.name, tool_type.default_port - ) - - # Ensure predictable container name for tunnel connectivity - _ensure_container_name_in_compose(instance.compose_path, instance.name) - _ensure_backend_network_in_compose(instance.compose_path) - - # Execute docker compose up with env file - logger.debug( - "Running docker compose up for instance %s (compose_path=%s)", - instance.id, - instance.compose_path, - ) - returncode, stdout, stderr = execute_compose_command( - instance.compose_path, "up", env_file=env_file_path - ) - logger.debug( - "Docker compose up completed for instance %s: returncode=%d, stdout=%s, stderr=%s", - instance.id, - returncode, - stdout[:200] if stdout else "", - stderr[:500] if stderr else "", - ) - - if returncode != 0: - instance.status = "error" - await session.commit() - logger.error("Failed to start instance %s: %s", instance.id, stderr) - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"failed to start instance: {stderr}", - ) - - # Get container ID and name (use predictable name from compose) - expected_container_name = instance.name.lower() - container_id = get_container_id(expected_container_name) - if container_id: - instance.container_id = container_id - logger.debug("Container ID for instance %s: %s", instance.id, container_id) - - instance.container_name = expected_container_name - logger.debug( - "Container name for instance %s: %s", instance.id, expected_container_name - ) - - # Verify container reached running state - if instance.container_id: - instance.status = "starting" - instance.last_started_at = datetime.now() - await session.commit() - await publish_lifecycle_event( - event_bus=_event_bus, - session=session, - instance=instance, - event_type="instance.started", - created_by=user_id, - status="starting", - message="Container starting...", - ) - logger.debug("Instance %s: verifying container startup...", instance.id) - - startup_result = wait_for_container_running( - instance.container_id, timeout=30, interval=2.0 - ) - - if not startup_result["success"]: - # Container failed to start - error_msg = f"Container failed to start: status={startup_result['status']}" - if startup_result["exit_code"] is not None: - error_msg += f", exit_code={startup_result['exit_code']}" - - # Get logs for debugging - logs = get_container_logs(instance.container_id, tail=50) - - instance.status = "error" - await session.commit() - await publish_lifecycle_event( - event_bus=_event_bus, - session=session, - instance=instance, - event_type="instance.error", - created_by=user_id, - status="error", - message=error_msg, - metadata={ - "exit_code": startup_result["exit_code"], - "error_type": "container", - }, - ) - logger.error( - "Instance %s container startup failed after %.1fs: %s\nLogs:\n%s", - instance.id, - startup_result["waited_seconds"], - error_msg, - logs, - ) - return { - "status": "error", - "error": error_msg, - "logs": logs, - } - - logger.debug( - "Instance %s container started successfully after %.1fs", - instance.id, - startup_result["waited_seconds"], - ) - - # Apply mount permission fixes for manifest-based instances - if resolved_manifest and instance.container_id: - mounts = resolved_manifest.get("mounts", []) - if mounts: - logger.debug( - "Applying permission fixes for instance %s (%d mounts)", - instance.id, - len(mounts), - ) - permission_results = apply_mount_permissions( - instance.container_id, - mounts, - ) - for result in permission_results: - if not result["success"]: - logger.warning( - "Permission fix failed for mount %s on instance %s: %s", - result["mount_name"], - instance.id, - result["error"], - ) - - # Fix SSH key ownership/permissions inside the container - if instance.ssh_key_ids and instance.container_id: - container_user = ( - "root" - if home_dir == "/root" - else home_dir[6:] - if home_dir.startswith("/home/") - else "root" - ) - ssh_target = os.path.join(home_dir, ".ssh") - logger.debug( - "Applying SSH permissions for user %s on %s in instance %s", - container_user, - ssh_target, - instance.id, - ) - ssh_perm_result = apply_ssh_permissions( - instance.container_id, - ssh_target, - container_user, - ) - if not ssh_perm_result["success"]: - logger.warning( - "SSH permission fix failed for instance %s: %s", - instance.id, - ssh_perm_result["error"], - ) - - # Execute readiness probe if configured - tool_type = await session.get(ToolType, instance.tool_type_id) - if tool_type and instance.container_id: - # Determine probe command - probe_command = None - probe_timeout = 30 - probe_interval = 2 - - if tool_type.readiness_probe: - probe_config = tool_type.readiness_probe - probe_command = probe_config.get("command", "") - probe_timeout = probe_config.get("timeout", 30) - probe_interval = probe_config.get("interval", 2) - elif tool_type.interface_type == "web": - # Default probe for web tools - probe_command = f"curl -f http://localhost:{tool_type.default_port or 8080}" - probe_timeout = 30 - probe_interval = 2 - - if probe_command: - instance.status = "probing" - await session.commit() - logger.debug( - "Executing readiness probe for instance %s: command='%s', timeout=%d, interval=%d", - instance.id, - probe_command, - probe_timeout, - probe_interval, - ) - - success, probe_logs = await execute_probe( - container_id=instance.container_id, - command=probe_command, - timeout=probe_timeout, - interval=probe_interval, - ) - - # Store probe result - instance.probe_result = { - "success": success, - "command": probe_command, - "logs": probe_logs, - "timestamp": datetime.now().isoformat(), - } - - if not success: - instance.status = "unhealthy" - await session.commit() - await publish_lifecycle_event( - event_bus=_event_bus, - session=session, - instance=instance, - event_type="instance.health_changed", - created_by=user_id, - status="unhealthy", - message="Readiness probe failed", - metadata={"probe_output": "\n".join(probe_logs)}, - ) - logger.error( - "Readiness probe failed for instance %s after %ds: %s", - instance.id, - probe_timeout, - "\n".join(probe_logs), - ) - return { - "status": "unhealthy", - "error": f"Readiness probe failed after {probe_timeout}s", - "probe_logs": probe_logs, - } - - logger.info("Readiness probe succeeded for instance %s", instance.id) - - instance.status = "running" - await session.commit() - await publish_lifecycle_event( - event_bus=_event_bus, - session=session, - instance=instance, - event_type="instance.health_changed", - created_by=user_id, - status="running", - message="Container running", - metadata={"previous_status": "starting"}, - ) - logger.info("Instance %s is now running", instance.id) - - # Get tool type for default port - tool_type = await session.get(ToolType, instance.tool_type_id) - if not tool_type: - logger.error("Tool type %s not found", instance.tool_type_id) - instance.status = "error" - await session.commit() - await publish_lifecycle_event( - event_bus=_event_bus, - session=session, - instance=instance, - event_type="instance.error", - created_by=user_id, - status="error", - message=f"Tool type '{instance.tool_type_id}' not found", - ) - return { - "status": "error", - "error": f"Tool type '{instance.tool_type_id}' not found", - } - - logger.debug( - "Tool type for instance %s: name=%s, container_port=%s, interface_type=%s", - instance.id, - tool_type.name, - tool_type.default_port or 0, - tool_type.interface_type, - ) - - # Only create Cloudflare tunnel for web-enabled tools - if tool_type.interface_type == "web": - # Create temporary Cloudflare tunnel for public access - try: - logger.debug( - "Creating tunnel for instance %s (container_port=%d)", - instance.id, - tool_type.default_port or 0, - ) - tunnel_info = start_tunnel( - instance_name=instance.name, - container_port=tool_type.default_port or 0, - ) - instance.tunnel_id = tunnel_info["container_name"] - instance.public_url = tunnel_info["url"] - instance.url = tunnel_info["url"] - await session.commit() - logger.debug( - "Created tunnel for instance %s: container=%s, url=%s", - instance.id, - tunnel_info["container_name"], - tunnel_info["url"], - ) - except Exception as exc: - import traceback - - error_msg = str(exc) - error_trace = traceback.format_exc() - logger.error( - "Failed to create tunnel for instance %s: %s\nTraceback:\n%s", - instance.id, - error_msg, - error_trace, - ) - instance.status = "error" - instance.url = None - await session.commit() - return { - "status": "error", - "error": f"Failed to create tunnel: {error_msg}", - } - else: - # Terminal-only tool - no tunnel needed - logger.info( - "Instance %s is terminal-only (no web interface), skipping tunnel creation", - instance.id, - ) - instance.url = None - instance.public_url = None - await session.commit() - - return {"status": instance.status, "url": instance.url} - - -@router.post( - "/{project_id}/repositories/{repo_id}/instances/{instance_id}/stop", - summary="Stop instance", - description="Stop a running tool instance.", -) + """Start a tool instance.""" + instance = await _get_instance(session, instance_id, repo_id) + return await lifecycle.start_existing_instance(session, instance, user, project_id) +@router.post("/{project_id}/repositories/{repo_id}/instances/{instance_id}/stop") async def stop_instance( project_id: uuid.UUID, repo_id: uuid.UUID, instance_id: uuid.UUID, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), session: AsyncSession = Depends(get_db_session), ) -> dict: - """Stop a tool instance. - - Args: - project_id: UUID of the project. - repo_id: UUID of the repository. - instance_id: UUID of the instance to stop. - user_id: ID of the authenticated user. - session: Database session. - - Returns: - Dictionary with the stopped status. - """ - _user = await _get_user(session, user_id) - _project = await _get_owned_project(project_id, user_id, session) - - instance = await session.get(ToolInstance, instance_id) - if instance is None or instance.repository_id != repo_id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="instance not found" - ) - - # Stop Cloudflare tunnel if exists - if instance.tunnel_id: - try: - stop_tunnel(instance.name) - logger.debug( - "Stopped tunnel for instance %s (container=%s)", - instance.id, - instance.tunnel_id, - ) - except Exception as exc: - logger.warning( - "Failed to stop tunnel for instance %s: %s", instance.id, exc - ) - - if instance.compose_path and os.path.exists(instance.compose_path): - execute_compose_command(instance.compose_path, "stop") - - instance.status = "stopped" - instance.last_stopped_at = datetime.now() - instance.url = None - instance.public_url = None - instance.tunnel_id = None - await session.commit() - await publish_lifecycle_event( - event_bus=_event_bus, - session=session, - instance=instance, - event_type="instance.stopped", - created_by=user_id, - status="stopped", - message="Instance stopped", - ) - + """Stop a running tool instance.""" + instance = await _get_instance(session, instance_id, repo_id) + await lifecycle.stop_existing_instance(session, instance) return {"status": instance.status} - - -@router.post( - "/{project_id}/repositories/{repo_id}/instances/{instance_id}/restart", - summary="Restart instance", - description="Restart a tool instance.", -) +@router.post("/{project_id}/repositories/{repo_id}/instances/{instance_id}/restart") async def restart_instance( project_id: uuid.UUID, repo_id: uuid.UUID, instance_id: uuid.UUID, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), session: AsyncSession = Depends(get_db_session), ) -> dict: - """Restart a tool instance. - - Args: - project_id: UUID of the project. - repo_id: UUID of the repository. - instance_id: UUID of the instance to restart. - user_id: ID of the authenticated user. - session: Database session. - - Returns: - Dictionary with status and URL of the restarted instance. - """ - _user = await _get_user(session, user_id) - _project = await _get_owned_project(project_id, user_id, session) - - instance = await session.get(ToolInstance, instance_id) - if instance is None or instance.repository_id != repo_id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="instance not found" - ) - - # Stop old tunnel if exists - if instance.tunnel_id: - try: - stop_tunnel(instance.name) - logger.debug( - "Stopped old tunnel for instance %s (container=%s)", - instance.id, - instance.tunnel_id, - ) - except Exception as exc: - logger.warning( - "Failed to stop old tunnel for instance %s: %s", instance.id, exc - ) - - # Re-apply stored config profile on restart - if instance.compose_path and os.path.exists(instance.compose_path): - instance_dir = os.path.dirname(instance.compose_path) - if instance.selected_config_profile_id is not None: - try: - resolved = await resolve_profile( - session, instance.selected_config_profile_id - ) - profile_env, profile_files, profile_mounts, profile_hints = ( - apply_resolved_profile(instance_dir, resolved) - ) - # Write env file with resolved profile env vars - if profile_env: - write_env_file(instance_dir, profile_env) - logger.debug( - "Re-applied config profile %s on restart for instance %s", - resolved.profile_name, - instance.id, - ) - except ConfigProfileCycleError as exc: - logger.error( - "Cycle detected in stored config profile for instance %s: %s", - instance.id, - exc, - ) - - # Re-apply compose fixes in case they were updated since last start - _sanitize_compose_file(instance.compose_path) - tool_type = await session.get(ToolType, instance.tool_type_id) - if tool_type and tool_type.interface_type == "web": - _ensure_web_bind_address( - instance.compose_path, tool_type.name, tool_type.default_port - ) - _ensure_container_name_in_compose(instance.compose_path, instance.name) - _ensure_backend_network_in_compose(instance.compose_path) - - returncode, stdout, stderr = execute_compose_command( - instance.compose_path, "restart" - ) - - if returncode == 0: - instance.status = "running" - instance.last_started_at = datetime.now() - - # Get tool type for default port - tool_type = await session.get(ToolType, instance.tool_type_id) - if not tool_type or not tool_type.default_port: - logger.error( - "Tool type %s has no default_port configured. Cannot create tunnel.", - instance.tool_type_id, - ) - instance.status = "error" - await session.commit() - return { - "status": "error", - "error": f"Tool type '{tool_type.name if tool_type else 'unknown'}' has no port configured", - } - - # Only create tunnel for web-enabled tools - if tool_type.interface_type == "web": - # Create new tunnel - try: - tunnel_info = start_tunnel( - instance_name=instance.name, - container_port=tool_type.default_port or 0, - ) - instance.tunnel_id = tunnel_info["container_name"] - instance.public_url = tunnel_info["url"] - instance.url = tunnel_info["url"] - logger.debug( - "Created new tunnel for instance %s: %s", - instance.id, - tunnel_info["url"], - ) - except Exception as exc: - logger.warning( - "Failed to create tunnel for instance %s: %s", - instance.id, - exc, - ) - instance.status = "error" - instance.url = None - await session.commit() - return { - "status": "error", - "error": f"Failed to create tunnel: {exc}", - } - else: - # Terminal-only tool - instance.url = None - instance.public_url = None - - await session.commit() - await publish_lifecycle_event( - event_bus=_event_bus, - session=session, - instance=instance, - event_type="instance.restarted", - created_by=user_id, - status="running", - message="Instance restarted", - ) - return {"status": instance.status, "url": instance.url} - - instance.status = "error" - await session.commit() - return {"status": instance.status} - - -@router.delete( - "/{project_id}/repositories/{repo_id}/instances/{instance_id}", - summary="Delete instance", - description="Delete a tool instance and remove its Docker containers and files.", -) + """Restart a tool instance.""" + instance = await _get_instance(session, instance_id, repo_id) + return await lifecycle.restart_existing_instance(session, instance, user, project_id) +@router.delete("/{project_id}/repositories/{repo_id}/instances/{instance_id}") async def delete_instance( project_id: uuid.UUID, repo_id: uuid.UUID, instance_id: uuid.UUID, - force: bool = False, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), session: AsyncSession = Depends(get_db_session), ) -> None: - """Delete a tool instance. - - Args: - project_id: UUID of the project. - repo_id: UUID of the repository. - instance_id: UUID of the instance to delete. - user_id: ID of the authenticated user. - session: Database session. - - Returns: - None with 204 status code. - """ - _user = await _get_user(session, user_id) - _project = await _get_owned_project(project_id, user_id, session) - - instance = await session.get(ToolInstance, instance_id) - if instance is None or instance.repository_id != repo_id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="instance not found" - ) - - # Check dirty state for clone-mode instances - if instance.clone_mode == "clone" and not force: - instance_dir = ( - os.path.dirname(instance.compose_path) if instance.compose_path else None - ) - if instance_dir: - clone_path = os.path.join(instance_dir, "repo-clone") - if os.path.exists(clone_path): - is_dirty, changed_files = check_dirty_state(clone_path) - if is_dirty: - raise HTTPException( - status_code=status.HTTP_409_CONFLICT, - detail={ - "message": "Repository has uncommitted changes", - "changed_files": changed_files, - "force_required": True, - }, - ) - - # Stop Cloudflare tunnel if exists - if instance.tunnel_id: - try: - stop_tunnel(instance.name) - logger.debug( - "Stopped tunnel for instance %s (container=%s)", - instance.id, - instance.tunnel_id, - ) - except Exception as exc: - logger.warning( - "Failed to stop tunnel for instance %s: %s", instance.id, exc - ) - - # Stop and remove container - if instance.compose_path and os.path.exists(instance.compose_path): - execute_compose_command(instance.compose_path, "down") - - # Remove instance directory (includes clone and SSH keys) - if instance.compose_path: - instance_dir = os.path.dirname(instance.compose_path) - if os.path.exists(instance_dir): - import shutil - - shutil.rmtree(instance_dir) - - await publish_lifecycle_event( - event_bus=_event_bus, - session=session, - instance=instance, - event_type="instance.deleted", - created_by=user_id, - status="deleted", - message="Instance deleted", - ) - await session.delete(instance) - await session.commit() - - -@router.get( - "/{project_id}/repositories/{repo_id}/instances/{instance_id}/logs", - summary="Get instance logs", - description="Get container logs for a tool instance.", -) + """Delete a tool instance.""" + instance = await _get_instance(session, instance_id, repo_id) + await lifecycle.delete_existing_instance(session, instance) +@router.get("/{project_id}/repositories/{repo_id}/instances/{instance_id}/logs") async def get_instance_logs( project_id: uuid.UUID, repo_id: uuid.UUID, instance_id: uuid.UUID, tail: int = 100, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), session: AsyncSession = Depends(get_db_session), ) -> dict: - """Get container logs for an instance. - - Args: - project_id: UUID of the project. - repo_id: UUID of the repository. - instance_id: UUID of the instance. - tail: Number of log lines to return (default: 100). - user_id: ID of the authenticated user. - session: Database session. - - Returns: - Dictionary containing the container logs. - """ - _user = await _get_user(session, user_id) - _project = await _get_owned_project(project_id, user_id, session) - - instance = await session.get(ToolInstance, instance_id) - if instance is None or instance.repository_id != repo_id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="instance not found" - ) - + """Get container logs for an instance.""" + instance = await _get_instance(session, instance_id, repo_id) if not instance.container_id: return {"logs": "No container running"} - - logs = get_container_logs(instance.container_id, tail) - return {"logs": logs} - - -@router.post( - "/{project_id}/repositories/{repo_id}/instances/{instance_id}/recreate-tunnel", - summary="Recreate tunnel", - description="Recreate the temporary Cloudflare tunnel for a running instance.", -) + return {"logs": container_svc.get_container_logs(instance.container_id, tail)} +@router.post("/{project_id}/repositories/{repo_id}/instances/{instance_id}/recreate-tunnel") async def recreate_tunnel_endpoint( project_id: uuid.UUID, repo_id: uuid.UUID, instance_id: uuid.UUID, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), session: AsyncSession = Depends(get_db_session), ) -> dict: - """Recreate the temporary tunnel for an instance. - - Args: - project_id: UUID of the project. - repo_id: UUID of the repository. - instance_id: UUID of the instance. - user_id: ID of the authenticated user. - session: Database session. - - Returns: - Dictionary with new URL and status. - """ - _user = await _get_user(session, user_id) - _project = await _get_owned_project(project_id, user_id, session) - - instance = await session.get(ToolInstance, instance_id) - if instance is None or instance.repository_id != repo_id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="instance not found" - ) - + """Recreate the temporary tunnel for an instance.""" + instance = await _get_instance(session, instance_id, repo_id) if instance.status != "running": - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="instance must be running to recreate tunnel", - ) + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="instance must be running") tool_type = await session.get(ToolType, instance.tool_type_id) - if not tool_type: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="Tool type not found for this instance", - ) - - expected_name = instance.name.lower() - logger.info( - "Recreate tunnel for instance %s (expected container name: %s, default_port: %s)", - instance.id, - expected_name, - tool_type.default_port, - ) - - # Find the tool container — try stored ID first, then fall back to name lookup - tool_container_id = instance.container_id - if tool_container_id: - logger.info("Using stored container_id: %s", tool_container_id) - else: - tool_container_id = get_container_id(expected_name) - if tool_container_id: - logger.info("Found container by name: %s", tool_container_id) - else: - logger.error("Container %s not found", expected_name) - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="Could not find running container for this instance", - ) - - # Ensure the tool container is on the backend network so the tunnel can reach it - network_name = get_backend_network_name() - on_network = is_container_on_network(tool_container_id, network_name) - logger.info( - "Container %s on network %s: %s", - tool_container_id, - network_name, - on_network, - ) - if not on_network: - logger.info( - "Connecting container %s to network %s", - tool_container_id, - network_name, - ) - connected = connect_container_to_network(tool_container_id, network_name) - logger.info("Network connect result: %s", connected) - - # Get the container's IP on the backend network - target_ip = get_container_ip_on_network(tool_container_id, network_name) - if target_ip: - target_url = f"http://{target_ip}:{tool_type.default_port or 0}" - logger.info( - "Tunnel target for instance %s: %s (IP %s on %s)", - instance.id, - target_url, - target_ip, - network_name, - ) - else: - target_url = f"http://{expected_name}:{tool_type.default_port or 0}" - logger.warning( - "Could not get container IP, falling back to name-based target: %s", - target_url, - ) + instance_port = tool_type.default_port if tool_type and tool_type.default_port else 8080 try: - tunnel_info = recreate_tunnel( - instance_name=instance.name, - container_port=tool_type.default_port or 0, - target_url=target_url, + tunnel_info = tunnel_svc.recreate_tunnel( + container_name=instance.container_name or instance.name, + port=instance_port, + old_pid=instance.tunnel_id, ) - logger.info( - "Tunnel recreated: container=%s, url=%s", - tunnel_info["container_name"], - tunnel_info["url"], - ) - - # Verify the tunnel can actually reach the origin - health = check_tunnel_health(tunnel_info["url"], timeout=10) - logger.info( - "Tunnel health check: status=%s, code=%s, error=%s", - health.get("tunnel_status"), - health.get("status_code"), - health.get("error"), - ) - - # Also probe from inside the API container directly to the target - probe = subprocess.run( - [ - "curl", - "-s", - "-o", - "/dev/null", - "-w", - "%{http_code}", - "--max-time", - "5", - target_url, - ], - capture_output=True, - text=True, - ) - logger.info( - "Direct probe from API to %s: HTTP %s", target_url, probe.stdout.strip() - ) - - instance.tunnel_id = tunnel_info["container_name"] + instance.tunnel_id = tunnel_info["pid"] instance.public_url = tunnel_info["url"] instance.url = tunnel_info["url"] await session.commit() return {"status": "healthy", "url": instance.url} except Exception as exc: - logger.exception("Failed to recreate tunnel for instance %s", instance.id) - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Failed to recreate tunnel: {str(exc)}", - ) - - -@router.get( - "/{project_id}/repositories/{repo_id}/instances/{instance_id}/health", - summary="Check instance health", - description="Check container and tunnel health for an instance.", -) + logger.exception("Failed to recreate tunnel") + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to recreate tunnel: {exc}") +@router.get("/{project_id}/repositories/{repo_id}/instances/{instance_id}/health") async def check_instance_tunnel_health( project_id: uuid.UUID, repo_id: uuid.UUID, instance_id: uuid.UUID, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), + project: Project = Depends(get_owned_project), session: AsyncSession = Depends(get_db_session), ) -> dict: - """Check health for an instance (container + tunnel). - - Args: - project_id: UUID of the project. - repo_id: UUID of the repository. - instance_id: UUID of the instance. - user_id: ID of the authenticated user. - session: Database session. - - Returns: - Dictionary with container_status, tunnel_status, probe_status, and overall healthy flag. - """ - _user = await _get_user(session, user_id) - _project = await _get_owned_project(project_id, user_id, session) - - instance = await session.get(ToolInstance, instance_id) - if instance is None or instance.repository_id != repo_id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="instance not found" - ) - - # Check container status - container_info = {"status": "not_found", "exit_code": None, "health": None} - if instance.container_id: - container_info = get_container_status(instance.container_id) - - # Build response - response = { - "healthy": False, - "container_status": container_info["status"], - "container_health": container_info["health"], - "tunnel_status": "not_applicable", - "tunnel_status_code": None, - "probe_status": "not_applicable", - "last_probe_output": None, - "error": None, - } - - # Determine probe status - if instance.status == "probing": - response["probe_status"] = "pending" - elif instance.probe_result: - response["probe_status"] = ( - "success" if instance.probe_result.get("success") else "failed" - ) - response["last_probe_output"] = "\n".join(instance.probe_result.get("logs", [])) - - # Check tunnel health if instance has a URL and is web-enabled - if instance.url and instance.status in ("running", "unhealthy"): - tunnel_health = check_tunnel_health(instance.url) - response["tunnel_status"] = tunnel_health["tunnel_status"] - response["tunnel_status_code"] = tunnel_health.get("status_code") - if tunnel_health.get("error"): - response["error"] = tunnel_health["error"] - - # Overall healthy: web tools need running container + healthy tunnel; - # terminal tools only need running container - container_healthy = container_info["status"] == "running" - if instance.url: - tunnel_healthy = response["tunnel_status"] == "healthy" - response["healthy"] = container_healthy and tunnel_healthy - else: - response["healthy"] = container_healthy - - # If container is not running, override error message - if not container_healthy: - response["error"] = f"Container is {container_info['status']}" - if container_info["exit_code"] is not None: - response["error"] += f" (exit code: {container_info['exit_code']})" - - return response - - -@router.get( - "/{project_id}/repositories/{repo_id}/instances/{instance_id}/events", - summary="Get instance events history", - description="Get lifecycle event history for a tool instance.", -) -async def get_instance_events( - project_id: uuid.UUID, - repo_id: uuid.UUID, - instance_id: uuid.UUID, - limit: int = 50, - user_id: uuid.UUID = Depends(get_current_user_id), - session: AsyncSession = Depends(get_db_session), -) -> list[dict]: - """Get lifecycle event history for an instance. - - Args: - project_id: UUID of the project. - repo_id: UUID of the repository. - instance_id: UUID of the instance. - limit: Maximum number of events to return (default: 50). - user_id: ID of the authenticated user. - session: Database session. - - Returns: - List of event dictionaries. - """ - from sqlalchemy import select - from src.models.instance_event import InstanceEvent - - _user = await _get_user(session, user_id) - _project = await _get_owned_project(project_id, user_id, session) - - instance = await session.get(ToolInstance, instance_id) - if instance is None or instance.repository_id != repo_id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="instance not found" - ) - - result = await session.execute( - select(InstanceEvent) - .where(InstanceEvent.instance_id == instance_id) - .order_by(InstanceEvent.created_at.desc()) - .limit(limit) - ) - rows = result.scalars().all() - - return [ - { - "id": str(row.id), - "event_type": row.event_type, - "status": row.status, - "message": row.message, - "metadata": row.event_metadata, - "created_at": row.created_at.isoformat() if row.created_at else None, - } - for row in rows - ] - - -@router.get( + """Check tunnel health for an instance.""" + instance = await _get_instance(session, instance_id, repo_id) + if not instance.url or instance.status != "running": + return {"healthy": False, "status_code": None, "error": "instance not running"} + return tunnel_svc.check_tunnel_health(instance.url) +@router.api_route( "/{project_id}/repositories/{repo_id}/instances/{instance_id}/proxy/{path:path}", - summary="Proxy to instance", - description="Proxy HTTP requests to a running tool instance.", -) -@router.post( - "/{project_id}/repositories/{repo_id}/instances/{instance_id}/proxy/{path:path}", - summary="Proxy to instance", - description="Proxy HTTP requests to a running tool instance.", - include_in_schema=False, -) -@router.put( - "/{project_id}/repositories/{repo_id}/instances/{instance_id}/proxy/{path:path}", - summary="Proxy to instance", - description="Proxy HTTP requests to a running tool instance.", - include_in_schema=False, -) -@router.delete( - "/{project_id}/repositories/{repo_id}/instances/{instance_id}/proxy/{path:path}", - summary="Proxy to instance", - description="Proxy HTTP requests to a running tool instance.", - include_in_schema=False, -) -@router.patch( - "/{project_id}/repositories/{repo_id}/instances/{instance_id}/proxy/{path:path}", - summary="Proxy to instance", - description="Proxy HTTP requests to a running tool instance.", - include_in_schema=False, -) -@router.head( - "/{project_id}/repositories/{repo_id}/instances/{instance_id}/proxy/{path:path}", - summary="Proxy to instance", - description="Proxy HTTP requests to a running tool instance.", - include_in_schema=False, -) -@router.options( - "/{project_id}/repositories/{repo_id}/instances/{instance_id}/proxy/{path:path}", - summary="Proxy to instance", - description="Proxy HTTP requests to a running tool instance.", - include_in_schema=False, + methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"], ) async def proxy_to_instance( request: Request, @@ -2830,152 +246,39 @@ async def proxy_to_instance( repo_id: uuid.UUID, instance_id: uuid.UUID, path: str = "", - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), session: AsyncSession = Depends(get_db_session), ) -> Response: - """Proxy requests to a running tool instance. - - Args: - request: The incoming HTTP request. - project_id: UUID of the project. - repo_id: UUID of the repository. - instance_id: UUID of the instance. - path: The path to proxy to the instance. - user_id: ID of the authenticated user. - session: Database session. - - Returns: - Response from the proxied instance. - """ - instance = await session.get(ToolInstance, instance_id) - if instance is None or instance.repository_id != repo_id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="instance not found" - ) - - # Verify ownership - if instance.owner_id != user_id: - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, - detail="not authorized to access this instance", - ) - + """Proxy HTTP requests to a running tool instance.""" + instance = await _get_instance(session, instance_id, repo_id) + if instance.owner_id != user.id: + raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="not authorized") if instance.status != "running" or not instance.container_name: - raise HTTPException( - status_code=status.HTTP_503_SERVICE_UNAVAILABLE, - detail="instance is not running", - ) + raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="instance is not running") - # Build target URL target_url = f"http://{instance.container_name}:{instance.port}" if path: target_url += f"/{path}" + query = str(request.query_params) + if query: + target_url += f"?{query}" - # Get query string - query_string = str(request.query_params) - if query_string: - target_url += f"?{query_string}" - - # Forward headers (excluding host) headers = dict(request.headers) headers.pop("host", None) - headers.pop("cookie", None) # Don't forward session cookies + headers.pop("cookie", None) - # Forward the request try: async with httpx.AsyncClient() as client: body = await request.body() response = await client.request( - method=request.method, - url=target_url, - headers=headers, - content=body, - follow_redirects=False, - timeout=30.0, + method=request.method, url=target_url, headers=headers, + content=body, follow_redirects=False, timeout=30.0, ) except Exception as exc: logger.error("Proxy error: %s", exc) - raise HTTPException( - status_code=status.HTTP_502_BAD_GATEWAY, - detail=f"failed to reach instance: {exc}", - ) + raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=f"failed to reach instance: {exc}") - # Build response - response_headers = dict(response.headers) - # Remove hop-by-hop headers - for header in ["content-encoding", "transfer-encoding", "connection"]: - response_headers.pop(header, None) - - return Response( - content=response.content, - status_code=response.status_code, - headers=response_headers, - ) - - -sessions_router = FastAPIRouter(prefix="/users", tags=["sessions"]) - - -@sessions_router.get( - "/me/sessions", - summary="Get user sessions", - description="Get all active sessions (running instances) for the current user.", -) -async def get_user_sessions( - user_id: uuid.UUID = Depends(get_current_user_id), - session: AsyncSession = Depends(get_db_session), -) -> dict: - """Get all active sessions for the current user. - - Args: - user_id: ID of the authenticated user. - session: Database session. - - Returns: - Dictionary containing list of active sessions with instance details. - """ - _user = await _get_user(session, user_id) - - result = await session.execute( - select(ToolInstance) - .where(ToolInstance.owner_id == user_id) - .where( - ToolInstance.status.in_( - ["running", "building", "pending", "stopped", "error"] - ) - ) - .order_by(ToolInstance.created_at.desc()) - ) - instances = result.scalars().all() - - sessions = [] - for instance in instances: - tool_type = await session.get(ToolType, instance.tool_type_id) - repo = await session.get(GitRepository, instance.repository_id) - project = await session.get(Project, instance.project_id) - - sessions.append( - { - "id": str(instance.id), - "display_name": instance.display_name, - "tool_type_name": tool_type.name if tool_type else "unknown", - "tool_icon": tool_type.name if tool_type else "code", - "tool_type_interfaces": [tool_type.interface_type] if tool_type else [], - "repository_name": repo.name if repo else "unknown", - "repository_id": str(instance.repository_id), - "project_name": project.name if project else "unknown", - "project_id": str(instance.project_id), - "status": instance.status, - "url": instance.url, - "clone_mode": instance.clone_mode, - "branch": instance.branch, - "selected_config_profile_id": str(instance.selected_config_profile_id) - if instance.selected_config_profile_id - else None, - "created_at": instance.created_at.isoformat() - if instance.created_at - else None, - } - ) - - return {"sessions": sessions} + resp_headers = dict(response.headers) + for h in ["content-encoding", "transfer-encoding", "connection"]: + resp_headers.pop(h, None) + return Response(content=response.content, status_code=response.status_code, headers=resp_headers) diff --git a/apps/api/src/api/tool_types.py b/apps/api/src/api/tool_types.py index d669c39..6cad489 100644 --- a/apps/api/src/api/tool_types.py +++ b/apps/api/src/api/tool_types.py @@ -1,19 +1,15 @@ import uuid from datetime import datetime +import yaml from fastapi import APIRouter, Depends, HTTPException, status -from pydantic import BaseModel, ConfigDict, field_validator, model_validator from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession -from src.api.tool_types_validation import ( - check_port_exposed, - validate_compose_yaml, - validate_required_variables, -) -from src.auth.dependencies import _get_user, get_current_user_id, get_db_session +from src.auth.dependencies import get_current_user, get_db_session from src.models.tool_type import ToolType from src.models.user import User +from src.schemas.tool_type import ToolTypeCreate, ToolTypeResponse, ToolTypeUpdate, ToolTypeValidateRequest router = APIRouter(prefix="/tool-types", tags=["tool-types"]) @@ -29,237 +25,6 @@ async def _require_admin(user: User) -> None: pass -class ToolTypeCreate(BaseModel): - name: str - display_name: str - description: str | None = None - default_port: int = 0 - definition_type: str = "compose" - manifest_id: uuid.UUID | None = None - compose_template: str | None = None - dockerfile_template: str | None = None - build_context: dict | None = None - readiness_probe: dict | None = None - startup_command: str | None = None - required_variables: list[str] = [] - category: str = "other" - interface_type: str = "web" - requires_port: bool = True - - @field_validator("definition_type") - @classmethod - def validate_definition_type(cls, v: str) -> str: - if v not in ("compose", "dockerfile", "manifest"): - raise ValueError( - "definition_type must be 'compose', 'dockerfile', or 'manifest'" - ) - return v - - @field_validator("compose_template") - @classmethod - def validate_compose_template(cls, v: str | None, info) -> str | None: - data = info.data - if data.get("definition_type") != "compose": - return v - - if v is None or not v.strip(): - raise ValueError( - "compose_template is required when definition_type is 'compose'" - ) - - validate_compose_yaml(v) - return v - - @field_validator("dockerfile_template") - @classmethod - def validate_dockerfile_template(cls, v: str | None, info) -> str | None: - data = info.data - if data.get("definition_type") != "dockerfile": - return v - - if v is None or not v.strip(): - raise ValueError( - "dockerfile_template is required when definition_type is 'dockerfile'" - ) - - if not v.strip().startswith("FROM"): - raise ValueError("Dockerfile must start with a FROM instruction") - - return v - - @field_validator("interface_type") - @classmethod - def validate_interface_type(cls, v: str) -> str: - if v not in ("web", "terminal"): - raise ValueError("interface_type must be 'web' or 'terminal'") - return v - - @field_validator("default_port") - @classmethod - def validate_default_port(cls, v: int, info) -> int: - data = info.data - requires_port = data.get("requires_port", True) - if not requires_port: - return v - if v <= 0 or v > 65535: - raise ValueError("Port must be between 1 and 65535") - return v - - @field_validator("required_variables") - @classmethod - def validate_required_variables(cls, v: list[str], info) -> list[str]: - if not v: - return v - - data = info.data - if data.get("definition_type") != "compose": - return v - - template = data.get("compose_template") - if not template: - return v - - for var in v: - placeholder = f"{{{{{var}}}}}" - if placeholder not in template: - raise ValueError( - f"Required variable '{var}' not found in compose template" - ) - - return v - - @model_validator(mode="after") - def validate_templates(self) -> "ToolTypeCreate": - if self.definition_type == "manifest": - if self.manifest_id is None: - raise ValueError( - "manifest_id is required when definition_type is 'manifest'" - ) - return self - - if self.definition_type == "dockerfile" and ( - self.dockerfile_template is None or not self.dockerfile_template.strip() - ): - raise ValueError( - "dockerfile_template is required when definition_type is 'dockerfile'" - ) - if self.definition_type == "compose" and ( - self.compose_template is None or not self.compose_template.strip() - ): - raise ValueError( - "compose_template is required when definition_type is 'compose'" - ) - - # Validate that default_port is exposed in compose template (only if requires_port) - if ( - self.requires_port - and self.definition_type == "compose" - and self.compose_template - ): - try: - parsed = validate_compose_yaml(self.compose_template) - except ValueError: - return self - - if not check_port_exposed(parsed, self.default_port): - raise ValueError( - f"Port {self.default_port} is not exposed in the compose template. Add it to the 'ports' section." - ) - - return self - - -class ToolTypeUpdate(BaseModel): - display_name: str | None = None - description: str | None = None - default_port: int | None = None - definition_type: str | None = None - manifest_id: uuid.UUID | None = None - compose_template: str | None = None - dockerfile_template: str | None = None - build_context: dict | None = None - readiness_probe: dict | None = None - startup_command: str | None = None - required_variables: list[str] | None = None - category: str | None = None - interface_type: str | None = None - requires_port: bool | None = None - - @field_validator("definition_type") - @classmethod - def validate_definition_type(cls, v: str | None) -> str | None: - if v is None: - return v - if v not in ("compose", "dockerfile", "manifest"): - raise ValueError( - "definition_type must be 'compose', 'dockerfile', or 'manifest'" - ) - return v - - @field_validator("interface_type") - @classmethod - def validate_interface_type(cls, v: str | None) -> str | None: - if v is None: - return v - if v not in ("web", "terminal"): - raise ValueError("interface_type must be 'web' or 'terminal'") - return v - - @field_validator("compose_template") - @classmethod - def validate_compose_template(cls, v: str | None, info) -> str | None: - if v is None: - return v - - data = info.data - definition_type = data.get("definition_type") - if definition_type and definition_type != "compose": - return v - - validate_compose_yaml(v) - return v - - @field_validator("dockerfile_template") - @classmethod - def validate_dockerfile_template(cls, v: str | None, info) -> str | None: - if v is None: - return v - - data = info.data - definition_type = data.get("definition_type") - if definition_type and definition_type != "dockerfile": - return v - - if not v.strip().startswith("FROM"): - raise ValueError("Dockerfile must start with a FROM instruction") - - return v - - -class ToolTypeResponse(BaseModel): - model_config = ConfigDict(from_attributes=True) - - id: uuid.UUID - name: str - display_name: str - description: str | None - category: str - interface_type: str - requires_port: bool - default_port: int - definition_type: str - manifest_id: uuid.UUID | None - compose_template: str | None - dockerfile_template: str | None - build_context: dict | None - readiness_probe: dict | None - startup_command: str | None - required_variables: list[str] - created_by_id: uuid.UUID | None - created_at: datetime - updated_at: datetime - - @router.post( "", response_model=ToolTypeResponse, @@ -269,7 +34,7 @@ class ToolTypeResponse(BaseModel): ) async def create_tool_type( data: ToolTypeCreate, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), session: AsyncSession = Depends(get_db_session), ) -> ToolType: """Create a new tool type. @@ -282,33 +47,27 @@ async def create_tool_type( Returns: The newly created tool type. """ - user = await _get_user(session, user_id) await _require_admin(user) - + # Check for duplicate name existing = await session.scalar(select(ToolType).where(ToolType.name == data.name)) if existing: - raise HTTPException( - status_code=status.HTTP_409_CONFLICT, - detail="tool type with this name already exists", - ) - + raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="tool type with this name already exists") + tool_type = ToolType( name=data.name, display_name=data.display_name, description=data.description, default_port=data.default_port, definition_type=data.definition_type, - manifest_id=data.manifest_id, compose_template=data.compose_template, dockerfile_template=data.dockerfile_template, build_context=data.build_context, readiness_probe=data.readiness_probe, - startup_command=data.startup_command, required_variables=data.required_variables, category=data.category, - interface_type=data.interface_type, - requires_port=data.requires_port, + interfaces=data.interfaces, + is_builtin=False, created_by_id=user.id, ) session.add(tool_type) @@ -324,7 +83,7 @@ async def create_tool_type( description="List all available tool types including built-in and custom ones.", ) async def list_tool_types( - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), session: AsyncSession = Depends(get_db_session), ) -> list[ToolType]: """List all tool types. @@ -336,7 +95,6 @@ async def list_tool_types( Returns: List of all tool types ordered by name. """ - await _get_user(session, user_id) result = await session.execute(select(ToolType).order_by(ToolType.name)) return list(result.scalars().all()) @@ -349,7 +107,7 @@ async def list_tool_types( ) async def get_tool_type( tool_type_id: uuid.UUID, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), session: AsyncSession = Depends(get_db_session), ) -> ToolType: """Get a specific tool type by ID. @@ -362,12 +120,9 @@ async def get_tool_type( Returns: The requested tool type. """ - await _get_user(session, user_id) tool_type = await session.get(ToolType, tool_type_id) if tool_type is None: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="tool type not found" - ) + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="tool type not found") return tool_type @@ -380,7 +135,7 @@ async def get_tool_type( async def update_tool_type( tool_type_id: uuid.UUID, data: ToolTypeUpdate, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), session: AsyncSession = Depends(get_db_session), ) -> ToolType: """Update a tool type. @@ -394,79 +149,88 @@ async def update_tool_type( Returns: The updated tool type. """ - user = await _get_user(session, user_id) await _require_admin(user) - + tool_type = await session.get(ToolType, tool_type_id) if tool_type is None: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="tool type not found" - ) - - # Built-in tool types can now be modified - + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="tool type not found") + + if tool_type.is_builtin: + raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="cannot modify built-in tool types") + update_data = data.model_dump(exclude_unset=True) - + # Validate port if being updated - requires_port = update_data.get("requires_port", tool_type.requires_port) - if "default_port" in update_data and requires_port: + if "default_port" in update_data: new_port = update_data["default_port"] if new_port <= 0 or new_port > 65535: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, - detail="Port must be between 1 and 65535", + detail="Port must be between 1 and 65535" ) - + # Only validate port exposure for compose definitions definition_type = update_data.get("definition_type", tool_type.definition_type) if definition_type == "compose": template = update_data.get("compose_template", tool_type.compose_template) if template: try: - parsed = validate_compose_yaml(template) - if not check_port_exposed(parsed, new_port): + parsed = yaml.safe_load(template) + except yaml.YAMLError: + parsed = None + + if parsed and isinstance(parsed, dict) and "services" in parsed: + port_str = str(new_port) + port_exposed = False + for service_config in parsed["services"].values(): + if isinstance(service_config, dict) and "ports" in service_config: + for port_mapping in service_config["ports"]: + if isinstance(port_mapping, str) and port_str in port_mapping: + port_exposed = True + break + elif isinstance(port_mapping, int) and port_mapping == new_port: + port_exposed = True + break + if port_exposed: + break + + if not port_exposed: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, - detail=f"Port {new_port} is not exposed in the compose template", + detail=f"Port {new_port} is not exposed in the compose template" ) - except ValueError as e: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, detail=str(e) - ) - + # Validate required variables for compose definitions definition_type = update_data.get("definition_type", tool_type.definition_type) if definition_type == "compose": if "required_variables" in update_data and "compose_template" in update_data: - validate_required_variables( - update_data["compose_template"], update_data["required_variables"] - ) + template = update_data["compose_template"] + for var in update_data["required_variables"]: + placeholder = f"{{{{{var}}}}}" + if placeholder not in template: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=f"Required variable '{var}' not found in compose template" + ) elif "required_variables" in update_data: template = tool_type.compose_template if template: - validate_required_variables(template, update_data["required_variables"]) - - # When switching to manifest, clear legacy templates - if definition_type == "manifest": - if "manifest_id" in update_data: - tool_type.manifest_id = update_data["manifest_id"] - tool_type.compose_template = None - tool_type.dockerfile_template = None - + for var in update_data["required_variables"]: + placeholder = f"{{{{{var}}}}}" + if placeholder not in template: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=f"Required variable '{var}' not found in compose template" + ) + for field, value in update_data.items(): setattr(tool_type, field, value) - + await session.commit() await session.refresh(tool_type) return tool_type -class ToolTypeValidateRequest(BaseModel): - definition_type: str - compose_template: str | None = None - dockerfile_template: str | None = None - - @router.post( "/validate", summary="Validate tool type template", @@ -474,7 +238,7 @@ class ToolTypeValidateRequest(BaseModel): ) async def validate_tool_type_template( data: ToolTypeValidateRequest, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), session: AsyncSession = Depends(get_db_session), ) -> dict: """Validate a tool type template syntax. @@ -487,7 +251,6 @@ async def validate_tool_type_template( Returns: Validation result with success status and any errors. """ - await _get_user(session, user_id) errors = [] @@ -496,9 +259,15 @@ async def validate_tool_type_template( errors.append("Compose template is required") else: try: - validate_compose_yaml(data.compose_template) - except ValueError as e: - errors.append(str(e)) + parsed = yaml.safe_load(data.compose_template) + if not isinstance(parsed, dict): + errors.append("Compose template must be a YAML mapping") + elif "services" not in parsed: + errors.append("Compose template must contain 'services' key") + elif not parsed["services"]: + errors.append("Compose template must define at least one service") + except yaml.YAMLError as e: + errors.append(f"Invalid YAML: {e}") elif data.definition_type == "dockerfile": if not data.dockerfile_template: @@ -506,11 +275,8 @@ async def validate_tool_type_template( elif not data.dockerfile_template.strip().startswith("FROM"): errors.append("Dockerfile must start with a FROM instruction") - elif data.definition_type == "manifest": - pass # Manifest validation is handled separately - else: - errors.append("definition_type must be 'compose', 'dockerfile', or 'manifest'") + errors.append("definition_type must be 'compose' or 'dockerfile'") return { "valid": len(errors) == 0, @@ -525,7 +291,7 @@ async def validate_tool_type_template( ) async def validate_tool_type( tool_type_id: uuid.UUID, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), session: AsyncSession = Depends(get_db_session), ) -> dict: """Validate a tool type's template syntax. @@ -538,34 +304,33 @@ async def validate_tool_type( Returns: Validation result with success status and any errors. """ - await _get_user(session, user_id) tool_type = await session.get(ToolType, tool_type_id) if tool_type is None: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="tool type not found" - ) - + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="tool type not found") + errors = [] - + if tool_type.definition_type == "compose": if not tool_type.compose_template: errors.append("Compose template is empty") else: try: - validate_compose_yaml(tool_type.compose_template) - except ValueError as e: - errors.append(str(e)) - + parsed = yaml.safe_load(tool_type.compose_template) + if not isinstance(parsed, dict): + errors.append("Compose template must be a YAML mapping") + elif "services" not in parsed: + errors.append("Compose template must contain 'services' key") + elif not parsed["services"]: + errors.append("Compose template must define at least one service") + except yaml.YAMLError as e: + errors.append(f"Invalid YAML: {e}") + elif tool_type.definition_type == "dockerfile": if not tool_type.dockerfile_template: errors.append("Dockerfile template is empty") elif not tool_type.dockerfile_template.strip().startswith("FROM"): errors.append("Dockerfile must start with a FROM instruction") - - elif tool_type.definition_type == "manifest": - if not tool_type.manifest_id: - errors.append("Manifest reference is missing") - + return { "valid": len(errors) == 0, "errors": errors, @@ -580,7 +345,7 @@ async def validate_tool_type( ) async def delete_tool_type( tool_type_id: uuid.UUID, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), session: AsyncSession = Depends(get_db_session), ) -> None: """Delete a tool type. @@ -593,16 +358,14 @@ async def delete_tool_type( Returns: None with 204 status code. """ - user = await _get_user(session, user_id) await _require_admin(user) - + tool_type = await session.get(ToolType, tool_type_id) if tool_type is None: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="tool type not found" - ) - - # Built-in tool types can now be deleted - + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="tool type not found") + + if tool_type.is_builtin: + raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="cannot delete built-in tool types") + await session.delete(tool_type) await session.commit() diff --git a/apps/api/src/api/user_config.py b/apps/api/src/api/user_config.py index 99a8892..ec2c4af 100644 --- a/apps/api/src/api/user_config.py +++ b/apps/api/src/api/user_config.py @@ -1,22 +1,20 @@ import logging import uuid -from fastapi import APIRouter, Depends -from pydantic import BaseModel, ConfigDict +from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession -from src.auth.dependencies import _get_user, get_current_user_id, get_db_session +from src.auth.dependencies import get_current_user, get_db_session +from src.models.user import User from src.models.user_config import UserConfig - -logger = logging.getLogger(__name__) +from src.schemas.user_config import UserConfigResponse, UserConfigUpdate router = APIRouter(prefix="/users/me", tags=["user-config"]) -async def _get_or_create_config( - session: AsyncSession, user_id: uuid.UUID -) -> UserConfig: + +async def _get_or_create_config(session: AsyncSession, user_id: uuid.UUID) -> UserConfig: """Get or create user config record. Args: @@ -26,40 +24,16 @@ async def _get_or_create_config( Returns: The user's config, creating a new one if it doesn't exist. """ - result = await session.execute( - select(UserConfig).where(UserConfig.user_id == user_id) - ) + result = await session.execute(select(UserConfig).where(UserConfig.user_id == user.id)) config = result.scalar_one_or_none() if config is None: - config = UserConfig(user_id=user_id, config={}) + config = UserConfig(user_id=user.id, config={}) session.add(config) await session.commit() await session.refresh(config) return config -class UserConfigResponse(BaseModel): - model_config = ConfigDict(from_attributes=True) - - default_editor: str | None = None - theme: str = "system" - git_user_name: str | None = None - git_user_email: str | None = None - last_session_id: str | None = None - notification_mute_categories: list[str] | None = None - notification_toast_level: str | None = None - - -class UserConfigUpdate(BaseModel): - default_editor: str | None = None - theme: str | None = None - git_user_name: str | None = None - git_user_email: str | None = None - last_session_id: str | None = None - notification_mute_categories: list[str] | None = None - notification_toast_level: str | None = None - - @router.get( "/config", response_model=UserConfigResponse, @@ -67,7 +41,7 @@ class UserConfigUpdate(BaseModel): description="Get the current user's configuration settings.", ) async def get_user_config( - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), session: AsyncSession = Depends(get_db_session), ) -> UserConfigResponse: """Get the current user's configuration. @@ -79,8 +53,7 @@ async def get_user_config( Returns: The user's configuration settings. """ - _user = await _get_user(session, user_id) - config = await _get_or_create_config(session, user_id) + config = await _get_or_create_config(session, user.id) return UserConfigResponse.model_validate(config.config) @@ -92,7 +65,7 @@ async def get_user_config( ) async def update_user_config( data: UserConfigUpdate, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), session: AsyncSession = Depends(get_db_session), ) -> UserConfigResponse: """Update the current user's configuration. @@ -105,16 +78,15 @@ async def update_user_config( Returns: The updated user configuration. """ - _user = await _get_user(session, user_id) - config = await _get_or_create_config(session, user_id) + config = await _get_or_create_config(session, user.id) # Merge updates update_data = data.model_dump(exclude_unset=True) - logger.debug("Updating user config for user %s: %s", user_id, update_data) + logger.info("Updating user config for user %s: %s", user.id, update_data) # SQLAlchemy JSON doesn't track dict mutations, so we replace the whole dict config.config = {**config.config, **update_data} await session.commit() await session.refresh(config) - logger.debug("Updated config: %s", config.config) + logger.info("Updated config: %s", config.config) return UserConfigResponse.model_validate(config.config) diff --git a/apps/api/src/api/users.py b/apps/api/src/api/users.py index 5533b4b..1955f1b 100644 --- a/apps/api/src/api/users.py +++ b/apps/api/src/api/users.py @@ -2,11 +2,14 @@ import uuid from pathlib import Path from fastapi import APIRouter, Depends, HTTPException, UploadFile, status -from pydantic import BaseModel, ConfigDict +from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession -from src.auth.dependencies import _get_user, get_current_user_id, get_db_session +from src.auth.dependencies import get_current_user, get_db_session +from src.models.tool_instance import ToolInstance from src.models.user import User +from src.schemas.tool_instance import SessionItemResponse, SessionListResponse +from src.schemas.user import UserProfileResponse, UserProfileUpdate router = APIRouter(prefix="/users", tags=["users"]) @@ -16,19 +19,6 @@ ALLOWED_CONTENT_TYPES = {"image/png", "image/jpeg", "image/jpg"} MAX_AVATAR_SIZE = 2 * 1024 * 1024 # 2MB -class UserProfileResponse(BaseModel): - model_config = ConfigDict(from_attributes=True) - - id: uuid.UUID - email: str - name: str - avatar_url: str | None - - -class UserProfileUpdate(BaseModel): - name: str | None = None - email: str | None = None - @router.get( "/me", @@ -37,7 +27,7 @@ class UserProfileUpdate(BaseModel): description="Retrieve the profile of the currently authenticated user.", ) async def get_profile( - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), session: AsyncSession = Depends(get_db_session), ) -> User: """Get the current user's profile. @@ -49,7 +39,7 @@ async def get_profile( Returns: The user's profile information. """ - return await _get_user(session, user_id) + return user @router.put( @@ -60,7 +50,7 @@ async def get_profile( ) async def update_profile( data: UserProfileUpdate, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), session: AsyncSession = Depends(get_db_session), ) -> User: """Update the current user's profile. @@ -73,7 +63,6 @@ async def update_profile( Returns: The updated user profile. """ - user = await _get_user(session, user_id) if data.name is not None: if len(data.name.strip()) == 0: @@ -98,7 +87,7 @@ async def update_profile( ) async def upload_avatar( file: UploadFile, - user_id: uuid.UUID = Depends(get_current_user_id), + user: User = Depends(get_current_user), session: AsyncSession = Depends(get_db_session), ) -> User: """Upload a profile avatar image. @@ -111,7 +100,6 @@ async def upload_avatar( Returns: The updated user profile with new avatar URL. """ - user = await _get_user(session, user_id) if file.content_type not in ALLOWED_CONTENT_TYPES: raise HTTPException( @@ -146,3 +134,41 @@ async def upload_avatar( await session.commit() await session.refresh(user) return user + + +@router.get( + "/me/sessions", + response_model=SessionListResponse, + summary="Get current user sessions", + description="Retrieve all tool instances (sessions) for the authenticated user.", +) +async def get_user_sessions( + user: User = Depends(get_current_user), + session: AsyncSession = Depends(get_db_session), +) -> SessionListResponse: + """Return all tool instances for the current user with related names.""" + result = await session.execute( + select(ToolInstance) + .where(ToolInstance.owner_id == user.id) + .order_by(ToolInstance.created_at.desc()) + ) + instances = result.scalars().all() + + sessions = [ + SessionItemResponse( + id=str(inst.id), + display_name=inst.display_name, + tool_type_name=inst.tool_type.display_name if inst.tool_type else "Unknown", + tool_icon=inst.tool_type.icon if inst.tool_type else None, + tool_type_interfaces=inst.tool_type.interfaces if inst.tool_type else [], + repository_name=inst.repository.name if inst.repository else "Unknown", + repository_id=str(inst.repository_id), + project_name=inst.project.name if inst.project else "Unknown", + project_id=str(inst.project_id), + status=inst.status, + url=inst.url, + ) + for inst in instances + ] + + return SessionListResponse(sessions=sessions) diff --git a/apps/api/src/auth/dependencies.py b/apps/api/src/auth/dependencies.py index 0e2a580..0b3ea9b 100644 --- a/apps/api/src/auth/dependencies.py +++ b/apps/api/src/auth/dependencies.py @@ -50,25 +50,17 @@ async def get_current_user( return user -async def _get_user(session: AsyncSession, user_id: uuid.UUID) -> User: - """Fetch a user by ID or raise 401 if not found.""" - user = await session.get(User, user_id) - if user is None: - raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="user not found") - return user - - -async def _get_owned_project( +async def get_owned_project( project_id: uuid.UUID, - user_id: uuid.UUID, - session: AsyncSession, -) -> "Project": + user: User = Depends(get_current_user), + db_session: AsyncSession = Depends(get_db_session), +) -> Project: """Fetch a project and verify ownership. Args: - project_id: UUID of the project. - user_id: ID of the authenticated user. - session: Database session. + project_id: UUID of the project (injected from path parameter). + user: The currently authenticated user. + db_session: Database session. Returns: The project if found and owned by the user. @@ -76,11 +68,9 @@ async def _get_owned_project( Raises: HTTPException: 404 if project not found, 403 if user is not the owner. """ - from src.models.project import Project - - project = await session.get(Project, project_id) + project = await db_session.get(Project, project_id) if project is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="project not found") - if project.owner_id != user_id: + if project.owner_id != user.id: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="not project owner") return project diff --git a/apps/api/src/main.py b/apps/api/src/main.py index d94f8a5..9e2cc4b 100644 --- a/apps/api/src/main.py +++ b/apps/api/src/main.py @@ -1,3 +1,4 @@ +import json import logging import os @@ -6,40 +7,30 @@ from fastapi.exceptions import RequestValidationError from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from fastapi.staticfiles import StaticFiles - from src.api.auth import router as auth_router from src.api.dashboard import router as dashboard_router -from src.api.events import router as events_router from src.api.git_repositories import router as git_repositories_router from src.api.health import router as health_router from src.api.projects import router as projects_router from src.api.ssh_keys import router as ssh_keys_router from src.api.terminal import router as terminal_router from src.api.instance_proxy import router as instance_proxy_router +from src.api.config_folders import router as config_folders_router from src.api.config_profiles import router as config_profiles_router -from src.api.tool_definitions import router as tool_definitions_router +from src.api.tool_configs import router as tool_configs_router from src.api.tool_instances import router as tool_instances_router from src.api.tool_instances import sessions_router from src.api.tool_types import router as tool_types_router -from src.api.notifications import router as notifications_router from src.api.user_config import router as user_config_router from src.api.users import router as users_router -from src.api.workspace_files import router as workspace_files_router -from src.api.workspace_git import router as workspace_git_router -from src.api.workspace_instances import router as workspace_instances_router -from src.api.workspaces import all_workspaces_router, router as workspaces_router from src.config import Settings -from src.models.notification import Notification # noqa: F401 – Alembic model discovery -from src.models.terminal_session import TerminalSessionModel # noqa: F401 – Alembic model discovery -from src.database import init_database +from src.database import SessionLocal, init_database from src.logging_config import ( ExceptionLoggingMiddleware, RequestLoggingMiddleware, configure_logging, ) -from src.services.correlation import CorrelationIdMiddleware -from src.services.event_bus import InstanceEventBus -from src.services.health_monitor import HealthMonitor +from src.seeds.builtin_tool_types import seed_builtin_tool_types # Configure logging early log_level = os.getenv("LOG_LEVEL", "INFO").upper() @@ -64,7 +55,6 @@ app.add_middleware( allow_headers=["*"], ) -app.add_middleware(CorrelationIdMiddleware) app.add_middleware(RequestLoggingMiddleware) app.add_middleware(ExceptionLoggingMiddleware) @@ -77,9 +67,7 @@ def _sanitize_validation_errors(errors): "type": error.get("type"), "loc": error.get("loc"), "msg": error.get("msg"), - "input": str(error.get("input")) - if error.get("input") is not None - else None, + "input": str(error.get("input")) if error.get("input") is not None else None, } # Convert ctx to safe format ctx = error.get("ctx") @@ -114,11 +102,6 @@ async def validation_exception_handler(request: Request, exc: RequestValidationE ) -# Global services -_event_bus = InstanceEventBus() -_health_monitor = HealthMonitor(_event_bus) - - @app.on_event("startup") async def on_startup(): logger.info("Starting up Headquarter API...") @@ -128,24 +111,12 @@ async def on_startup(): if not db_ready: logger.error("Database initialization failed. Shutting down.") import sys - sys.exit(1) - # Start background health monitor - _health_monitor.start() - logger.info("Health monitor started") - + # Seed built-in data + await seed_builtin_tool_types() logger.info("Startup complete.") - -@app.on_event("shutdown") -async def on_shutdown(): - logger.info("Shutting down Headquarter API...") - _health_monitor.stop() - logger.info("Health monitor stopped") - logger.info("Shutdown complete.") - - app.include_router(health_router) app.include_router(auth_router) app.include_router(dashboard_router) @@ -155,17 +126,11 @@ app.include_router(ssh_keys_router) app.include_router(git_repositories_router) app.include_router(user_config_router) app.include_router(tool_types_router) -app.include_router(tool_definitions_router) +app.include_router(config_folders_router) app.include_router(config_profiles_router) app.include_router(tool_instances_router) +app.include_router(tool_configs_router) app.include_router(sessions_router) app.include_router(instance_proxy_router) app.include_router(terminal_router) -app.include_router(events_router) -app.include_router(notifications_router) -app.include_router(all_workspaces_router) -app.include_router(workspaces_router) -app.include_router(workspace_files_router) -app.include_router(workspace_git_router) -app.include_router(workspace_instances_router) app.mount("/uploads", StaticFiles(directory="uploads"), name="uploads") diff --git a/apps/api/src/models/__init__.py b/apps/api/src/models/__init__.py index 0ea612d..3358553 100644 --- a/apps/api/src/models/__init__.py +++ b/apps/api/src/models/__init__.py @@ -1,13 +1,11 @@ from src.models.base import Base -from src.models.config_profile import ConfigProfile, ConfigProfileInclude +from src.models.config_folder import ConfigFolder +from src.models.config_include import ConfigInclude +from src.models.config_mount import ConfigMount +from src.models.config_profile import ConfigProfile from src.models.git_repository import GitRepository -from src.models.health_check import HealthCheck -from src.models.instance_event import InstanceEvent -from src.models.notification import Notification from src.models.project import Project from src.models.ssh_key import SSHKey -from src.models.terminal_session import TerminalSessionModel -from src.models.tool_definition_manifest import ToolDefinitionManifest from src.models.tool_instance import ToolInstance from src.models.tool_type import ToolType from src.models.user import User @@ -15,16 +13,13 @@ from src.models.user_config import UserConfig __all__ = [ "Base", + "ConfigFolder", + "ConfigInclude", + "ConfigMount", "ConfigProfile", - "ConfigProfileInclude", "GitRepository", - "HealthCheck", - "InstanceEvent", - "Notification", "Project", "SSHKey", - "TerminalSessionModel", - "ToolDefinitionManifest", "ToolInstance", "ToolType", "User", diff --git a/apps/api/src/models/config_folder.py b/apps/api/src/models/config_folder.py new file mode 100644 index 0000000..9c232fe --- /dev/null +++ b/apps/api/src/models/config_folder.py @@ -0,0 +1,33 @@ +import uuid +from typing import TYPE_CHECKING + +from sqlalchemy import Boolean, ForeignKey, JSON, String, Text +from sqlalchemy import Uuid as UUID +from sqlalchemy.orm import Mapped, mapped_column, relationship + +from src.models.base import Base, TimestampMixin, UUIDPrimaryKeyMixin + +if TYPE_CHECKING: + from src.models.user import User + + +class ConfigFolder(UUIDPrimaryKeyMixin, TimestampMixin, Base): + __tablename__ = "config_folders" + + user_id: Mapped[uuid.UUID] = mapped_column( + UUID(), ForeignKey("users.id", ondelete="CASCADE"), nullable=False + ) + name: Mapped[str] = mapped_column(String(255), nullable=False) + description: Mapped[str | None] = mapped_column(Text, nullable=True) + mount_path: Mapped[str] = mapped_column(String(1024), nullable=False) + files: Mapped[dict] = mapped_column( + JSON, default=dict, nullable=False + ) # {"relative/path": "content", ...} + project_overrides: Mapped[dict | None] = mapped_column( + JSON, default=dict, nullable=True + ) # {"project_id": {"mount_path": "...", "files": {...}}} + # DEPRECATED: Legacy auto-mounting flag. No longer used for launch-time + # auto-mounting. Use ConfigProfile and ToolInstance.selected_profile_id instead. + is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False) + + user: Mapped["User"] = relationship() diff --git a/apps/api/src/models/config_include.py b/apps/api/src/models/config_include.py new file mode 100644 index 0000000..90cc2c8 --- /dev/null +++ b/apps/api/src/models/config_include.py @@ -0,0 +1,36 @@ +import uuid +from typing import TYPE_CHECKING + +from sqlalchemy import ForeignKey, Integer, UniqueConstraint +from sqlalchemy import Uuid as UUID +from sqlalchemy.orm import Mapped, mapped_column, relationship + +from src.models.base import Base, TimestampMixin, UUIDPrimaryKeyMixin + +if TYPE_CHECKING: + from src.models.config_profile import ConfigProfile + + +class ConfigInclude(UUIDPrimaryKeyMixin, TimestampMixin, Base): + __tablename__ = "config_includes" + __table_args__ = ( + UniqueConstraint("profile_id", "included_profile_id", name="uq_config_includes_pair"), + ) + + profile_id: Mapped[uuid.UUID] = mapped_column( + UUID(), ForeignKey("config_profiles.id", ondelete="CASCADE"), nullable=False + ) + included_profile_id: Mapped[uuid.UUID] = mapped_column( + UUID(), ForeignKey("config_profiles.id", ondelete="CASCADE"), nullable=False + ) + order_index: Mapped[int] = mapped_column(Integer, nullable=False, default=0) + + profile: Mapped["ConfigProfile"] = relationship( + "ConfigProfile", + foreign_keys=[profile_id], + back_populates="includes", + ) + included_profile: Mapped["ConfigProfile"] = relationship( + "ConfigProfile", + foreign_keys=[included_profile_id], + ) diff --git a/apps/api/src/models/config_mount.py b/apps/api/src/models/config_mount.py new file mode 100644 index 0000000..de112a3 --- /dev/null +++ b/apps/api/src/models/config_mount.py @@ -0,0 +1,31 @@ +import uuid +from typing import TYPE_CHECKING + +from sqlalchemy import ForeignKey, Integer, JSON, String +from sqlalchemy import Uuid as UUID +from sqlalchemy.orm import Mapped, mapped_column, relationship + +from src.models.base import Base, TimestampMixin, UUIDPrimaryKeyMixin + +if TYPE_CHECKING: + from src.models.config_profile import ConfigProfile + + +class ConfigMount(UUIDPrimaryKeyMixin, TimestampMixin, Base): + __tablename__ = "config_mounts" + + profile_id: Mapped[uuid.UUID] = mapped_column( + UUID(), ForeignKey("config_profiles.id", ondelete="CASCADE"), nullable=False + ) + target_path: Mapped[str] = mapped_column(String(1024), nullable=False) + mode: Mapped[str] = mapped_column(String(10), nullable=False, default="rw") + files: Mapped[dict[str, str] | None] = mapped_column( + JSON, default=dict, nullable=True + ) + order_index: Mapped[int] = mapped_column(Integer, nullable=False, default=0) + + profile: Mapped["ConfigProfile"] = relationship( + "ConfigProfile", + foreign_keys=[profile_id], + back_populates="mounts", + ) diff --git a/apps/api/src/models/config_profile.py b/apps/api/src/models/config_profile.py index 04eae6f..2797241 100644 --- a/apps/api/src/models/config_profile.py +++ b/apps/api/src/models/config_profile.py @@ -1,13 +1,15 @@ import uuid from typing import TYPE_CHECKING -from sqlalchemy import ForeignKey, JSON, Integer, String, Text, Boolean +from sqlalchemy import ForeignKey, Integer, JSON, String, Text, UniqueConstraint from sqlalchemy import Uuid as UUID from sqlalchemy.orm import Mapped, mapped_column, relationship from src.models.base import Base, TimestampMixin, UUIDPrimaryKeyMixin if TYPE_CHECKING: + from src.models.config_include import ConfigInclude + from src.models.config_mount import ConfigMount from src.models.project import Project from src.models.tool_type import ToolType from src.models.user import User @@ -15,63 +17,43 @@ if TYPE_CHECKING: class ConfigProfile(UUIDPrimaryKeyMixin, TimestampMixin, Base): __tablename__ = "config_profiles" + __table_args__ = ( + UniqueConstraint("user_id", "name", name="uq_config_profiles_user_name"), + ) user_id: Mapped[uuid.UUID] = mapped_column( UUID(), ForeignKey("users.id", ondelete="CASCADE"), nullable=False ) - name: Mapped[str] = mapped_column(String(255), nullable=False) - description: Mapped[str | None] = mapped_column(Text, nullable=True) project_id: Mapped[uuid.UUID | None] = mapped_column( UUID(), ForeignKey("projects.id", ondelete="CASCADE"), nullable=True ) tool_type_id: Mapped[uuid.UUID | None] = mapped_column( UUID(), ForeignKey("tool_types.id", ondelete="CASCADE"), nullable=True ) - env_vars: Mapped[dict] = mapped_column( - JSON, default=dict, nullable=False - ) # {"VAR_NAME": "value", ...} - runtime_hints: Mapped[dict] = mapped_column( - JSON, default=dict, nullable=False - ) # {"start_command": "...", "working_dir": "...", ...} - mounts: Mapped[list] = mapped_column( - JSON, default=list, nullable=False - ) # [{"target": "/path", "mode": "rw", "files": {"rel/path": "content"}}, ...] - files: Mapped[dict] = mapped_column( - JSON, default=dict, nullable=False - ) # {"rel/path": "content", ...} - git_mounts: Mapped[list] = mapped_column( - JSON, default=list, nullable=False - ) # [{"remote_url": "https://github.com/user/repo.git", "source_path": ".", "target_path": "/path", "branch": "main"}, ...] - is_default: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) + name: Mapped[str] = mapped_column(String(255), nullable=False) + description: Mapped[str | None] = mapped_column(Text, nullable=True) + environment_variables: Mapped[dict[str, str] | None] = mapped_column( + JSON, default=dict, nullable=True + ) + start_command: Mapped[str | None] = mapped_column(Text, nullable=True) + working_directory: Mapped[str | None] = mapped_column(Text, nullable=True) + port: Mapped[int | None] = mapped_column(Integer, nullable=True) + is_default: Mapped[bool] = mapped_column(default=False, nullable=False) user: Mapped["User"] = relationship() project: Mapped["Project | None"] = relationship() tool_type: Mapped["ToolType | None"] = relationship() - includes: Mapped[list["ConfigProfileInclude"]] = relationship( - "ConfigProfileInclude", - foreign_keys="ConfigProfileInclude.profile_id", - order_by="ConfigProfileInclude.order_index", + includes: Mapped[list["ConfigInclude"]] = relationship( + "ConfigInclude", + primaryjoin="ConfigProfile.id == ConfigInclude.profile_id", + back_populates="profile", cascade="all, delete-orphan", + order_by="ConfigInclude.order_index", ) - - -class ConfigProfileInclude(UUIDPrimaryKeyMixin, TimestampMixin, Base): - __tablename__ = "config_profile_includes" - - profile_id: Mapped[uuid.UUID] = mapped_column( - UUID(), ForeignKey("config_profiles.id", ondelete="CASCADE"), nullable=False - ) - included_profile_id: Mapped[uuid.UUID] = mapped_column( - UUID(), ForeignKey("config_profiles.id", ondelete="CASCADE"), nullable=False - ) - order_index: Mapped[int] = mapped_column(Integer, nullable=False, default=0) - - profile: Mapped["ConfigProfile"] = relationship( - "ConfigProfile", - foreign_keys=[profile_id], - back_populates="includes", - ) - included_profile: Mapped["ConfigProfile"] = relationship( - "ConfigProfile", - foreign_keys=[included_profile_id], + mounts: Mapped[list["ConfigMount"]] = relationship( + "ConfigMount", + primaryjoin="ConfigProfile.id == ConfigMount.profile_id", + back_populates="profile", + cascade="all, delete-orphan", + order_by="ConfigMount.order_index", ) diff --git a/apps/api/src/models/tool_instance.py b/apps/api/src/models/tool_instance.py index ddca249..4fa0a89 100644 --- a/apps/api/src/models/tool_instance.py +++ b/apps/api/src/models/tool_instance.py @@ -2,7 +2,7 @@ import uuid from datetime import datetime from typing import TYPE_CHECKING -from sqlalchemy import DateTime, ForeignKey, Integer, JSON, String +from sqlalchemy import DateTime, ForeignKey, Integer, String from sqlalchemy import Uuid as UUID from sqlalchemy.orm import Mapped, mapped_column, relationship @@ -14,7 +14,6 @@ if TYPE_CHECKING: from src.models.project import Project from src.models.tool_type import ToolType from src.models.user import User - from src.models.workspace import Workspace class ToolInstance(UUIDPrimaryKeyMixin, TimestampMixin, Base): @@ -34,40 +33,42 @@ class ToolInstance(UUIDPrimaryKeyMixin, TimestampMixin, Base): owner_id: Mapped[uuid.UUID] = mapped_column( UUID(), ForeignKey("users.id"), nullable=False ) - status: Mapped[str] = mapped_column(String(50), nullable=False, default="pending") - container_id: Mapped[str | None] = mapped_column(String(255), nullable=True) - container_name: Mapped[str | None] = mapped_column(String(255), nullable=True) - compose_path: Mapped[str | None] = mapped_column(String(1024), nullable=True) - url: Mapped[str | None] = mapped_column(String(1024), nullable=True) - public_url: Mapped[str | None] = mapped_column(String(1024), nullable=True) - tunnel_id: Mapped[str | None] = mapped_column(String(255), nullable=True) - port: Mapped[int | None] = mapped_column(Integer, nullable=True) + status: Mapped[str] = mapped_column( + String(50), nullable=False, default="pending" + ) + container_id: Mapped[str | None] = mapped_column( + String(255), nullable=True + ) + container_name: Mapped[str | None] = mapped_column( + String(255), nullable=True + ) + compose_path: Mapped[str | None] = mapped_column( + String(1024), nullable=True + ) + url: Mapped[str | None] = mapped_column( + String(1024), nullable=True + ) + public_url: Mapped[str | None] = mapped_column( + String(1024), nullable=True + ) + tunnel_id: Mapped[str | None] = mapped_column( + String(255), nullable=True + ) + port: Mapped[int | None] = mapped_column( + Integer, nullable=True + ) last_started_at: Mapped[datetime | None] = mapped_column( DateTime(timezone=True), nullable=True ) last_stopped_at: Mapped[datetime | None] = mapped_column( DateTime(timezone=True), nullable=True ) - manifest_compiled_at: Mapped[datetime | None] = mapped_column( - DateTime(timezone=True), nullable=True - ) - image_tag: Mapped[str | None] = mapped_column(String(256), nullable=True) - probe_result: Mapped[dict | None] = mapped_column(JSON, nullable=True) - clone_mode: Mapped[str] = mapped_column(String(20), nullable=False, default="mount") - branch: Mapped[str | None] = mapped_column( - String(255), nullable=True, default="main" - ) - selected_config_profile_id: Mapped[uuid.UUID | None] = mapped_column( + selected_profile_id: Mapped[uuid.UUID | None] = mapped_column( UUID(), ForeignKey("config_profiles.id", ondelete="SET NULL"), nullable=True ) - ssh_key_ids: Mapped[list[str] | None] = mapped_column(JSON, nullable=True) - workspace_id: Mapped[uuid.UUID | None] = mapped_column( - UUID(), ForeignKey("workspaces.id", ondelete="SET NULL"), nullable=True - ) tool_type: Mapped["ToolType"] = relationship() - workspace: Mapped["Workspace | None"] = relationship() repository: Mapped["GitRepository"] = relationship() project: Mapped["Project"] = relationship() owner: Mapped["User"] = relationship() - selected_config_profile: Mapped["ConfigProfile | None"] = relationship() + selected_profile: Mapped["ConfigProfile | None"] = relationship() diff --git a/apps/api/src/models/user_config.py b/apps/api/src/models/user_config.py index 169de24..fae0c1a 100644 --- a/apps/api/src/models/user_config.py +++ b/apps/api/src/models/user_config.py @@ -18,3 +18,23 @@ class UserConfig(UUIDPrimaryKeyMixin, TimestampMixin, Base): config: Mapped[dict[str, object]] = mapped_column(JSON, default=dict, nullable=False) user: Mapped["User"] = relationship(back_populates="user_config") + + @property + def default_profile_id(self) -> uuid.UUID | None: + profile_id = self.config.get("default_profile_id") + return uuid.UUID(profile_id) if profile_id else None + + @default_profile_id.setter + def default_profile_id(self, value: uuid.UUID | None) -> None: + if value is not None: + self.config["default_profile_id"] = str(value) + elif "default_profile_id" in self.config: + del self.config["default_profile_id"] + + @property + def default_profiles(self) -> dict[str, str]: + return self.config.get("default_profiles", {}) + + @default_profiles.setter + def default_profiles(self, value: dict[str, str]) -> None: + self.config["default_profiles"] = value diff --git a/apps/api/src/schemas/__init__.py b/apps/api/src/schemas/__init__.py new file mode 100644 index 0000000..00969af --- /dev/null +++ b/apps/api/src/schemas/__init__.py @@ -0,0 +1 @@ +"""Pydantic request/response schemas.""" diff --git a/apps/api/src/schemas/config_folder.py b/apps/api/src/schemas/config_folder.py new file mode 100644 index 0000000..51d6af5 --- /dev/null +++ b/apps/api/src/schemas/config_folder.py @@ -0,0 +1,44 @@ +"""Config folder request/response schemas.""" + +import uuid + +from pydantic import BaseModel, Field + + +class ConfigFolderCreate(BaseModel): + name: str = Field(description="Folder name") + description: str | None = Field(default=None, description="Optional description") + mount_path: str = Field(description="Mount path in container") + files: dict[str, str] | None = Field( + default=None, description="Files as {path: content}" + ) + is_active: bool = Field(default=True, description="Whether folder is active") + + +class ConfigFolderUpdate(BaseModel): + name: str | None = None + description: str | None = None + mount_path: str | None = None + files: dict[str, str] | None = None + is_active: bool | None = None + + +class ProjectOverrideCreate(BaseModel): + project_id: str = Field(description="Project ID to override for") + mount_path: str | None = Field(default=None, description="Override mount path") + files: dict[str, str] | None = Field( + default=None, description="Override files" + ) + is_active: bool | None = Field(default=None, description="Override active state") + + +class ConfigFolderResponse(BaseModel): + id: str + user_id: str + name: str + description: str | None + mount_path: str + files: dict[str, str] | None + is_active: bool + created_at: str + updated_at: str diff --git a/apps/api/src/schemas/config_profile.py b/apps/api/src/schemas/config_profile.py new file mode 100644 index 0000000..5a6579a --- /dev/null +++ b/apps/api/src/schemas/config_profile.py @@ -0,0 +1,131 @@ +"""Config profile request/response schemas.""" + +from typing import Any + +from pydantic import BaseModel, Field, field_validator + +MAX_MOUNT_PATH_LENGTH = 1024 + + +class ConfigProfileCreate(BaseModel): + name: str = Field(description="Profile name (unique per user)") + description: str | None = Field(default=None, description="Optional description") + + @field_validator("name") + @classmethod + def validate_name(cls, v: str) -> str: + v = v.strip() + if not v: + raise ValueError("Profile name cannot be empty") + if len(v) > 255: + raise ValueError("Profile name must be 255 characters or less") + return v + + +class ConfigProfileUpdate(BaseModel): + name: str | None = Field(default=None, description="Profile name") + description: str | None = Field(default=None, description="Optional description") + + @field_validator("name") + @classmethod + def validate_name(cls, v: str | None) -> str | None: + if v is None: + return v + v = v.strip() + if not v: + raise ValueError("Profile name cannot be empty") + if len(v) > 255: + raise ValueError("Profile name must be 255 characters or less") + return v + + +class ConfigProfileResponse(BaseModel): + id: str + user_id: str + name: str + description: str | None + created_at: str + updated_at: str + + +class ConfigProfileDetailResponse(ConfigProfileResponse): + includes: list[dict[str, Any]] + mounts: list[dict[str, Any]] + + +class ConfigIncludeCreate(BaseModel): + included_profile_id: str = Field(description="UUID of the profile to include") + order_index: int = Field(default=0, description="Order index for include resolution") + + +class ConfigIncludeUpdate(BaseModel): + order_index: int = Field(description="Order index for include resolution") + + +class ConfigIncludeResponse(BaseModel): + id: str + profile_id: str + included_profile_id: str + included_profile_name: str | None + order_index: int + created_at: str + updated_at: str + + +class ConfigMountCreate(BaseModel): + target_path: str = Field(description="Absolute target path in container") + mode: str = Field(default="rw", description="Mount mode (rw or ro)") + files: dict[str, str] | None = Field( + default=None, description="Files as {path: content}" + ) + order_index: int = Field(default=0, description="Order index for mount resolution") + + @field_validator("target_path") + @classmethod + def validate_target_path(cls, v: str) -> str: + if not v.startswith("/"): + raise ValueError("Target path must be absolute (start with /)") + if ".." in v: + raise ValueError("Target path cannot contain parent directory references (..)") + if len(v) > MAX_MOUNT_PATH_LENGTH: + raise ValueError(f"Target path must be {MAX_MOUNT_PATH_LENGTH} characters or less") + return v + + +class ConfigMountUpdate(BaseModel): + target_path: str | None = Field(default=None, description="Absolute target path in container") + mode: str | None = Field(default=None, description="Mount mode (rw or ro)") + files: dict[str, str] | None = Field( + default=None, description="Files as {path: content}" + ) + order_index: int | None = Field(default=None, description="Order index for mount resolution") + + @field_validator("target_path") + @classmethod + def validate_target_path(cls, v: str | None) -> str | None: + if v is None: + return v + if not v.startswith("/"): + raise ValueError("Target path must be absolute (start with /)") + if ".." in v: + raise ValueError("Target path cannot contain parent directory references (..)") + if len(v) > MAX_MOUNT_PATH_LENGTH: + raise ValueError(f"Target path must be {MAX_MOUNT_PATH_LENGTH} characters or less") + return v + + +class ConfigMountResponse(BaseModel): + id: str + profile_id: str + target_path: str + mode: str + files: dict[str, str] | None + order_index: int + created_at: str + updated_at: str + + +class DefaultProfilesUpdate(BaseModel): + default_profiles: dict[str, str] = Field( + description="Mapping of tool_type_id to profile_id" + ) diff --git a/apps/api/src/schemas/git_repository.py b/apps/api/src/schemas/git_repository.py new file mode 100644 index 0000000..05672af --- /dev/null +++ b/apps/api/src/schemas/git_repository.py @@ -0,0 +1,129 @@ +"""Git repository request/response schemas.""" + +import uuid +from datetime import datetime + +from pydantic import BaseModel, ConfigDict + + +class GitRepositoryCreate(BaseModel): + name: str + remote_url: str | None = None + force_original_url: bool = False + + +class URLParseRequest(BaseModel): + url: str + + +class URLParseResponse(BaseModel): + original_url: str + base_url: str | None + is_valid_clone_url: bool + needs_parsing: bool + host: str | None + message: str + error_code: str | None + + +class GitRepositoryResponse(BaseModel): + model_config = ConfigDict(from_attributes=True) + + id: uuid.UUID + name: str + path: str + project_id: uuid.UUID + owner_id: uuid.UUID + is_mirror: bool + remote_url: str | None + last_push: datetime | None + created_at: datetime + updated_at: datetime + + +class FileListResponse(BaseModel): + path: str + branch: str + entries: list[dict] + + +class FileContentResponse(BaseModel): + path: str + branch: str + content: str + size: int + encoding: str + language: str | None + is_binary: bool + last_commit: dict | None + + +class BranchesResponse(BaseModel): + branches: list[dict] + default_branch: str + + +class FileUpdateRequest(BaseModel): + path: str + branch: str + content: str + commit_message: str + + +class FileUpdateResponse(BaseModel): + commit_hash: str + message: str + branch: str + + +class StatusResponse(BaseModel): + branch: str + modified: list[str] + added: list[str] + deleted: list[str] + untracked: list[str] + renamed: list[str] + ahead: int + behind: int + + +class BranchCreateRequest(BaseModel): + name: str + base_branch: str = "HEAD" + + +class CheckoutRequest(BaseModel): + branch: str + + +class CommitRequest(BaseModel): + message: str + files: list[str] | None = None + + +class CommitResponse(BaseModel): + commit_hash: str + message: str + + +class FetchResponse(BaseModel): + message: str + + +class PullResponse(BaseModel): + message: str + + +class PushResponse(BaseModel): + message: str + + +class MergeRequest(BaseModel): + source_branch: str + target_branch: str | None = None + message: str | None = None + + +class MergeResponse(BaseModel): + commit_hash: str + message: str diff --git a/apps/api/src/schemas/health.py b/apps/api/src/schemas/health.py new file mode 100644 index 0000000..4aab904 --- /dev/null +++ b/apps/api/src/schemas/health.py @@ -0,0 +1,50 @@ +"""Health check response schemas.""" + +from pydantic import BaseModel, Field + + +class DatabaseHealth(BaseModel): + """Database health check result.""" + + status: str = Field(description="Database health status", examples=["healthy"]) + response_time_ms: float = Field( + description="Query response time in milliseconds", examples=[5.2] + ) + + +class DiskHealth(BaseModel): + """Disk space health check result.""" + + status: str = Field(description="Disk health status", examples=["healthy"]) + free_gb: float = Field(description="Free disk space in GB", examples=[45.2]) + total_gb: float = Field(description="Total disk space in GB", examples=[100.0]) + + +class HealthChecks(BaseModel): + """Individual health checks.""" + + database: DatabaseHealth | None = None + disk: DiskHealth | None = None + + +class HealthResponse(BaseModel): + """Overall health check response.""" + + status: str = Field(description="Overall health status", examples=["healthy"]) + timestamp: str = Field( + description="ISO 8601 timestamp", examples=["2026-05-19T12:00:00Z"] + ) + version: str = Field(description="API version", examples=["0.1.0"]) + checks: HealthChecks = Field(description="Individual health checks") + uptime_seconds: float = Field( + description="Server uptime in seconds", examples=[3600.0] + ) + + +class DatabaseHealthResponse(BaseModel): + """Database-specific health check response.""" + + status: str = Field(description="Database health status", examples=["healthy"]) + response_time_ms: float = Field( + description="Query response time in milliseconds", examples=[5.2] + ) diff --git a/apps/api/src/schemas/project.py b/apps/api/src/schemas/project.py new file mode 100644 index 0000000..fb80dbf --- /dev/null +++ b/apps/api/src/schemas/project.py @@ -0,0 +1,25 @@ +"""Project request/response schemas.""" + +from pydantic import BaseModel + + +class ProjectCreate(BaseModel): + name: str + description: str | None = None + + +class ProjectUpdate(BaseModel): + name: str | None = None + description: str | None = None + + +class ProjectResponse(BaseModel): + id: str + name: str + description: str | None + created_at: str + updated_at: str + + +class SetDefaultSSHKeyRequest(BaseModel): + ssh_key_id: str diff --git a/apps/api/src/schemas/ssh_key.py b/apps/api/src/schemas/ssh_key.py new file mode 100644 index 0000000..33e1d41 --- /dev/null +++ b/apps/api/src/schemas/ssh_key.py @@ -0,0 +1,16 @@ +"""SSH key request/response schemas.""" + +from pydantic import BaseModel + + +class SSHKeyCreate(BaseModel): + name: str + public_key: str + + +class SSHKeyResponse(BaseModel): + id: str + name: str + public_key: str + fingerprint: str + created_at: str diff --git a/apps/api/src/schemas/tool_config.py b/apps/api/src/schemas/tool_config.py new file mode 100644 index 0000000..3fc2781 --- /dev/null +++ b/apps/api/src/schemas/tool_config.py @@ -0,0 +1,47 @@ +"""Tool config request/response schemas.""" + +from pydantic import BaseModel, Field + + +class ToolConfigCreate(BaseModel): + tool_type_id: str = Field(description="UUID of the tool type") + key: str = Field(description="Configuration key") + value: str = Field(description="Configuration value") + config_type: str = Field(default="env", description="Config type: env or file") + file_path: str | None = Field(default=None, description="File path for file configs") + port_override: int | None = Field(default=None, description="Port override") + start_command: str | None = Field(default=None, description="Start command override") + working_directory: str | None = Field(default=None, description="Working directory") + environment_variables: dict[str, str] | None = Field( + default=None, description="Additional environment variables" + ) + volumes: list[dict] | None = Field(default=None, description="Volume mounts") + + +class ToolConfigUpdate(BaseModel): + value: str | None = None + config_type: str | None = None + file_path: str | None = None + port_override: int | None = None + start_command: str | None = None + working_directory: str | None = None + environment_variables: dict[str, str] | None = None + volumes: list[dict] | None = None + + +class ToolConfigResponse(BaseModel): + id: str + tool_type_id: str + user_id: str + project_id: str | None + key: str + value: str + config_type: str + file_path: str | None + port_override: int | None + start_command: str | None + working_directory: str | None + environment_variables: dict[str, str] | None + volumes: list[dict] | None + created_at: str + updated_at: str diff --git a/apps/api/src/schemas/tool_instance.py b/apps/api/src/schemas/tool_instance.py new file mode 100644 index 0000000..09454d7 --- /dev/null +++ b/apps/api/src/schemas/tool_instance.py @@ -0,0 +1,41 @@ +"""Tool instance request/response schemas.""" + +from pydantic import BaseModel, Field + + +class CreateInstanceRequest(BaseModel): + """Request body for creating a tool instance.""" + + model_config = {"extra": "ignore"} + + tool_type_id: str = Field(description="UUID of the tool type to instantiate") + display_name: str | None = Field( + default=None, description="Optional display name for the instance" + ) + config_profile_id: str | None = Field( + default=None, description="Optional config profile ID to apply to the instance" + ) + + +class SessionItemResponse(BaseModel): + """Lightweight session summary for sidebar and dashboard.""" + + model_config = {"extra": "ignore"} + + id: str = Field(description="Session (tool instance) ID") + display_name: str = Field(description="Display name of the session") + tool_type_name: str = Field(description="Name of the tool type") + tool_icon: str | None = Field(default=None, description="Icon URL for the tool type") + tool_type_interfaces: list[str] = Field(default_factory=list, description="Supported interfaces") + repository_name: str = Field(description="Name of the repository") + repository_id: str = Field(description="Repository ID") + project_name: str = Field(description="Name of the project") + project_id: str = Field(description="Project ID") + status: str = Field(description="Current status") + url: str | None = Field(default=None, description="Access URL") + + +class SessionListResponse(BaseModel): + """Response wrapping a list of session summaries.""" + + sessions: list[SessionItemResponse] diff --git a/apps/api/src/schemas/tool_type.py b/apps/api/src/schemas/tool_type.py new file mode 100644 index 0000000..ff449aa --- /dev/null +++ b/apps/api/src/schemas/tool_type.py @@ -0,0 +1,204 @@ +"""Tool type request/response schemas.""" + +import uuid +from datetime import datetime + +import yaml +from pydantic import BaseModel, ConfigDict, field_validator, model_validator + + +class ToolTypeCreate(BaseModel): + name: str + display_name: str + description: str | None = None + default_port: int + definition_type: str = "compose" + compose_template: str | None = None + dockerfile_template: str | None = None + build_context: dict | None = None + readiness_probe: dict | None = None + required_variables: list[str] = [] + category: str = "other" + interfaces: list[str] = ["web"] + + @field_validator("definition_type") + @classmethod + def validate_definition_type(cls, v: str) -> str: + if v not in ("compose", "dockerfile"): + raise ValueError("definition_type must be 'compose' or 'dockerfile'") + return v + + @field_validator("compose_template") + @classmethod + def validate_compose_template(cls, v: str | None, info) -> str | None: + data = info.data + if data.get("definition_type") != "compose": + return v + if v is None: + raise ValueError("compose_template is required when definition_type is 'compose'") + try: + parsed = yaml.safe_load(v) + except yaml.YAMLError as e: + raise ValueError(f"Invalid YAML: {e}") + if not isinstance(parsed, dict): + raise ValueError("Compose template must be a YAML mapping") + if "services" not in parsed: + raise ValueError("Compose template must contain 'services' key") + if not parsed["services"]: + raise ValueError("Compose template must define at least one service") + return v + + @field_validator("dockerfile_template") + @classmethod + def validate_dockerfile_template(cls, v: str | None, info) -> str | None: + data = info.data + if data.get("definition_type") != "dockerfile": + return v + if v is None: + raise ValueError("dockerfile_template is required when definition_type is 'dockerfile'") + if not v.strip().startswith("FROM"): + raise ValueError("Dockerfile must start with a FROM instruction") + return v + + @field_validator("default_port") + @classmethod + def validate_default_port(cls, v: int, info) -> int: + if v <= 0 or v > 65535: + raise ValueError("Port must be between 1 and 65535") + data = info.data + if data.get("definition_type") != "compose": + return v + template = data.get("compose_template") + if not template: + return v + try: + parsed = yaml.safe_load(template) + except yaml.YAMLError: + return v + port_str = str(v) + port_exposed = False + if isinstance(parsed, dict) and "services" in parsed: + for service_config in parsed["services"].values(): + if isinstance(service_config, dict) and "ports" in service_config: + for port_mapping in service_config["ports"]: + if isinstance(port_mapping, str) and port_str in port_mapping: + port_exposed = True + break + elif isinstance(port_mapping, int) and port_mapping == v: + port_exposed = True + break + if port_exposed: + break + if not port_exposed: + raise ValueError(f"Port {v} is not exposed in the compose template. Add it to the 'ports' section.") + return v + + @field_validator("required_variables") + @classmethod + def validate_required_variables(cls, v: list[str], info) -> list[str]: + if not v: + return v + data = info.data + if data.get("definition_type") != "compose": + return v + template = data.get("compose_template") + if not template: + return v + for var in v: + placeholder = f"{{{{{var}}}}}" + if placeholder not in template: + raise ValueError(f"Required variable '{var}' not found in compose template") + return v + + @model_validator(mode="after") + def validate_templates(self) -> "ToolTypeCreate": + if self.definition_type == "dockerfile" and self.dockerfile_template is None: + raise ValueError("dockerfile_template is required when definition_type is 'dockerfile'") + if self.definition_type == "compose" and self.compose_template is None: + raise ValueError("compose_template is required when definition_type is 'compose'") + return self + + +class ToolTypeUpdate(BaseModel): + display_name: str | None = None + description: str | None = None + default_port: int | None = None + definition_type: str | None = None + compose_template: str | None = None + dockerfile_template: str | None = None + build_context: dict | None = None + readiness_probe: dict | None = None + required_variables: list[str] | None = None + category: str | None = None + interfaces: list[str] | None = None + + @field_validator("definition_type") + @classmethod + def validate_definition_type(cls, v: str | None) -> str | None: + if v is None: + return v + if v not in ("compose", "dockerfile"): + raise ValueError("definition_type must be 'compose' or 'dockerfile'") + return v + + @field_validator("compose_template") + @classmethod + def validate_compose_template(cls, v: str | None, info) -> str | None: + if v is None: + return v + data = info.data + definition_type = data.get("definition_type") + if definition_type and definition_type != "compose": + return v + try: + parsed = yaml.safe_load(v) + except yaml.YAMLError as e: + raise ValueError(f"Invalid YAML: {e}") + if not isinstance(parsed, dict): + raise ValueError("Compose template must be a YAML mapping") + if "services" not in parsed: + raise ValueError("Compose template must contain 'services' key") + if not parsed["services"]: + raise ValueError("Compose template must define at least one service") + return v + + @field_validator("dockerfile_template") + @classmethod + def validate_dockerfile_template(cls, v: str | None, info) -> str | None: + if v is None: + return v + data = info.data + definition_type = data.get("definition_type") + if definition_type and definition_type != "dockerfile": + return v + if not v.strip().startswith("FROM"): + raise ValueError("Dockerfile must start with a FROM instruction") + return v + + +class ToolTypeResponse(BaseModel): + model_config = ConfigDict(from_attributes=True) + + id: uuid.UUID + name: str + display_name: str + description: str | None + category: str + interfaces: list[str] + default_port: int + definition_type: str + compose_template: str | None + dockerfile_template: str | None + build_context: dict | None + readiness_probe: dict | None + required_variables: list[str] + is_builtin: bool + created_by_id: uuid.UUID | None + created_at: datetime + updated_at: datetime + + +class ToolTypeValidateRequest(BaseModel): + definition_type: str + compose_template: str | None = None + dockerfile_template: str | None = None diff --git a/apps/api/src/schemas/user.py b/apps/api/src/schemas/user.py new file mode 100644 index 0000000..d890683 --- /dev/null +++ b/apps/api/src/schemas/user.py @@ -0,0 +1,19 @@ +"""User request/response schemas.""" + +import uuid + +from pydantic import BaseModel, ConfigDict + + +class UserProfileResponse(BaseModel): + model_config = ConfigDict(from_attributes=True) + + id: uuid.UUID + email: str + name: str + avatar_url: str | None + + +class UserProfileUpdate(BaseModel): + name: str | None = None + email: str | None = None diff --git a/apps/api/src/schemas/user_config.py b/apps/api/src/schemas/user_config.py new file mode 100644 index 0000000..a013d47 --- /dev/null +++ b/apps/api/src/schemas/user_config.py @@ -0,0 +1,21 @@ +"""User config request/response schemas.""" + +from pydantic import BaseModel, ConfigDict + + +class UserConfigResponse(BaseModel): + model_config = ConfigDict(from_attributes=True) + + default_editor: str | None = None + theme: str = "system" + git_user_name: str | None = None + git_user_email: str | None = None + last_session_id: str | None = None + + +class UserConfigUpdate(BaseModel): + default_editor: str | None = None + theme: str | None = None + git_user_name: str | None = None + git_user_email: str | None = None + last_session_id: str | None = None diff --git a/apps/api/src/seeds/__init__.py b/apps/api/src/seeds/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/api/src/seeds/builtin_tool_types.py b/apps/api/src/seeds/builtin_tool_types.py new file mode 100644 index 0000000..023b2d1 --- /dev/null +++ b/apps/api/src/seeds/builtin_tool_types.py @@ -0,0 +1,161 @@ +import logging + +from sqlalchemy import select + +from src.database import SessionLocal +from src.models.tool_type import ToolType + +logger = logging.getLogger(__name__) + + +async def _table_exists(session, table_name: str) -> bool: + """Check if a table exists in the database.""" + from sqlalchemy import text + + try: + result = await session.execute( + text( + """ + SELECT EXISTS ( + SELECT FROM information_schema.tables + WHERE table_schema = 'public' + AND table_name = :table_name + ) + """ + ), + {"table_name": table_name}, + ) + return result.scalar() or False + except Exception: + return False + + +async def seed_builtin_tool_types(): + async with SessionLocal() as session: + # Check if tool_types table exists before attempting to seed + if not await _table_exists(session, "tool_types"): + logger.warning( + "tool_types table does not exist. Skipping seeding. " + "Migrations may not have run yet." + ) + return + + builtin_types = [ + { + "name": "code-server", + "display_name": "VS Code Server", + "description": "VS Code running in the browser via code-server", + "category": "editor", + "interfaces": ["web"], + "compose_template": """version: "3.8" +services: + code-server: + image: lscr.io/linuxserver/code-server:latest + container_name: {{TOOL_NAME}} + environment: + - PUID=1000 + - PGID=1000 + - TZ=Europe/London + volumes: + - {{REPO_PATH}}:/config/workspace + ports: + - "8443:8443" + restart: unless-stopped""", + "default_port": 8443, + "required_variables": ["REPO_PATH", "TOOL_NAME"], + }, + { + "name": "jupyter-notebook", + "display_name": "Jupyter Notebook", + "description": "Jupyter Lab for interactive development", + "category": "notebook", + "interfaces": ["web"], + "default_port": 8888, + "compose_template": """version: "3.8" +services: + jupyter: + image: jupyter/scipy-notebook:latest + container_name: {{TOOL_NAME}} + environment: + - JUPYTER_ENABLE_LAB=yes + volumes: + - {{REPO_PATH}}:/home/jovyan/work + ports: + - "8888:8888" + restart: unless-stopped""", + "required_variables": ["REPO_PATH", "TOOL_NAME"], + }, + { + "name": "opencode", + "display_name": "OpenCode", + "description": "AI coding assistant - run opencode in terminal", + "category": "ai-assistant", + "interfaces": ["terminal"], + "default_port": 3000, + "compose_template": """version: "3.8" +services: + opencode: + image: node:20-slim + container_name: {{TOOL_NAME}} + working_dir: /workspace + environment: + - HOME=/tmp + volumes: + - {{REPO_PATH}}:/workspace + - opencode_home:/tmp + ports: + - "3000:3000" + command: > + sh -c "set -x && + apt-get update && apt-get install -y git ca-certificates && + echo 'Installing opencode...' && + npm install -g opencode-ai 2>&1 || echo 'ERROR: npm install failed' && + which opencode || echo 'ERROR: opencode not in PATH' && + npm bin -g && + ls -la $(npm bin -g) || echo 'ERROR: global bin dir not found' && + echo 'export PATH=\"$(npm bin -g):\\$PATH\"' >> /root/.bashrc && + echo 'cd /workspace' >> /root/.bashrc && + echo 'OpenCode installation complete' && + cd /workspace && + exec tail -f /dev/null" + stdin_open: true + tty: true + restart: unless-stopped + +volumes: + opencode_home:""", + "required_variables": ["REPO_PATH", "TOOL_NAME"], + }, + ] + + for tool_data in builtin_types: + existing = await session.scalar(select(ToolType).where(ToolType.name == tool_data["name"])) + if not existing: + tool_type = ToolType( + name=tool_data["name"], + display_name=tool_data["display_name"], + description=tool_data["description"], + category=tool_data["category"], + interfaces=tool_data["interfaces"], + definition_type="compose", + compose_template=tool_data["compose_template"], + required_variables=tool_data["required_variables"], + default_port=tool_data.get("default_port"), + is_builtin=True, + ) + session.add(tool_type) + logger.info("Created built-in tool type: %s", tool_data["name"]) + else: + # Update existing built-in tool types to reflect code changes + existing.display_name = tool_data["display_name"] + existing.description = tool_data["description"] + existing.category = tool_data["category"] + existing.interfaces = tool_data["interfaces"] + existing.definition_type = "compose" + existing.compose_template = tool_data["compose_template"] + existing.required_variables = tool_data["required_variables"] + existing.default_port = tool_data.get("default_port") + logger.info("Updated built-in tool type: %s", tool_data["name"]) + + await session.commit() + logger.info("Built-in tool types seeded successfully.") diff --git a/apps/api/src/services/config_profiles.py b/apps/api/src/services/config_profiles.py new file mode 100644 index 0000000..daba56c --- /dev/null +++ b/apps/api/src/services/config_profiles.py @@ -0,0 +1,299 @@ +"""Config profile business logic.""" + +import logging +import uuid + +from fastapi import HTTPException, status +from sqlalchemy import select +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy.orm import selectinload + +from src.models.config_include import ConfigInclude +from src.models.config_mount import ConfigMount +from src.models.config_profile import ConfigProfile +from src.models.tool_type import ToolType +from src.models.user_config import UserConfig + +logger = logging.getLogger(__name__) + +MAX_INCLUDES_DEPTH = 10 + + +async def get_owned_profile( + profile_id: uuid.UUID, + user_id: uuid.UUID, + session: AsyncSession, +) -> ConfigProfile: + """Fetch a config profile and verify ownership.""" + profile = await session.get(ConfigProfile, profile_id) + if profile is None or profile.user_id != user_id: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="config profile not found", + ) + return profile + + +async def _detect_cycle( + session: AsyncSession, + profile_id: uuid.UUID, + visited: set[uuid.UUID] | None = None, + depth: int = 0, +) -> bool: + """Detect cycles in profile includes using DFS. + + Returns True if a cycle is detected. + """ + if depth > MAX_INCLUDES_DEPTH: + return True + + if visited is None: + visited = set() + + if profile_id in visited: + return True + + visited.add(profile_id) + + result = await session.execute( + select(ConfigInclude.included_profile_id).where( + ConfigInclude.profile_id == profile_id + ) + ) + included_ids = result.scalars().all() + + for included_id in included_ids: + if await _detect_cycle(session, included_id, visited.copy(), depth + 1): + return True + + return False + + +async def validate_includes_no_cycle( + session: AsyncSession, + profile_id: uuid.UUID, + new_included_id: uuid.UUID | None = None, +) -> None: + """Validate that adding an include wouldn't create a cycle.""" + if new_included_id and await _detect_cycle(session, new_included_id, {profile_id}): + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="adding this include would create a circular reference", + ) + + +# --------------------------------------------------------------------------- +# Profile CRUD helpers +# --------------------------------------------------------------------------- + +async def check_duplicate_name( + session: AsyncSession, + user_id: uuid.UUID, + name: str, + exclude_id: uuid.UUID | None = None, +) -> None: + """Raise 409 if a profile with the given name already exists.""" + query = select(ConfigProfile).where( + ConfigProfile.user_id == user_id, + ConfigProfile.name == name, + ) + if exclude_id: + query = query.where(ConfigProfile.id != exclude_id) + existing = await session.scalar(query) + if existing: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, + detail=f"config profile with name '{name}' already exists", + ) + + +def profile_to_dict(profile: ConfigProfile) -> dict: + """Serialize a ConfigProfile to a dict.""" + return { + "id": str(profile.id), + "user_id": str(profile.user_id), + "name": profile.name, + "description": profile.description, + "created_at": profile.created_at.isoformat() if profile.created_at else None, + "updated_at": profile.updated_at.isoformat() if profile.updated_at else None, + } + + +# --------------------------------------------------------------------------- +# Include helpers +# --------------------------------------------------------------------------- + +async def check_duplicate_include( + session: AsyncSession, + profile_id: uuid.UUID, + included_profile_id: uuid.UUID, +) -> None: + """Raise 409 if the include already exists.""" + existing = await session.scalar( + select(ConfigInclude).where( + ConfigInclude.profile_id == profile_id, + ConfigInclude.included_profile_id == included_profile_id, + ) + ) + if existing: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, + detail="this include already exists", + ) + + +def include_to_dict(inc: ConfigInclude, included_name: str | None) -> dict: + """Serialize a ConfigInclude to a dict.""" + return { + "id": str(inc.id), + "profile_id": str(inc.profile_id), + "included_profile_id": str(inc.included_profile_id), + "included_profile_name": included_name, + "order_index": inc.order_index, + "created_at": inc.created_at.isoformat() if inc.created_at else None, + "updated_at": inc.updated_at.isoformat() if inc.updated_at else None, + } + + +# --------------------------------------------------------------------------- +# Mount helpers +# --------------------------------------------------------------------------- + +async def check_duplicate_mount_path( + session: AsyncSession, + profile_id: uuid.UUID, + target_path: str, + exclude_id: uuid.UUID | None = None, +) -> None: + """Raise 409 if a mount with the given path already exists.""" + query = select(ConfigMount).where( + ConfigMount.profile_id == profile_id, + ConfigMount.target_path == target_path, + ) + if exclude_id: + query = query.where(ConfigMount.id != exclude_id) + existing = await session.scalar(query) + if existing: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, + detail=f"mount with path '{target_path}' already exists", + ) + + +def mount_to_dict(mount: ConfigMount) -> dict: + """Serialize a ConfigMount to a dict.""" + return { + "id": str(mount.id), + "profile_id": str(mount.profile_id), + "target_path": mount.target_path, + "files": mount.files, + "mode": mount.mode, + "order_index": mount.order_index, + "created_at": mount.created_at.isoformat() if mount.created_at else None, + "updated_at": mount.updated_at.isoformat() if mount.updated_at else None, + } + + +# --------------------------------------------------------------------------- +# Default profile helpers +# --------------------------------------------------------------------------- + +async def get_or_create_user_config( + session: AsyncSession, + user_id: uuid.UUID, +) -> UserConfig: + """Get existing user config or create a new one.""" + result = await session.execute(select(UserConfig).where(UserConfig.user_id == user_id)) + user_config = result.scalar_one_or_none() + if user_config is None: + user_config = UserConfig(user_id=user_id, config={}) + session.add(user_config) + return user_config + + +async def validate_default_profiles( + session: AsyncSession, + user_id: uuid.UUID, + default_profiles: dict[str, str], +) -> None: + """Validate that all profile IDs in default_profiles belong to the user.""" + for tool_type_id, profile_id_str in default_profiles.items(): + profile = await session.get(ConfigProfile, uuid.UUID(profile_id_str)) + if profile is None: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"profile {profile_id_str} not found") + if profile.user_id != user_id: + raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=f"profile {profile_id_str} does not belong to user") + + +async def get_default_profiles( + session: AsyncSession, + user_id: uuid.UUID, +) -> dict: + """Get default profiles for a user.""" + result = await session.execute(select(UserConfig).where(UserConfig.user_id == user_id)) + user_config = result.scalar_one_or_none() + return {"default_profiles": user_config.default_profiles if user_config else {}} + + +async def set_default_profiles( + session: AsyncSession, + user_id: uuid.UUID, + default_profiles: dict[str, str], +) -> dict: + """Set default profiles for a user.""" + user_config = await get_or_create_user_config(session, user_id) + await validate_default_profiles(session, user_id, default_profiles) + user_config.config = {**user_config.config, "default_profiles": default_profiles} + await session.commit() + await session.refresh(user_config) + return {"default_profiles": user_config.default_profiles} + + +async def get_default_profile_for_tool_type( + session: AsyncSession, + user_id: uuid.UUID, + tool_type_id: str, +) -> dict: + """Get default profile for a specific tool type.""" + result = await session.execute(select(UserConfig).where(UserConfig.user_id == user_id)) + user_config = result.scalar_one_or_none() + profile_id = user_config.default_profiles.get(tool_type_id) if user_config else None + return {"tool_type_id": tool_type_id, "profile_id": profile_id} + + +# --------------------------------------------------------------------------- +# Include list helper +# --------------------------------------------------------------------------- + +async def list_includes_for_profile( + session: AsyncSession, + profile_id: uuid.UUID, +) -> dict: + """List all includes for a profile.""" + result = await session.execute( + select(ConfigInclude) + .where(ConfigInclude.profile_id == profile_id) + .order_by(ConfigInclude.order_index) + ) + includes_data = [] + for inc in result.scalars().all(): + included_profile = await session.get(ConfigProfile, inc.included_profile_id) + includes_data.append(include_to_dict(inc, included_profile.name if included_profile else None)) + return {"includes": includes_data} + + +# --------------------------------------------------------------------------- +# Mount list helper +# --------------------------------------------------------------------------- + +async def list_mounts_for_profile( + session: AsyncSession, + profile_id: uuid.UUID, +) -> dict: + """List all mounts for a profile.""" + result = await session.execute( + select(ConfigMount) + .where(ConfigMount.profile_id == profile_id) + .order_by(ConfigMount.order_index) + ) + return {"mounts": [mount_to_dict(m) for m in result.scalars().all()]} diff --git a/apps/api/src/services/docker.py b/apps/api/src/services/docker.py deleted file mode 100644 index 5e5a606..0000000 --- a/apps/api/src/services/docker.py +++ /dev/null @@ -1,484 +0,0 @@ -"""Docker service for managing tool instances.""" - -import logging -import subprocess -import time -from collections import Counter -from pathlib import Path -from typing import Any - -logger = logging.getLogger(__name__) - - -def sort_volumes_by_specificity(volumes: list[str]) -> list[str]: - """Sort volume strings so parent paths come before child paths. - - Docker Compose mounts volumes in array order. A later mount at a parent - path hides earlier mounts at child paths. By sorting shallow paths first - and deep paths last, deeper (more specific) mounts overlay correctly. - - Volume format: source:target or source:target:type - - Args: - volumes: List of Docker volume mount strings. - - Returns: - Sorted list with parent paths before child paths. - """ - - def _target_depth(vol: str) -> int: - parts = vol.split(":") - if len(parts) < 2: - return 0 - target = parts[1].rstrip("/") - if not target or target == "/": - return 0 - return target.count("/") - - # Detect duplicate targets and warn - targets = [] - for vol in volumes: - parts = vol.split(":") - targets.append(parts[1] if len(parts) > 1 else "") - dupes = [t for t, c in Counter(targets).items() if c > 1] - if dupes: - logger.warning("Duplicate mount targets detected: %s", dupes) - - # Stable sort: parent paths first, child paths last - return sorted(volumes, key=_target_depth) - - -def render_compose_template(template: str, variables: dict[str, Any]) -> str: - """Render a Docker Compose template with variable substitution. - - Args: - template: The compose template string - variables: Dictionary of variable names to values - - Returns: - Rendered compose file content - """ - result = template - for key, value in variables.items(): - placeholder = f"{{{{{key}}}}}" - result = result.replace(placeholder, str(value)) - return result - - -def ensure_instance_directory(instance_id: str, base_path: str | None = None) -> str: - """Create and return the instance directory path. - - Args: - instance_id: Unique instance identifier - base_path: Base directory for all instances (defaults to Settings.instance_base_path) - - Returns: - Absolute path to instance directory - """ - if base_path is None: - from src.config import Settings - - base_path = Settings().instance_base_path - instance_dir = Path(base_path) / instance_id - instance_dir.mkdir(parents=True, exist_ok=True) - return str(instance_dir.absolute()) - - -def write_compose_file(instance_dir: str, content: str) -> str: - """Write the rendered compose file to the instance directory. - - Args: - instance_dir: Path to instance directory - content: Rendered compose content - - Returns: - Path to the compose file - """ - compose_path = Path(instance_dir) / "docker-compose.yml" - compose_path.write_text(content) - return str(compose_path) - - -def write_env_file(instance_dir: str, env_vars: dict[str, str]) -> str: - """Write environment variables to a .env file. - - Args: - instance_dir: Path to instance directory - env_vars: Dictionary of env var names to values - - Returns: - Path to the env file - """ - env_path = Path(instance_dir) / ".env" - lines = [f'{key}="{value}"' for key, value in env_vars.items()] - env_path.write_text("\n".join(lines) + "\n") - return str(env_path) - - -def write_config_files(instance_dir: str, files: dict[str, str]) -> None: - """Write config files to the instance directory. - - Args: - instance_dir: Path to instance directory - files: Dictionary of file paths (relative to instance dir) to content - """ - instance_path = Path(instance_dir) - for file_path, content in files.items(): - # Ensure the path is within the instance directory (security) - full_path = instance_path / file_path - try: - full_path.resolve().relative_to(instance_path.resolve()) - except ValueError: - raise ValueError(f"File path '{file_path}' escapes instance directory") - - full_path.parent.mkdir(parents=True, exist_ok=True) - full_path.write_text(content) - - -def execute_compose_command( - compose_path: str, action: str, timeout: int = 60, env_file: str | None = None -) -> tuple[int, str, str]: - """Execute a docker compose command. - - Args: - compose_path: Path to docker-compose.yml - action: The compose action (up, down, start, stop, restart) - timeout: Command timeout in seconds - env_file: Optional path to .env file for environment variables - - Returns: - Tuple of (returncode, stdout, stderr) - """ - instance_dir = Path(compose_path).parent - - cmd = ["docker", "compose", "-f", compose_path] - - if env_file: - cmd.extend(["--env-file", env_file]) - - if action == "up": - cmd.extend(["up", "-d", "--force-recreate"]) - elif action == "down": - cmd.extend(["down", "-v"]) - elif action in ("start", "stop", "restart"): - cmd.append(action) - else: - raise ValueError(f"Unknown compose action: {action}") - - result = subprocess.run( - cmd, - cwd=str(instance_dir), - capture_output=True, - text=True, - timeout=timeout, - ) - - return result.returncode, result.stdout, result.stderr - - -def get_container_id(instance_name: str) -> str | None: - """Get the container ID for a compose service. - - Uses exact name matching to avoid substring collisions with tunnel - containers (e.g. tunnel-code-server-... matching code-server-...). - Falls back to case-insensitive matching since Docker DNS is case- - insensitive but docker inspect is case-sensitive. - - Args: - instance_name: The expected container name. - - Returns: - Container ID or None if not found. - """ - expected = instance_name.lower() - - # Fast path: exact match via docker inspect - result = subprocess.run( - ["docker", "inspect", "-f", "{{.Id}}", expected], - capture_output=True, - text=True, - ) - if result.returncode == 0 and result.stdout.strip(): - return result.stdout.strip() - - # Fallback: list all containers and do case-insensitive exact match - ps_result = subprocess.run( - ["docker", "ps", "-a", "--format", "{{.Names}}\t{{.ID}}"], - capture_output=True, - text=True, - ) - if ps_result.returncode == 0: - for line in ps_result.stdout.strip().splitlines(): - parts = line.split("\t") - if len(parts) == 2: - name, cid = parts - if name.lower() == expected: - return cid - return None - - -def get_container_name(instance_name: str) -> str | None: - """Get the full container name for a compose service. - - Uses exact name matching via docker inspect to avoid substring collisions. - - Args: - instance_name: The exact container name (case-insensitive for Docker). - - Returns: - Container name or None if not found. - """ - result = subprocess.run( - ["docker", "inspect", "-f", "{{.Name}}", instance_name.lower()], - capture_output=True, - text=True, - ) - if result.returncode == 0 and result.stdout.strip(): - return result.stdout.strip().lstrip("/") - return None - - -def get_backend_network_name() -> str: - """Auto-detect the actual Docker network name for the backend network. - - Docker Compose prefixes network names with the project directory name - (e.g. 'headquarter_backend' instead of 'backend'). We inspect the API - container itself to find the real network name it's connected to. - - Returns: - The actual Docker network name, or 'backend' as fallback. - """ - # Try to find the API container by its known name - api_container = "hq-api" - result = subprocess.run( - [ - "docker", - "inspect", - "-f", - "{{range $k, $v := .NetworkSettings.Networks}}{{$k}} {{end}}", - api_container, - ], - capture_output=True, - text=True, - ) - if result.returncode == 0 and result.stdout.strip(): - networks = result.stdout.strip().split() - for net in networks: - if "backend" in net.lower(): - return net - # API container is on some network — return the first one - return networks[0] - return "backend" - - -def connect_container_to_network( - container_name: str, network_name: str | None = None -) -> bool: - """Connect a Docker container to an existing network. - - Args: - container_name: Name or ID of the container - network_name: Name of the Docker network. If None, auto-detects - from the API container's own network membership. - - Returns: - True if successful, False otherwise - """ - if network_name is None: - network_name = get_backend_network_name() - result = subprocess.run( - ["docker", "network", "connect", network_name, container_name], - capture_output=True, - text=True, - ) - return result.returncode == 0 - - -def get_container_ip_on_network( - container_id: str, network_name: str | None = None -) -> str | None: - """Get a container's IP address on a specific Docker network. - - Args: - container_id: Docker container ID or name. - network_name: Network name. If None, auto-detects from the API container. - - Returns: - IP address string, or None if the container is not on that network. - """ - if network_name is None: - network_name = get_backend_network_name() - result = subprocess.run( - [ - "docker", - "inspect", - "-f", - f"{{{{.NetworkSettings.Networks.{network_name}.IPAddress}}}}", - container_id, - ], - capture_output=True, - text=True, - ) - if result.returncode == 0: - ip = result.stdout.strip() - if ip and ip != "": - return ip - return None - - -def is_container_on_network(container_id: str, network_name: str | None = None) -> bool: - """Check whether a container is already attached to a Docker network. - - Args: - container_id: Docker container ID or name. - network_name: Network name. If None, auto-detects from the API container. - - Returns: - True if the container is on the network. - """ - if network_name is None: - network_name = get_backend_network_name() - result = subprocess.run( - [ - "docker", - "inspect", - "-f", - f"{{{{.NetworkSettings.Networks.{network_name}}}}}", - container_id, - ], - capture_output=True, - text=True, - ) - return result.returncode == 0 and "" not in result.stdout - - -def get_container_status(container_id: str) -> dict[str, Any]: - """Get the status of a Docker container. - - Args: - container_id: Docker container ID - - Returns: - Dict with 'status' (running, exited, restarting, not_found), - 'exit_code' (int or None), and 'health' (health status or None) - """ - result = subprocess.run( - [ - "docker", - "inspect", - "-f", - "{{.State.Status}}|{{.State.ExitCode}}|{{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}", - container_id, - ], - capture_output=True, - text=True, - ) - - if result.returncode != 0: - return {"status": "not_found", "exit_code": None, "health": None} - - parts = result.stdout.strip().split("|") - status = parts[0] if parts else "unknown" - exit_code = int(parts[1]) if len(parts) > 1 and parts[1].isdigit() else None - health = parts[2] if len(parts) > 2 and parts[2] != "none" else None - - return {"status": status, "exit_code": exit_code, "health": health} - - -def wait_for_container_running( - container_id: str, timeout: int = 30, interval: float = 2.0 -) -> dict[str, Any]: - """Wait for a container to reach the running state. - - Polls docker inspect until the container status is "running" or timeout. - - Args: - container_id: Docker container ID - timeout: Maximum seconds to wait - interval: Seconds between polls - - Returns: - Dict with 'success' (bool), 'status' (str), 'exit_code' (int or None), - and 'waited_seconds' (float) - """ - - start_time = time.time() - - while time.time() - start_time < timeout: - info = get_container_status(container_id) - - if info["status"] == "running": - return { - "success": True, - "status": "running", - "exit_code": None, - "waited_seconds": time.time() - start_time, - } - - if info["status"] == "exited": - return { - "success": False, - "status": "exited", - "exit_code": info["exit_code"], - "waited_seconds": time.time() - start_time, - } - - if info["status"] == "not_found": - return { - "success": False, - "status": "not_found", - "exit_code": None, - "waited_seconds": time.time() - start_time, - } - - time.sleep(interval) - - # Timeout reached - info = get_container_status(container_id) - return { - "success": False, - "status": info["status"], - "exit_code": info["exit_code"], - "waited_seconds": time.time() - start_time, - } - - -def get_container_logs(container_id: str, tail: int = 100) -> str: - """Get the logs of a Docker container. - - Args: - container_id: Docker container ID - tail: Number of lines to return - - Returns: - Container logs - """ - result = subprocess.run( - ["docker", "logs", "--tail", str(tail), container_id], - capture_output=True, - text=True, - ) - - if result.returncode == 0: - return result.stdout - return f"Failed to get logs: {result.stderr}" - - -def find_free_port(start: int = 10000, end: int = 20000) -> int: - """Find a free TCP port in the given range. - - Args: - start: Start of port range - end: End of port range - - Returns: - Free port number - """ - import socket - - for port in range(start, end): - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - if s.connect_ex(("localhost", port)) != 0: - return port - - raise RuntimeError(f"No free port found in range {start}-{end}") diff --git a/apps/api/src/services/docker/__init__.py b/apps/api/src/services/docker/__init__.py new file mode 100644 index 0000000..cd1dc61 --- /dev/null +++ b/apps/api/src/services/docker/__init__.py @@ -0,0 +1,44 @@ +"""Docker services for container and tunnel management.""" + +from .compose import ( + ensure_instance_directory, + execute_compose_command, + render_compose_template, + write_compose_file, + write_env_file, +) +from .config_staging import write_config_files, write_config_folder_files +from .container import ( + connect_container_to_network, + find_free_port, + get_container_id, + get_container_logs, + get_container_name, + get_container_status, +) +from .tunnel import ( + check_tunnel_health, + recreate_tunnel, + start_cloudflared_tunnel, + stop_cloudflared_tunnel, +) + +__all__ = [ + "render_compose_template", + "ensure_instance_directory", + "write_compose_file", + "write_env_file", + "execute_compose_command", + "write_config_files", + "write_config_folder_files", + "get_container_id", + "get_container_name", + "connect_container_to_network", + "get_container_status", + "get_container_logs", + "find_free_port", + "start_cloudflared_tunnel", + "stop_cloudflared_tunnel", + "recreate_tunnel", + "check_tunnel_health", +] diff --git a/apps/api/src/services/docker/compose.py b/apps/api/src/services/docker/compose.py new file mode 100644 index 0000000..a730e3c --- /dev/null +++ b/apps/api/src/services/docker/compose.py @@ -0,0 +1,237 @@ +"""Docker Compose file generation and command execution.""" + +import re +import subprocess +import uuid +from pathlib import Path +from typing import Any + +from sqlalchemy import select +from sqlalchemy.ext.asyncio import AsyncSession + +from src.models.config_profile import ConfigProfile +from src.models.tool_instance import ToolInstance +from src.services.profile_resolver import resolve_profile + + +def _sanitize_name(name: str) -> str: + """Sanitize a string for use in Docker/container names.""" + sanitized = re.sub(r"[^a-z0-9-]", "-", name.lower()) + sanitized = re.sub(r"-+", "-", sanitized) + return sanitized.strip("-") + + +async def _generate_instance_name( + session: AsyncSession, + project_name: str, + tool_type_name: str, +) -> str: + """Generate a unique instance name: project-tool-NUM.""" + base = f"{_sanitize_name(project_name)}-{_sanitize_name(tool_type_name)}" + base = base.strip("-") or "instance" + result = await session.execute( + select(ToolInstance.name).where(ToolInstance.name.like(f"{base}-%")) + ) + names = result.scalars().all() + max_num = 0 + for name in names: + parts = name.rsplit("-", 1) + if len(parts) == 2 and parts[0] == base and parts[1].isdigit(): + max_num = max(max_num, int(parts[1])) + return f"{base}-{max_num + 1:03d}" + + +def _modify_compose_file( + compose_path: str, + port_override: int | None = None, + start_command: str | None = None, + working_directory: str | None = None, + extra_volumes: list[dict] | None = None, +) -> None: + """Modify compose file with runtime overrides.""" + import yaml + + compose_file = Path(compose_path) + content = compose_file.read_text() + compose_data = yaml.safe_load(content) + + if not compose_data or "services" not in compose_data: + return + + for service_name, service_config in compose_data["services"].items(): + if port_override and "ports" in service_config: + for i, port_mapping in enumerate(service_config["ports"]): + if isinstance(port_mapping, str) and ":" in port_mapping: + _host_port, container_port = port_mapping.split(":", 1) + service_config["ports"][i] = f"{port_override}:{container_port}" + break + + if start_command: + service_config["command"] = start_command + + if working_directory: + service_config["working_dir"] = working_directory + + if extra_volumes: + if "volumes" not in service_config: + service_config["volumes"] = [] + for vol in extra_volumes: + source = vol.get("source", "") + target = vol.get("target", "") + vol_type = vol.get("type", "bind") + if vol_type == "bind": + service_config["volumes"].append(f"{source}:{target}") + else: + service_config["volumes"].append(f"{source}:{target}:{vol_type}") + + break + + compose_file.write_text(yaml.dump(compose_data, default_flow_style=False)) + + +async def _apply_resolved_profile( + profile: ConfigProfile, + instance_dir: str, + env_vars: dict[str, str], + port_override: int | None, + start_command: str | None, + working_directory: str | None, + extra_volumes: list[dict], +) -> tuple[dict[str, str], int | None, str | None, str | None, list[dict]]: + """Resolve a profile and apply its output to instance configuration.""" + resolved = resolve_profile(profile) + + if resolved.environment_variables: + env_vars.update(resolved.environment_variables) + + if resolved.runtime_hints.start_command is not None: + start_command = resolved.runtime_hints.start_command + if resolved.runtime_hints.working_directory is not None: + working_directory = resolved.runtime_hints.working_directory + if resolved.runtime_hints.port is not None: + port_override = resolved.runtime_hints.port + + for target_path, mount in resolved.mounts.items(): + safe_name = target_path.strip("/").replace("/", "_") + mount_dir = Path(instance_dir) / "mounts" / safe_name + mount_dir.mkdir(parents=True, exist_ok=True) + + for rel_path, content in mount.files.items(): + file_path = mount_dir / rel_path + file_path.parent.mkdir(parents=True, exist_ok=True) + file_path.write_text(content) + + extra_volumes.append({ + "source": str(mount_dir), + "target": target_path, + "type": mount.mode, + }) + + return env_vars, port_override, start_command, working_directory, extra_volumes + + +def render_compose_template(template: str, variables: dict[str, Any]) -> str: + """Render a Docker Compose template with variable substitution. + + Args: + template: The compose template string + variables: Dictionary of variable names to values + + Returns: + Rendered compose file content + """ + result = template + for key, value in variables.items(): + placeholder = f"{{{{{key}}}}}" + result = result.replace(placeholder, str(value)) + return result + + +def ensure_instance_directory(instance_id: str, base_path: str | None = None) -> str: + """Create and return the instance directory path. + + Args: + instance_id: Unique instance identifier + base_path: Base directory for all instances (defaults to Settings.instance_base_path) + + Returns: + Absolute path to instance directory + """ + if base_path is None: + from src.config import Settings + base_path = Settings().instance_base_path + instance_dir = Path(base_path) / instance_id + instance_dir.mkdir(parents=True, exist_ok=True) + return str(instance_dir.absolute()) + + +def write_compose_file(instance_dir: str, content: str) -> str: + """Write the rendered compose file to the instance directory. + + Args: + instance_dir: Path to instance directory + content: Rendered compose content + + Returns: + Path to the compose file + """ + compose_path = Path(instance_dir) / "docker-compose.yml" + compose_path.write_text(content) + return str(compose_path) + + +def write_env_file(instance_dir: str, env_vars: dict[str, str]) -> str: + """Write environment variables to a .env file. + + Args: + instance_dir: Path to instance directory + env_vars: Dictionary of env var names to values + + Returns: + Path to the env file + """ + env_path = Path(instance_dir) / ".env" + lines = [f'{key}="{value}"' for key, value in env_vars.items()] + env_path.write_text("\n".join(lines) + "\n") + return str(env_path) + + +def execute_compose_command( + compose_path: str, action: str, timeout: int = 60, env_file: str | None = None +) -> tuple[int, str, str]: + """Execute a docker compose command. + + Args: + compose_path: Path to docker-compose.yml + action: The compose action (up, down, start, stop, restart) + timeout: Command timeout in seconds + env_file: Optional path to .env file for environment variables + + Returns: + Tuple of (returncode, stdout, stderr) + """ + instance_dir = Path(compose_path).parent + + cmd = ["docker", "compose", "-f", compose_path] + + if env_file: + cmd.extend(["--env-file", env_file]) + + if action == "up": + cmd.extend(["up", "-d"]) + elif action == "down": + cmd.extend(["down", "-v"]) + elif action in ("start", "stop", "restart"): + cmd.append(action) + else: + raise ValueError(f"Unknown compose action: {action}") + + result = subprocess.run( + cmd, + cwd=str(instance_dir), + capture_output=True, + text=True, + timeout=timeout, + ) + + return result.returncode, result.stdout, result.stderr diff --git a/apps/api/src/services/docker/config_staging.py b/apps/api/src/services/docker/config_staging.py new file mode 100644 index 0000000..817bcbf --- /dev/null +++ b/apps/api/src/services/docker/config_staging.py @@ -0,0 +1,79 @@ +"""Config folder file staging for Docker instances.""" + +import logging +from pathlib import Path + +logger = logging.getLogger(__name__) + + +def write_config_files(instance_dir: str, files: dict[str, str]) -> None: + """Write config files to the instance directory. + + Args: + instance_dir: Path to instance directory + files: Dictionary of file paths (relative to instance dir) to content + """ + instance_path = Path(instance_dir) + for file_path, content in files.items(): + # Ensure the path is within the instance directory (security) + full_path = instance_path / file_path + try: + full_path.resolve().relative_to(instance_path.resolve()) + except ValueError: + raise ValueError(f"File path '{file_path}' escapes instance directory") + + full_path.parent.mkdir(parents=True, exist_ok=True) + full_path.write_text(content) + + +def write_config_folder_files(instance_dir: str, folders: list, project_id: str | None = None) -> list[dict]: + """Write config folder files to the instance directory and return volume mounts. + + Args: + instance_dir: Path to instance directory + folders: List of ConfigFolder objects + project_id: Optional project ID for applying overrides + + Returns: + List of volume mount dicts [{"source": "...", "target": "...", "type": "..."}] + """ + instance_path = Path(instance_dir) + volume_mounts = [] + + for folder in folders: + # Determine mount path (with project override if applicable) + mount_path = folder.mount_path + files = folder.files.copy() + + if project_id and folder.project_overrides: + override = folder.project_overrides.get(str(project_id)) + if override: + if override.get("mount_path"): + mount_path = override["mount_path"] + if override.get("files"): + files.update(override["files"]) + + # Write files to instance directory + folder_dir = instance_path / "volumes" / folder.name + folder_dir.mkdir(parents=True, exist_ok=True) + + for file_path, content in files.items(): + # Security: ensure path doesn't escape folder_dir + full_path = folder_dir / file_path + try: + full_path.resolve().relative_to(folder_dir.resolve()) + except ValueError: + logger.warning("Config folder file path escapes directory: %s", file_path) + continue + + full_path.parent.mkdir(parents=True, exist_ok=True) + full_path.write_text(content) + + # Add volume mount + volume_mounts.append({ + "source": str(folder_dir), + "target": mount_path, + "type": "bind", + }) + + return volume_mounts diff --git a/apps/api/src/services/docker/container.py b/apps/api/src/services/docker/container.py new file mode 100644 index 0000000..e669e02 --- /dev/null +++ b/apps/api/src/services/docker/container.py @@ -0,0 +1,121 @@ +"""Docker container lifecycle and query operations.""" + +import socket +import subprocess + + +def get_container_id(instance_name: str) -> str | None: + """Get the container ID for a compose service. + + Args: + instance_name: The service name in compose + + Returns: + Container ID or None if not found + """ + result = subprocess.run( + ["docker", "ps", "-q", "--filter", f"name={instance_name}"], + capture_output=True, + text=True, + ) + + if result.returncode == 0 and result.stdout.strip(): + return result.stdout.strip().split("\n")[0] + return None + + +def get_container_name(instance_name: str) -> str | None: + """Get the full container name for a compose service. + + Args: + instance_name: The service name in compose + + Returns: + Container name or None if not found + """ + result = subprocess.run( + ["docker", "ps", "--format", "{{.Names}}", "--filter", f"name={instance_name}"], + capture_output=True, + text=True, + ) + + if result.returncode == 0 and result.stdout.strip(): + return result.stdout.strip().split("\n")[0] + return None + + +def connect_container_to_network(container_name: str, network_name: str = "backend") -> bool: + """Connect a Docker container to an existing network. + + Args: + container_name: Name or ID of the container + network_name: Name of the Docker network (default: backend) + + Returns: + True if successful, False otherwise + """ + result = subprocess.run( + ["docker", "network", "connect", network_name, container_name], + capture_output=True, + text=True, + ) + return result.returncode == 0 + + +def get_container_status(container_id: str) -> str: + """Get the status of a Docker container. + + Args: + container_id: Docker container ID + + Returns: + Container status string (running, exited, etc.) + """ + result = subprocess.run( + ["docker", "inspect", "-f", "{{.State.Status}}", container_id], + capture_output=True, + text=True, + ) + + if result.returncode == 0: + return result.stdout.strip() + return "unknown" + + +def get_container_logs(container_id: str, tail: int = 100) -> str: + """Get the logs of a Docker container. + + Args: + container_id: Docker container ID + tail: Number of lines to return + + Returns: + Container logs + """ + result = subprocess.run( + ["docker", "logs", "--tail", str(tail), container_id], + capture_output=True, + text=True, + ) + + if result.returncode == 0: + return result.stdout + return f"Failed to get logs: {result.stderr}" + + +def find_free_port(start: int = 10000, end: int = 20000) -> int: + """Find a free TCP port in the given range. + + Args: + start: Start of port range + end: End of port range + + Returns: + Free port number + """ + for port in range(start, end): + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + if s.connect_ex(("localhost", port)) != 0: + return port + + raise RuntimeError(f"No free port found in range {start}-{end}") diff --git a/apps/api/src/services/docker/tunnel.py b/apps/api/src/services/docker/tunnel.py new file mode 100644 index 0000000..d53356b --- /dev/null +++ b/apps/api/src/services/docker/tunnel.py @@ -0,0 +1,146 @@ +"""Cloudflare tunnel management for Docker instances.""" + +import logging +import os +import re +import signal +import subprocess +import time +from typing import Any + +logger = logging.getLogger(__name__) + + +def start_cloudflared_tunnel( + container_name: str, port: int, timeout: int = 30 +) -> dict[str, str]: + """Start a temporary Cloudflare tunnel for a container. + + Uses 'cloudflared tunnel --url' to create a temporary tunnel + with a random trycloudflare.com URL. + + Args: + container_name: Name of the Docker container to tunnel to + port: Port number the container listens on + timeout: Maximum seconds to wait for tunnel URL + + Returns: + Dict with 'url' (the public tunnel URL) and 'pid' (process ID) + """ + import select as sel + + # First verify the container is accessible + logger.info("Checking connectivity to %s:%d...", container_name, port) + for attempt in range(10): + check = subprocess.run( + ["curl", "-s", "-o", "/dev/null", "-w", "%{http_code}", + f"http://{container_name}:{port}"], + capture_output=True, + text=True, + timeout=5, + ) + logger.info("Connectivity check %d: http_code=%s", attempt + 1, check.stdout.strip()) + if check.returncode == 0: + break + time.sleep(1) + else: + logger.warning("Container %s:%d not responding to curl checks", container_name, port) + + # Run cloudflared in background, capture output + logger.info("Starting cloudflared tunnel to http://%s:%d", container_name, port) + proc = subprocess.Popen( + ["cloudflared", "tunnel", "--url", f"http://{container_name}:{port}"], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + ) + + # Wait for the URL to appear in output + url_pattern = re.compile(r"https://[a-z0-9-]+\.trycloudflare\.com") + start_time = time.time() + url = None + + while time.time() - start_time < timeout: + # Read available output + readable, _, _ = sel.select([proc.stdout], [], [], 1.0) + if readable: + line = proc.stdout.readline() + if line: + match = url_pattern.search(line) + if match: + url = match.group(0) + break + + if not url: + proc.terminate() + proc.wait(timeout=5) + raise RuntimeError( + f"Failed to get tunnel URL within {timeout}s. " + f"cloudflared output may contain errors." + ) + + return {"url": url, "pid": str(proc.pid)} + + +def stop_cloudflared_tunnel(pid: str) -> None: + """Stop a cloudflared tunnel process. + + Args: + pid: Process ID of the cloudflared tunnel + """ + try: + os.kill(int(pid), signal.SIGTERM) + except ProcessLookupError: + pass # Already stopped + + +def recreate_tunnel( + container_name: str, port: int, old_pid: str | None = None +) -> dict[str, str]: + """Recreate a temporary Cloudflare tunnel. + + Stops the old tunnel (if pid provided) and starts a new one. + + Args: + container_name: Name of the Docker container to tunnel to + port: Port number the container listens on + old_pid: Optional PID of the old tunnel process to stop + + Returns: + Dict with 'url' and 'pid' for the new tunnel + """ + if old_pid: + stop_cloudflared_tunnel(old_pid) + + return start_cloudflared_tunnel(container_name, port) + + +def check_tunnel_health(url: str, timeout: int = 10) -> dict[str, Any]: + """Check if a tunnel URL is healthy. + + Args: + url: The tunnel URL to check + timeout: Request timeout in seconds + + Returns: + Dict with 'healthy' (bool) and 'status_code' (int or None) + """ + try: + result = subprocess.run( + ["curl", "-s", "-o", "/dev/null", "-w", "%{http_code}", + "--max-time", str(timeout), url], + capture_output=True, + text=True, + timeout=timeout + 5, + ) + status_code = int(result.stdout.strip()) + return { + "healthy": 200 <= status_code < 400, + "status_code": status_code, + } + except (ValueError, subprocess.TimeoutExpired, Exception) as e: + return { + "healthy": False, + "status_code": None, + "error": str(e), + } diff --git a/apps/api/src/services/git/__init__.py b/apps/api/src/services/git/__init__.py new file mode 100644 index 0000000..ed70d67 --- /dev/null +++ b/apps/api/src/services/git/__init__.py @@ -0,0 +1 @@ +"""Git services package.""" diff --git a/apps/api/src/services/git/control.py b/apps/api/src/services/git/control.py new file mode 100644 index 0000000..0cbb9c0 --- /dev/null +++ b/apps/api/src/services/git/control.py @@ -0,0 +1,196 @@ +"""Git control operations with repo validation.""" + +import logging +import os +import uuid + +from fastapi import HTTPException, status +from sqlalchemy.ext.asyncio import AsyncSession + +from src.models.git_repository import GitRepository +from src.models.user import User +from src.schemas.git_repository import ( + BranchCreateRequest, + CheckoutRequest, + CommitRequest, + FetchResponse, + MergeRequest, + MergeResponse, + PullResponse, + PushResponse, + StatusResponse, +) +from src.services.git.repository import ensure_repo_on_disk, get_repo_and_validate +from src.utils.git_control import ( + checkout_branch, + commit_changes, + create_branch, + delete_branch, + fetch, + get_status, + merge, + pull, + push, +) + +logger = logging.getLogger(__name__) + + +async def get_status_with_validation( + session: AsyncSession, + project_id: uuid.UUID, + repo_id: uuid.UUID, +) -> StatusResponse: + repo = await get_repo_and_validate(session, repo_id, project_id) + ensure_repo_on_disk(repo) + try: + result = get_status(repo.path) + return StatusResponse( + branch=result.branch, + modified=result.modified, + added=result.added, + deleted=result.deleted, + untracked=result.untracked, + renamed=result.renamed, + ahead=result.ahead, + behind=result.behind, + ) + except RuntimeError as e: + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) + + +async def create_branch_with_validation( + session: AsyncSession, + project_id: uuid.UUID, + repo_id: uuid.UUID, + data: BranchCreateRequest, +) -> dict: + repo = await get_repo_and_validate(session, repo_id, project_id) + ensure_repo_on_disk(repo) + try: + create_branch(repo.path, data.name, data.base_branch) + return {"message": f"Branch '{data.name}' created", "branch": data.name} + except RuntimeError as e: + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) + + +async def delete_branch_with_validation( + session: AsyncSession, + project_id: uuid.UUID, + repo_id: uuid.UUID, + branch_name: str, + force: bool = False, +) -> dict: + repo = await get_repo_and_validate(session, repo_id, project_id) + ensure_repo_on_disk(repo) + try: + delete_branch(repo.path, branch_name, force) + return {"message": f"Branch '{branch_name}' deleted"} + except RuntimeError as e: + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) + + +async def checkout_branch_with_validation( + session: AsyncSession, + project_id: uuid.UUID, + repo_id: uuid.UUID, + data: CheckoutRequest, +) -> dict: + repo = await get_repo_and_validate(session, repo_id, project_id) + ensure_repo_on_disk(repo) + try: + checkout_branch(repo.path, data.branch) + return {"message": f"Checked out branch '{data.branch}'", "branch": data.branch} + except RuntimeError as e: + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) + + +async def commit_changes_with_validation( + session: AsyncSession, + project_id: uuid.UUID, + repo_id: uuid.UUID, + data: CommitRequest, + user: User, +) -> dict: + repo = await get_repo_and_validate(session, repo_id, project_id) + ensure_repo_on_disk(repo) + author_name = user.name or "Unknown" + author_email = user.email or "unknown@example.com" + try: + commit_hash = commit_changes( + repo_path=repo.path, + message=data.message, + author_name=author_name, + author_email=author_email, + files=data.files, + ) + return {"commit_hash": commit_hash, "message": data.message} + except RuntimeError as e: + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) + + +async def fetch_with_validation( + session: AsyncSession, + project_id: uuid.UUID, + repo_id: uuid.UUID, +) -> FetchResponse: + repo = await get_repo_and_validate(session, repo_id, project_id) + ensure_repo_on_disk(repo) + try: + fetch(repo.path) + return FetchResponse(message="Fetched from remote") + except RuntimeError as e: + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) + + +async def pull_with_validation( + session: AsyncSession, + project_id: uuid.UUID, + repo_id: uuid.UUID, + branch: str | None = None, +) -> PullResponse: + repo = await get_repo_and_validate(session, repo_id, project_id) + ensure_repo_on_disk(repo) + try: + pull(repo.path, branch) + return PullResponse(message="Pulled from remote") + except RuntimeError as e: + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) + + +async def push_with_validation( + session: AsyncSession, + project_id: uuid.UUID, + repo_id: uuid.UUID, + branch: str | None = None, +) -> PushResponse: + repo = await get_repo_and_validate(session, repo_id, project_id) + ensure_repo_on_disk(repo) + try: + push(repo.path, branch) + return PushResponse(message="Pushed to remote") + except RuntimeError as e: + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) + + +async def merge_with_validation( + session: AsyncSession, + project_id: uuid.UUID, + repo_id: uuid.UUID, + data: MergeRequest, +) -> MergeResponse: + repo = await get_repo_and_validate(session, repo_id, project_id) + ensure_repo_on_disk(repo) + try: + commit_hash = merge( + repo_path=repo.path, + source_branch=data.source_branch, + target_branch=data.target_branch, + message=data.message, + ) + return MergeResponse( + commit_hash=commit_hash, + message=data.message or f"Merge {data.source_branch}", + ) + except RuntimeError as e: + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) diff --git a/apps/api/src/services/git/files.py b/apps/api/src/services/git/files.py new file mode 100644 index 0000000..b1d2148 --- /dev/null +++ b/apps/api/src/services/git/files.py @@ -0,0 +1,150 @@ +"""Git file operations with repo validation.""" + +import logging +import uuid + +from fastapi import HTTPException, status +from sqlalchemy.ext.asyncio import AsyncSession + +from src.models.git_repository import GitRepository +from src.models.user import User +from src.schemas.git_repository import ( + FileContentResponse, + FileListResponse, + FileUpdateRequest, + FileUpdateResponse, +) +from src.services.git.repository import ensure_repo_on_disk, get_repo_and_validate +from src.utils.git_files import ( + commit_file, + get_file_content, + list_branches, + list_tree, +) + +logger = logging.getLogger(__name__) + + +async def list_files( + session: AsyncSession, + project_id: uuid.UUID, + repo_id: uuid.UUID, + branch: str = "main", + path: str = "", +) -> FileListResponse: + repo = await get_repo_and_validate(session, repo_id, project_id) + ensure_repo_on_disk(repo) + try: + entries = list_tree(repo.path, branch=branch, path=path) + return FileListResponse( + path=path, + branch=branch, + entries=[ + { + "name": e.name, + "type": e.type, + "path": e.path, + "size": e.size, + "mode": e.mode, + "last_commit": e.last_commit, + } + for e in entries + ], + ) + except RuntimeError as e: + logger.error( + "Failed to list files for repo %s (path=%s, branch=%s): %s", + repo_id, + path, + branch, + str(e), + exc_info=True, + ) + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) + + +async def get_file( + session: AsyncSession, + project_id: uuid.UUID, + repo_id: uuid.UUID, + branch: str, + path: str, +) -> FileContentResponse: + repo = await get_repo_and_validate(session, repo_id, project_id) + ensure_repo_on_disk(repo) + try: + file_content = get_file_content(repo.path, branch=branch, path=path) + return FileContentResponse( + path=file_content.path, + branch=file_content.branch, + content=file_content.content, + size=file_content.size, + encoding=file_content.encoding, + language=file_content.language, + is_binary=file_content.is_binary, + last_commit=file_content.last_commit, + ) + except FileNotFoundError: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="file not found") + except RuntimeError as e: + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) + + +async def update_file( + session: AsyncSession, + project_id: uuid.UUID, + repo_id: uuid.UUID, + data: FileUpdateRequest, + user: User, +) -> FileUpdateResponse: + repo = await get_repo_and_validate(session, repo_id, project_id) + ensure_repo_on_disk(repo) + author_name = user.name or "Unknown" + author_email = user.email or "unknown@example.com" + try: + commit_hash = commit_file( + repo_path=repo.path, + branch=data.branch, + path=data.path, + content=data.content, + commit_message=data.commit_message, + author_name=author_name, + author_email=author_email, + ) + return FileUpdateResponse( + commit_hash=commit_hash, + message=data.commit_message, + branch=data.branch, + ) + except RuntimeError as e: + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) + + +async def list_branches_with_validation( + session: AsyncSession, + project_id: uuid.UUID, + repo_id: uuid.UUID, +) -> dict: + repo = await get_repo_and_validate(session, repo_id, project_id) + ensure_repo_on_disk(repo) + try: + branches, default_branch = list_branches(repo.path) + return { + "branches": [ + { + "name": b.name, + "is_default": b.is_default, + "last_commit": b.last_commit, + } + for b in branches + ], + "default_branch": default_branch, + } + except RuntimeError as e: + logger.error( + "Failed to list branches for repo %s: %s", + repo_id, + str(e), + exc_info=True, + ) + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) diff --git a/apps/api/src/services/git/repository.py b/apps/api/src/services/git/repository.py new file mode 100644 index 0000000..d4b4bb0 --- /dev/null +++ b/apps/api/src/services/git/repository.py @@ -0,0 +1,211 @@ +"""Repository lifecycle and path helpers.""" + +import logging +import os +import shutil +import subprocess +import uuid + +from fastapi import HTTPException, status +from sqlalchemy import select +from sqlalchemy.ext.asyncio import AsyncSession + +from src.config import Settings +from src.models.git_repository import GitRepository +from src.models.project import Project +from src.models.user import User +from src.schemas.git_repository import GitRepositoryCreate +from src.utils.git_url_parser import parse_git_url + +logger = logging.getLogger(__name__) + + +def _get_repo_path(user_id: uuid.UUID, project_id: uuid.UUID, name: str) -> str: + """Generate the filesystem path for a repository.""" + base = Settings().repo_base_path or "/data/repos" + return os.path.join(base, str(user_id), str(project_id), f"{name}.git") + + +def _build_provider_clone_url(owner: str, repo: str) -> str: + """Build the SSH clone URL for the fixed git provider.""" + return f"git@git.commumedia.org:{owner}/{repo}.git" + + +def _preflight_remote_repository(remote_url: str) -> None: + """Verify a remote repository is reachable before cloning.""" + try: + result = subprocess.run( + ["git", "ls-remote", remote_url], + capture_output=True, + text=True, + timeout=60, + ) + except subprocess.TimeoutExpired: + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="remote repository check timed out") + except FileNotFoundError: + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="git command not found") + + if result.returncode != 0: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="repository not found or inaccessible", + ) + + +def _clone_working_repository(remote_url: str, repo_path: str) -> None: + try: + result = subprocess.run( + ["git", "clone", remote_url, repo_path], + capture_output=True, + text=True, + timeout=300, + ) + except subprocess.TimeoutExpired: + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="clone operation timed out") + except FileNotFoundError: + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="git command not found") + + if result.returncode != 0: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=f"failed to clone repository: {result.stderr}", + ) + + +def _init_working_repository(repo_path: str) -> None: + try: + result = subprocess.run( + ["git", "init", "-b", "main", repo_path], + capture_output=True, + text=True, + ) + except FileNotFoundError: + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="git command not found") + + if result.returncode == 0: + return + + fallback = subprocess.run( + ["git", "init", repo_path], + capture_output=True, + text=True, + ) + if fallback.returncode != 0: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=f"failed to initialize repository: {fallback.stderr}", + ) + + ref_result = subprocess.run( + ["git", "-C", repo_path, "symbolic-ref", "HEAD", "refs/heads/main"], + capture_output=True, + text=True, + ) + if ref_result.returncode != 0: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=f"failed to set initial branch: {ref_result.stderr}", + ) + + +async def get_repo_and_validate( + session: AsyncSession, + repo_id: uuid.UUID, + project_id: uuid.UUID, +) -> GitRepository: + """Fetch a repository and validate ownership + disk presence.""" + repo = await session.get(GitRepository, repo_id) + if repo is None or repo.project_id != project_id: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="repository not found") + return repo + + +def ensure_repo_on_disk(repo: GitRepository) -> None: + """Raise 404 if the repository is not present on disk.""" + if not os.path.exists(repo.path): + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="repository not found on disk") + + +async def create_repository( + session: AsyncSession, + project_id: uuid.UUID, + data: GitRepositoryCreate, + user: User, +) -> GitRepository: + """Create a new git repository (clone or init).""" + # Check for duplicate name + existing = await session.execute( + select(GitRepository).where( + GitRepository.project_id == project_id, + GitRepository.name == data.name, + ) + ) + if existing.scalar_one_or_none(): + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="repository name already exists") + + # Validate and potentially correct the URL + remote_url = data.remote_url + if remote_url and not data.force_original_url: + parse_result = parse_git_url(remote_url) + if parse_result["needs_parsing"] and parse_result["base_url"]: + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + detail={ + "message": "The provided URL appears to be a browser URL, not a git clone URL", + "suggested_url": parse_result["base_url"], + "original_url": remote_url, + "error_code": "URL_NEEDS_PARSING", + }, + ) + if parse_result["base_url"]: + remote_url = parse_result["base_url"] + + if remote_url: + _preflight_remote_repository(remote_url) + + repo_path = _get_repo_path(user.id, project_id, data.name) + os.makedirs(os.path.dirname(repo_path), exist_ok=True) + + if remote_url: + _clone_working_repository(remote_url, repo_path) + else: + _init_working_repository(repo_path) + + repo = GitRepository( + name=data.name, + path=repo_path, + project_id=project_id, + owner_id=user.id, + is_mirror=False, + remote_url=remote_url, + ) + session.add(repo) + await session.commit() + await session.refresh(repo) + return repo + + +async def delete_repository( + session: AsyncSession, + repo_id: uuid.UUID, + project_id: uuid.UUID, +) -> None: + """Delete a repository from DB and disk.""" + repo = await get_repo_and_validate(session, repo_id, project_id) + + if os.path.exists(repo.path): + shutil.rmtree(repo.path) + + await session.delete(repo) + await session.commit() + + +async def list_repositories( + session: AsyncSession, + project_id: uuid.UUID, +) -> list[GitRepository]: + """List all repositories in a project.""" + result = await session.execute( + select(GitRepository).where(GitRepository.project_id == project_id) + ) + return list(result.scalars().all()) diff --git a/apps/api/src/services/instance_lifecycle.py b/apps/api/src/services/instance_lifecycle.py new file mode 100644 index 0000000..e28a51a --- /dev/null +++ b/apps/api/src/services/instance_lifecycle.py @@ -0,0 +1,420 @@ +"""High-level tool instance lifecycle orchestration. + +Coordinates Docker compose, container, tunnel, and config staging services +to create, start, stop, restart, and delete tool instances. +""" + +import logging +import os +import shutil +from datetime import datetime +from typing import Any + +from fastapi import HTTPException, status +from sqlalchemy import select +from sqlalchemy.ext.asyncio import AsyncSession + +from src.models.config_folder import ConfigFolder +from src.models.config_profile import ConfigProfile +from src.models.git_repository import GitRepository +from src.models.project import Project +from src.models.tool_config import ToolConfig +from src.models.tool_instance import ToolInstance +from src.models.tool_type import ToolType +from src.models.user import User +from src.services.docker import compose as compose_svc +from src.services.docker import config_staging +from src.services.docker import container as container_svc +from src.services.docker import tunnel as tunnel_svc +from src.services.docker_build import build_image +from src.services.readiness_probe import execute_probe + +logger = logging.getLogger(__name__) + + +async def create_new_instance( + session: AsyncSession, + project: Project, + repo: GitRepository, + tool_type: ToolType, + user: User, + display_name: str | None, + selected_profile: ConfigProfile | None, +) -> ToolInstance: + """Create a new tool instance record and its compose file.""" + instance_name = await compose_svc._generate_instance_name( + session, project.name, tool_type.name + ) + instance_dir = compose_svc.ensure_instance_directory(instance_name) + tool_port = container_svc.find_free_port() + + compose_path = await _build_or_render_compose( + tool_type, instance_name, instance_dir, repo, user, project.id, tool_port + ) + + instance = ToolInstance( + name=instance_name, + display_name=display_name or f"{project.name} / {repo.name} / {tool_type.display_name}", + tool_type_id=tool_type.id, + repository_id=repo.id, + project_id=project.id, + owner_id=user.id, + status="pending", + compose_path=compose_path, + port=tool_port, + selected_profile_id=selected_profile.id if selected_profile else None, + ) + session.add(instance) + await session.commit() + await session.refresh(instance) + return instance + + +async def start_existing_instance( + session: AsyncSession, + instance: ToolInstance, + user: User, + project_id: Any, +) -> dict: + """Start an existing instance: stage configs, compose up, probe, tunnel.""" + if not instance.compose_path or not os.path.exists(instance.compose_path): + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, detail="compose file not found" + ) + + instance.status = "building" + await session.commit() + + env_vars, config_files, port_override, start_command, working_directory, _extra_env, extra_volumes = await _fetch_tool_configs( + session, user.id, instance.tool_type_id, project_id + ) + + selected_profile = None + if instance.selected_profile_id: + selected_profile = await session.get(ConfigProfile, instance.selected_profile_id) + if selected_profile and selected_profile.user_id == user.id: + instance_dir = os.path.dirname(instance.compose_path) + env_vars, port_override, start_command, working_directory, extra_volumes = await compose_svc._apply_resolved_profile( + selected_profile, + instance_dir, + env_vars, + port_override, + start_command, + working_directory, + extra_volumes, + ) + + env_file_path, extra_volumes = await _stage_configs_and_folders( + session, user.id, project_id, os.path.dirname(instance.compose_path), + env_vars, config_files, extra_volumes + ) + + if port_override or start_command or working_directory or extra_volumes: + compose_svc._modify_compose_file( + instance.compose_path, port_override, start_command, working_directory, extra_volumes + ) + + returncode, _stdout, stderr = compose_svc.execute_compose_command( + instance.compose_path, "up", env_file=env_file_path + ) + if returncode != 0: + instance.status = "error" + await session.commit() + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"failed to start instance: {stderr}", + ) + + container_id = container_svc.get_container_id(instance.name) + if container_id: + instance.container_id = container_id + container_name = container_svc.get_container_name(instance.name) + if container_name: + instance.container_name = container_name + container_svc.connect_container_to_network(container_name, "backend") + + instance.status = "starting" + instance.last_started_at = datetime.now() + await session.commit() + + tool_type = await session.get(ToolType, instance.tool_type_id) + success, probe_logs = await _run_readiness_probe(instance, tool_type) + if not success: + instance.status = "failed" + instance.url = None + instance.public_url = None + await session.commit() + return { + "status": "failed", + "error": f"Readiness probe failed: {' '.join(probe_logs)}", + } + + instance.status = "running" + await session.commit() + await _start_tunnel_if_web(instance, tool_type) + await session.commit() + + return {"status": instance.status, "url": instance.url} + + +async def restart_existing_instance( + session: AsyncSession, + instance: ToolInstance, + user: User, + project_id: Any, +) -> dict: + """Restart an instance: re-stage configs, compose restart, tunnel.""" + if instance.tunnel_id: + try: + tunnel_svc.stop_cloudflared_tunnel(instance.tunnel_id) + except Exception as exc: + logger.warning("Failed to stop old tunnel: %s", exc) + + if not instance.compose_path or not os.path.exists(instance.compose_path): + instance.status = "error" + await session.commit() + return {"status": instance.status} + + env_vars, config_files, port_override, start_command, working_directory, _extra_env, extra_volumes = await _fetch_tool_configs( + session, user.id, instance.tool_type_id, project_id + ) + + stored_profile = None + if instance.selected_profile_id: + stored_profile = await session.get(ConfigProfile, instance.selected_profile_id) + if stored_profile and stored_profile.user_id == user.id: + instance_dir = os.path.dirname(instance.compose_path) + env_vars, port_override, start_command, working_directory, extra_volumes = await compose_svc._apply_resolved_profile( + stored_profile, instance_dir, env_vars, port_override, start_command, working_directory, extra_volumes + ) + + env_file_path, extra_volumes = await _stage_configs_and_folders( + session, user.id, project_id, os.path.dirname(instance.compose_path), + env_vars, config_files, extra_volumes + ) + + if port_override or start_command or working_directory or extra_volumes: + compose_svc._modify_compose_file( + instance.compose_path, port_override, start_command, working_directory, extra_volumes + ) + + returncode, _stdout, _stderr = compose_svc.execute_compose_command( + instance.compose_path, "restart", env_file=env_file_path + ) + if returncode != 0: + instance.status = "error" + await session.commit() + return {"status": instance.status} + + instance.status = "running" + instance.last_started_at = datetime.now() + + tool_type = await session.get(ToolType, instance.tool_type_id) + await _start_tunnel_if_web(instance, tool_type) + await session.commit() + + return {"status": instance.status, "url": instance.url} + + +async def stop_existing_instance(session: AsyncSession, instance: ToolInstance) -> None: + """Stop an instance and its tunnel.""" + if instance.tunnel_id: + try: + tunnel_svc.stop_cloudflared_tunnel(instance.tunnel_id) + except Exception as exc: + logger.warning("Failed to stop tunnel: %s", exc) + + if instance.compose_path and os.path.exists(instance.compose_path): + compose_svc.execute_compose_command(instance.compose_path, "stop") + + instance.status = "stopped" + instance.last_stopped_at = datetime.now() + instance.url = None + instance.public_url = None + instance.tunnel_id = None + await session.commit() + + +async def delete_existing_instance(session: AsyncSession, instance: ToolInstance) -> None: + """Delete an instance, its containers, and its directory.""" + if instance.tunnel_id: + try: + tunnel_svc.stop_cloudflared_tunnel(instance.tunnel_id) + except Exception as exc: + logger.warning("Failed to stop tunnel: %s", exc) + + if instance.compose_path and os.path.exists(instance.compose_path): + compose_svc.execute_compose_command(instance.compose_path, "down") + instance_dir = os.path.dirname(instance.compose_path) + if os.path.exists(instance_dir): + shutil.rmtree(instance_dir) + + await session.delete(instance) + await session.commit() + + +# ── Internal helpers ─────────────────────────────────────────────────────── + +async def _build_or_render_compose( + tool_type: ToolType, + instance_name: str, + instance_dir: str, + repo: GitRepository, + user: User, + project_id: Any, + tool_port: int, +) -> str: + """Build Dockerfile or render compose template.""" + if tool_type.definition_type == "dockerfile": + image_tag = f"headquarter/{instance_name}:latest" + if tool_type.dockerfile_template: + returncode, _stdout, stderr = build_image( + instance_dir=instance_dir, + dockerfile=tool_type.dockerfile_template, + tag=image_tag, + build_context=tool_type.build_context, + ) + if returncode != 0: + logger.error("Build failed for %s: %s", instance_name, stderr) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Failed to build Docker image: {stderr[:500]}", + ) + + compose_content = ( + f'version: "3.8"\nservices:\n app:\n' + f' image: {image_tag}\n' + f' container_name: {instance_name}\n' + f' ports:\n - "{tool_port}:{tool_type.default_port}"\n' + f' volumes:\n - {repo.path}:/workspace\n' + f' restart: unless-stopped\n' + ) + else: + variables = { + "REPO_PATH": repo.path, + "INSTANCE_NAME": instance_name, + "INSTANCE_ID": instance_name, + "TOOL_NAME": instance_name, + "TOOL_PORT": tool_port, + "USER_ID": str(user.id), + "PROJECT_ID": str(project_id), + } + compose_content = compose_svc.render_compose_template( + tool_type.compose_template, variables + ) + + compose_svc.write_compose_file(instance_dir, compose_content) + return os.path.join(instance_dir, "docker-compose.yml") + + +async def _fetch_tool_configs( + session: AsyncSession, + user_id: Any, + tool_type_id: Any, + project_id: Any, +) -> tuple[dict, dict, Any, Any, Any, dict, list]: + """Fetch tool configs and return parsed values.""" + env_vars: dict[str, str] = {} + config_files: dict[str, str] = {} + port_override = None + start_command = None + working_directory = None + extra_env_vars: dict[str, str] = {} + extra_volumes: list[dict] = [] + + query = ( + select(ToolConfig) + .where(ToolConfig.user_id == user_id, ToolConfig.tool_type_id == tool_type_id) + .where((ToolConfig.project_id == project_id) | (ToolConfig.project_id.is_(None))) + ) + configs = (await session.execute(query)).scalars().all() + + for cfg in configs: + if cfg.config_type == "env": + env_vars[cfg.key] = cfg.value + elif cfg.config_type == "file" and cfg.file_path: + config_files[cfg.file_path] = cfg.value + if cfg.port_override: + port_override = cfg.port_override + if cfg.start_command: + start_command = cfg.start_command + if cfg.working_directory: + working_directory = cfg.working_directory + if cfg.environment_variables: + extra_env_vars.update(cfg.environment_variables) + if cfg.volumes: + extra_volumes.extend(cfg.volumes) + + env_vars.update(extra_env_vars) + return env_vars, config_files, port_override, start_command, working_directory, extra_env_vars, extra_volumes + + +async def _stage_configs_and_folders( + session: AsyncSession, + user_id: Any, + project_id: Any, + instance_dir: str, + env_vars: dict[str, str], + config_files: dict[str, str], + extra_volumes: list[dict], +) -> tuple[str | None, list[dict]]: + """Write env/config files and config folders.""" + env_file_path: str | None = None + if env_vars: + env_file_path = compose_svc.write_env_file(instance_dir, env_vars) + if config_files: + config_staging.write_config_files(instance_dir, config_files) + + folder_query = select(ConfigFolder).where( + ConfigFolder.user_id == user_id, ConfigFolder.is_active.is_(True) + ) + folders = (await session.execute(folder_query)).scalars().all() + if folders: + folder_volumes = config_staging.write_config_folder_files( + instance_dir, folders, str(project_id) + ) + extra_volumes.extend(folder_volumes) + + return env_file_path, extra_volumes + + +async def _start_tunnel_if_web(instance: ToolInstance, tool_type: ToolType) -> None: + """Create Cloudflare tunnel for web-enabled tools.""" + if "web" not in tool_type.interfaces or not tool_type.default_port: + instance.url = None + instance.public_url = None + return + + try: + tunnel_info = tunnel_svc.start_cloudflared_tunnel( + container_name=instance.container_name or instance.name, + port=tool_type.default_port, + ) + instance.tunnel_id = tunnel_info["pid"] + instance.public_url = tunnel_info["url"] + instance.url = tunnel_info["url"] + logger.info("Created tunnel for instance %s: %s", instance.id, tunnel_info["url"]) + except Exception as exc: + logger.error("Failed to create tunnel for instance %s: %s", instance.id, exc) + instance.status = "error" + instance.url = None + + +async def _run_readiness_probe( + instance: ToolInstance, tool_type: ToolType +) -> tuple[bool, list[str]]: + """Run readiness probe if configured.""" + if not tool_type.readiness_probe or not instance.container_id: + return True, [] + + probe = tool_type.readiness_probe + command = probe.get("command", "") + if not command: + return True, [] + + return await execute_probe( + container_id=instance.container_id, + command=command, + timeout=probe.get("timeout", 30), + interval=probe.get("interval", 2), + ) diff --git a/apps/api/src/services/profile_resolver.py b/apps/api/src/services/profile_resolver.py new file mode 100644 index 0000000..345497c --- /dev/null +++ b/apps/api/src/services/profile_resolver.py @@ -0,0 +1,251 @@ +"""Profile resolver service for recursive ordered include resolution. + +Provides deterministic merge rules, save-independent cycle protection, +and resolved output structures for env vars, runtime hints, mounts, +file trees, and override metadata. +""" + +from __future__ import annotations + +import uuid +from dataclasses import dataclass, field + +from src.models.config_include import ConfigInclude +from src.models.config_mount import ConfigMount +from src.models.config_profile import ConfigProfile + + +@dataclass +class ResolvedMount: + """A resolved mount with merged file tree and final mode.""" + + target_path: str + mode: str # "ro" or "rw" + files: dict[str, str] = field(default_factory=dict) + """Relative file paths to UTF-8 text content.""" + overridden_files: dict[str, list[str]] = field(default_factory=dict) + """Map of relative file path to list of profile names that contributed + (latest is the winner).""" + mode_overridden_by: str | None = None + """Name of the profile that set the final mode, if different from first.""" + + +@dataclass +class ResolvedRuntimeHints: + """Resolved runtime hints from profile layers.""" + + start_command: str | None = None + working_directory: str | None = None + port: int | None = None + overridden_hints: dict[str, str] = field(default_factory=dict) + """Map of hint key to profile name that provided the winning value.""" + + +@dataclass +class ResolvedProfileOutput: + """Complete resolved output for a config profile.""" + + profile_id: uuid.UUID + profile_name: str + environment_variables: dict[str, str] = field(default_factory=dict) + """Final merged env vars (later layers win).""" + env_var_sources: dict[str, list[str]] = field(default_factory=dict) + """Map of env var key to ordered list of contributing profile names + (latest is the winner).""" + runtime_hints: ResolvedRuntimeHints = field( + default_factory=lambda: ResolvedRuntimeHints() + ) + mounts: dict[str, ResolvedMount] = field(default_factory=dict) + """Map of target_path to ResolvedMount.""" + resolution_order: list[str] = field(default_factory=list) + """Ordered list of profile names as they were resolved.""" + cycle_detected: bool = False + cycle_path: list[str] | None = None + + +class ProfileResolutionError(Exception): + """Raised when profile resolution fails.""" + + pass + + +class ProfileCycleError(ProfileResolutionError): + """Raised when a cycle is detected during profile resolution.""" + + def __init__(self, cycle_path: list[str]) -> None: + self.cycle_path = cycle_path + path_str = " -> ".join(cycle_path) + super().__init__(f"Profile include cycle detected: {path_str}") + + +def _merge_env_vars( + current: dict[str, str], + sources: dict[str, list[str]], + profile: ConfigProfile, +) -> None: + """Merge a profile's env vars into the current dict, tracking sources.""" + if not profile.environment_variables: + return + for key, value in profile.environment_variables.items(): + current[key] = value + if key not in sources: + sources[key] = [] + sources[key].append(profile.name) + + +def _merge_runtime_hints( + hints: ResolvedRuntimeHints, + profile: ConfigProfile, +) -> None: + """Merge a profile's runtime hints, tracking overrides.""" + if profile.start_command is not None: + hints.start_command = profile.start_command + hints.overridden_hints["start_command"] = profile.name + if profile.working_directory is not None: + hints.working_directory = profile.working_directory + hints.overridden_hints["working_directory"] = profile.name + if profile.port is not None: + hints.port = profile.port + hints.overridden_hints["port"] = profile.name + + +def _merge_mounts( + mounts: dict[str, ResolvedMount], + profile_mounts: list[ConfigMount], + profile: ConfigProfile, +) -> None: + """Merge a profile's mounts into the current mounts dict.""" + for mount in profile_mounts: + target = mount.target_path + if target not in mounts: + mounts[target] = ResolvedMount( + target_path=target, + mode=mount.mode, + files={}, + overridden_files={}, + ) + resolved = mounts[target] + + # Mode override: later wins + if resolved.mode != mount.mode: + resolved.mode = mount.mode + resolved.mode_overridden_by = profile.name + + # File tree merge: later wins for same relative path + if mount.files: + for rel_path, content in mount.files.items(): + if rel_path not in resolved.files: + resolved.overridden_files[rel_path] = [] + else: + if rel_path not in resolved.overridden_files: + resolved.overridden_files[rel_path] = [] + resolved.overridden_files[rel_path].append(profile.name) + resolved.files[rel_path] = content + + +def _resolve_profile_recursive( + profile: ConfigProfile, + visited: set[uuid.UUID], + path: list[str], + resolution_order: list[str], + env_vars: dict[str, str], + env_var_sources: dict[str, list[str]], + runtime_hints: ResolvedRuntimeHints, + mounts: dict[str, ResolvedMount], +) -> None: + """Recursively resolve a profile and its includes. + + Args: + profile: The profile to resolve + visited: Set of already-resolved profile IDs to avoid duplicates + path: Current recursion path for cycle detection + resolution_order: Ordered list of profile names being resolved + env_vars: Accumulated environment variables + env_var_sources: Tracking of which profiles contributed each env var + runtime_hints: Accumulated runtime hints + mounts: Accumulated mounts + + Raises: + ProfileCycleError: If a cycle is detected + """ + if profile.name in path: + # Cycle detected + cycle_start = path.index(profile.name) + cycle_path = path[cycle_start:] + [profile.name] + raise ProfileCycleError(cycle_path) + + if profile.id in visited: + # Already resolved in another branch (diamond graph) + return + + visited.add(profile.id) + path.append(profile.name) + resolution_order.append(profile.name) + + # Resolve includes first (in order) + includes: list[ConfigInclude] = list(profile.includes) + includes.sort(key=lambda inc: inc.order_index) + for include in includes: + included_profile = include.included_profile + if included_profile is not None: + _resolve_profile_recursive( + included_profile, + visited, + path, + resolution_order, + env_vars, + env_var_sources, + runtime_hints, + mounts, + ) + + # Apply this profile's values (later layers win) + _merge_env_vars(env_vars, env_var_sources, profile) + _merge_runtime_hints(runtime_hints, profile) + _merge_mounts(mounts, list(profile.mounts), profile) + + path.pop() + + +def resolve_profile(profile: ConfigProfile) -> ResolvedProfileOutput: + """Resolve a config profile with all its includes. + + Processes included profiles in configured order, then applies the + selected profile itself. Later layers override earlier layers. + + Args: + profile: The root profile to resolve + + Returns: + ResolvedProfileOutput with merged env vars, runtime hints, mounts, + and override metadata + + Raises: + ProfileCycleError: If a cycle is detected in the include graph + """ + env_vars: dict[str, str] = {} + env_var_sources: dict[str, list[str]] = {} + runtime_hints = ResolvedRuntimeHints() + mounts: dict[str, ResolvedMount] = {} + resolution_order: list[str] = [] + + _resolve_profile_recursive( + profile, + set(), + [], + resolution_order, + env_vars, + env_var_sources, + runtime_hints, + mounts, + ) + + return ResolvedProfileOutput( + profile_id=profile.id, + profile_name=profile.name, + environment_variables=env_vars, + env_var_sources=env_var_sources, + runtime_hints=runtime_hints, + mounts=mounts, + resolution_order=resolution_order, + ) diff --git a/apps/api/src/services/terminal_manager.py b/apps/api/src/services/terminal_manager.py index b9605db..6cb8f73 100644 --- a/apps/api/src/services/terminal_manager.py +++ b/apps/api/src/services/terminal_manager.py @@ -1,426 +1,193 @@ """Terminal session manager for WebSocket connections.""" import asyncio +import contextlib +import json import logging +import time import uuid -from datetime import datetime, timezone +from collections.abc import Coroutine +from typing import Any from fastapi import WebSocket -from sqlalchemy.dialects.postgresql import insert as pg_insert -from src.database import SessionLocal -from src.models.terminal_session import TerminalSessionModel from src.services.terminal_session import TerminalSession logger = logging.getLogger(__name__) - -class MaxSessionsExceededError(Exception): - """Raised when the maximum number of terminal sessions per instance is reached.""" - - def __init__(self, instance_id: str, max_sessions: int = 5) -> None: - self.instance_id = instance_id - self.max_sessions = max_sessions - super().__init__( - f"Maximum of {max_sessions} terminal sessions reached for instance {instance_id}" - ) +_READ_BATCH_INTERVAL_S = 0.016 # 16ms max batching delay +_READ_POLL_TIMEOUT_S = 0.005 +_READ_POLL_SLEEP_S = 0.001 +_HEARTBEAT_INTERVAL_S = 15.0 +_IDLE_TIMEOUT_S = 60.0 class TerminalManager: - """Manages active terminal sessions with persistence support.""" - - # Maximum sessions per tool instance - MAX_SESSIONS_PER_INSTANCE = 5 + """Manages active terminal sessions.""" def __init__(self) -> None: - # Track sessions by (instance_id, session_id) for multi-session support - self._sessions: dict[tuple[str, str], TerminalSession] = {} - self._idle_check_task: asyncio.Task | None = None - self._start_idle_check() - - def _start_idle_check(self) -> None: - """Start the idle timeout background task.""" - if self._idle_check_task is not None and not self._idle_check_task.done(): - return - try: - loop = asyncio.get_running_loop() - self._idle_check_task = loop.create_task(self._idle_check_loop()) - except RuntimeError: - # No event loop running yet, will be started lazily - pass - - async def _idle_check_loop(self) -> None: - """Periodically check for idle sessions and clean them up.""" - while True: - try: - await asyncio.sleep(60) # Check every minute - await self._cleanup_idle_sessions() - except Exception as exc: - logger.error("Error in idle check loop: %s", exc) - - async def _cleanup_idle_sessions(self) -> None: - """Clean up sessions that have been idle for too long.""" - idle_keys = [] - for (instance_id, session_id), session in list(self._sessions.items()): - if session.is_idle(): - idle_keys.append((instance_id, session_id)) - - for key in idle_keys: - instance_id, session_id = key - logger.info( - "Cleaning up idle terminal session %s for instance %s", - session_id, - instance_id, - ) - session = self._sessions.pop(key, None) - if session: - await session.close() - # Update DB status fire-and-forget - asyncio.create_task(self._mark_closed_in_db(session_id)) - - async def _insert_db_session_row( - self, - session_id: str, - instance_id: uuid.UUID, - name: str, - ) -> None: - """Insert a TerminalSessionModel row into the database. - - Uses ON CONFLICT DO NOTHING to handle races when a session is - restored from DB and then re-inserted. - """ - try: - async with SessionLocal() as db_session: - stmt = ( - pg_insert(TerminalSessionModel) - .values( - id=uuid.UUID(session_id), - instance_id=instance_id, - name=name, - status="active", - created_at=datetime.now(timezone.utc), - last_activity_at=datetime.now(timezone.utc), - ) - .on_conflict_do_nothing(index_elements=["id"]) - ) - await db_session.execute(stmt) - await db_session.commit() - logger.debug( - "Inserted terminal session row %s for instance %s", - session_id, - instance_id, - ) - except Exception as exc: - logger.error("Failed to insert terminal session row: %s", exc) - - async def _mark_closed_in_db(self, session_id: str) -> None: - """Mark a terminal session as closed in the database.""" - try: - async with SessionLocal() as db_session: - db_row = await db_session.get( - TerminalSessionModel, uuid.UUID(session_id) - ) - if db_row: - db_row.status = "closed" - db_row.closed_at = datetime.now(timezone.utc) - await db_session.commit() - logger.debug( - "Marked terminal session %s as closed in DB", session_id - ) - except Exception as exc: - logger.error("Failed to mark terminal session as closed in DB: %s", exc) - - def _count_sessions_for_instance(self, instance_id_str: str) -> int: - """Count active in-memory sessions for a given instance.""" - return sum(1 for (iid, _sid) in self._sessions if iid == instance_id_str) + """Initialise the terminal manager.""" + self._sessions: dict[str, TerminalSession] = {} + self._last_client_message: dict[str, float] = {} + self._background_tasks: set[asyncio.Task[Any]] = set() async def create_session( self, instance_id: uuid.UUID, container_id: str, - startup_command: str | None = None, - name: str | None = None, - session_id: str | None = None, + websocket: WebSocket, ) -> TerminalSession: - """Create a new terminal session for an instance. - - Enforces a maximum of MAX_SESSIONS_PER_INSTANCE sessions per instance. - Inserts a DB row fire-and-forget. - - Args: - instance_id: UUID of the tool instance. - container_id: Docker container ID. - startup_command: Optional startup command to run. - name: Optional session name (auto-generated if omitted). - - Returns: - The newly created TerminalSession. - - Raises: - MaxSessionsExceededError: If the instance already has max sessions. - """ - instance_id_str = str(instance_id) - - if ( - self._count_sessions_for_instance(instance_id_str) - >= self.MAX_SESSIONS_PER_INSTANCE - ): - raise MaxSessionsExceededError( - instance_id_str, self.MAX_SESSIONS_PER_INSTANCE - ) - - if session_id is None: - session_id = str(uuid.uuid4()) - session = TerminalSession( - session_id=session_id, - instance_id=instance_id, - container_id=container_id, - startup_command=startup_command, - name=name, - ) - await session.start(startup_command=startup_command) - - key = (instance_id_str, session_id) - self._sessions[key] = session - - # Fire-and-forget DB insert (skip if row already exists) - asyncio.create_task( - self._insert_db_session_row(session_id, instance_id, session.name) - ) - - logger.info( - "Created terminal session %s for instance %s (name=%s)", - session_id, - instance_id, - session.name, - ) - return session - - async def get_or_create_session( - self, - instance_id: uuid.UUID, - container_id: str, - startup_command: str | None = None, - ) -> TerminalSession: - """Get existing session or create a new one. - - Backward-compatible alias that uses 'default' as the session_id. - """ - # Ensure idle check is running (lazy start) - self._start_idle_check() - - instance_id_str = str(instance_id) - key = (instance_id_str, "default") - - # Check for existing default session - if key in self._sessions: - session = self._sessions[key] - - # Check if session is still alive - if session.is_alive(): - logger.debug( - "Reattaching to existing terminal session for instance %s", - instance_id, - ) - return session - else: - # Session died, clean it up - logger.debug( - "Existing session for instance %s is dead, cleaning up", - instance_id, - ) - await session.close() - del self._sessions[key] - - # Create new default session - logger.info( - "Creating new default terminal session for instance %s", instance_id - ) + """Create a new terminal session.""" session_id = str(uuid.uuid4()) - session = TerminalSession( - session_id=session_id, - instance_id=instance_id, - container_id=container_id, - startup_command=startup_command, - name="Session 1", - ) - await session.start(startup_command=startup_command) - self._sessions[key] = session + session = TerminalSession(session_id, instance_id, container_id) + await session.start() + self._sessions[session_id] = session + self._last_client_message[session_id] = time.monotonic() - # Fire-and-forget DB insert - asyncio.create_task( - self._insert_db_session_row(session_id, instance_id, session.name) - ) + # Start background tasks for I/O streaming + self._start_task(self._read_loop(session, websocket)) + self._start_task(self._write_loop(session, websocket)) + self._start_task(self._heartbeat_loop(session, websocket)) return session - def get_session( - self, - instance_id: str, - session_id: str, - ) -> TerminalSession | None: - """Lookup a session by composite key, or by internal session_id.""" - session = self._sessions.get((instance_id, session_id)) - if session is not None: - return session - # Fallback: search by internal TerminalSession.session_id - for (iid, _sid), sess in self._sessions.items(): - if iid == instance_id and sess.session_id == session_id: - return sess - return None + def _start_task(self, coro: Coroutine[Any, Any, None]) -> None: + """Start a background task and store a reference to prevent GC.""" + task = asyncio.create_task(coro) + self._background_tasks.add(task) + task.add_done_callback(self._background_tasks.discard) - def _find_key_by_internal_id( - self, - instance_id: str, - internal_session_id: str, - ) -> tuple[str, str] | None: - """Find the manager dict key for a session by its internal session_id.""" - for (iid, sid), session in self._sessions.items(): - if iid == instance_id and session.session_id == internal_session_id: - return (iid, sid) - return None - - def get_sessions_for_instance( - self, - instance_id: str, - ) -> list[TerminalSession]: - """Return all in-memory sessions for a given instance.""" - return [ - session - for (iid, _sid), session in self._sessions.items() - if iid == instance_id - ] - - async def close_session( - self, - instance_id: str, - session_id: str, - ) -> None: - """Close a specific session and update its DB status.""" - key = (instance_id, session_id) - session = self._sessions.pop(key, None) - if session: - await session.close() - # Fire-and-forget DB update - asyncio.create_task(self._mark_closed_in_db(session_id)) - logger.info( - "Closed terminal session %s for instance %s", - session_id, - instance_id, - ) - - async def attach_websocket( + async def _read_loop( self, session: TerminalSession, websocket: WebSocket, ) -> None: - """Attach a WebSocket to an existing session. + """Read output from the container and send to WebSocket with batching.""" + try: + buffer = bytearray() + last_flush = time.monotonic() - Closes existing WebSocket connections only for this specific session. - """ - # Handle concurrent connections - close existing ones within the same session - if session.has_websockets(): - logger.debug( - "Closing existing WebSocket connections for session %s (instance %s)", + while session.is_alive() and not session.closed: + data = await session.read_output(select_timeout=_READ_POLL_TIMEOUT_S) + if data: + buffer.extend(data) + + now = time.monotonic() + flush_due = buffer and ( + now - last_flush >= _READ_BATCH_INTERVAL_S or not data + ) + + if flush_due: + await websocket.send_bytes(bytes(buffer)) + buffer.clear() + last_flush = now + elif not data: + await asyncio.sleep(_READ_POLL_SLEEP_S) + + # Flush any remaining data + if buffer: + with contextlib.suppress(Exception): + await websocket.send_bytes(bytes(buffer)) + + except Exception: + logger.exception("Read loop error for session %s", session.session_id) + finally: + await self._cleanup_session(session) + + async def _write_loop( + self, + session: TerminalSession, + websocket: WebSocket, + ) -> None: + """Read input from WebSocket and send to container.""" + try: + while session.is_alive() and not session.closed: + message = await websocket.receive() + self._last_client_message[session.session_id] = time.monotonic() + + if message["type"] == "websocket.receive": + if "bytes" in message: + await session.write_input(message["bytes"]) + elif "text" in message: + text = message["text"] + if text.startswith("{"): + try: + ctrl = json.loads(text) + await self._handle_control_message( + session, + websocket, + ctrl, + ) + except json.JSONDecodeError: + logger.debug("Invalid JSON control message: %s", text) + else: + await session.write_input(text.encode("utf-8")) + elif message["type"] == "websocket.disconnect": + break + except Exception: + logger.exception("Write loop error for session %s", session.session_id) + finally: + await self._cleanup_session(session) + + async def _handle_control_message( + self, + session: TerminalSession, + websocket: WebSocket, + ctrl: dict[str, Any], + ) -> None: + """Handle a JSON control message from the client.""" + msg_type = ctrl.get("type") + if msg_type == "resize": + await session.resize( + ctrl.get("cols", 80), + ctrl.get("rows", 24), + ) + elif msg_type == "ping": + await websocket.send_json( + {"type": "pong", "id": ctrl.get("id")}, + ) + + async def _heartbeat_loop( + self, + session: TerminalSession, + websocket: WebSocket, + ) -> None: + """Monitor client activity and close idle connections.""" + try: + while session.is_alive() and not session.closed: + await asyncio.sleep(_HEARTBEAT_INTERVAL_S) + last_msg = self._last_client_message.get(session.session_id, 0) + if time.monotonic() - last_msg > _IDLE_TIMEOUT_S: + # Client has been silent for 60s — close connection + with contextlib.suppress(Exception): + await websocket.close( + code=1000, + reason="Idle timeout", + ) + break + except Exception: + logger.exception( + "Heartbeat loop error for session %s", session.session_id, - session.instance_id, ) - for ws in list(session._websockets): - try: - await ws.close(code=4000, reason="New connection established") - except Exception: - pass # noqa: S110 - session._websockets.clear() + finally: + await self._cleanup_session(session) - # Attach new WebSocket - session.attach_websocket(websocket) - - # Replay buffer - buffer = session.get_buffer() - if buffer: - try: - await websocket.send_bytes(buffer) - except Exception: - pass # noqa: S110 - - async def detach_websocket( - self, - session: TerminalSession, - websocket: WebSocket, - ) -> None: - """Detach a WebSocket from a session.""" - session.detach_websocket(websocket) - - async def reset_session( - self, - instance_id: uuid.UUID, - container_id: str, - startup_command: str | None = None, - session_id: str | None = None, - name: str | None = None, - ) -> TerminalSession: - """Reset a session by killing it and creating a new one. - - Args: - instance_id: UUID of the tool instance. - container_id: Docker container ID. - startup_command: Optional startup command. - session_id: Specific session to reset. If None, resets the default session. - name: Optional name to preserve for the new session. - - Returns: - The newly created TerminalSession. - """ - instance_id_str = str(instance_id) - target_session_id = session_id or "default" - key = (instance_id_str, target_session_id) - - # Preserve old name if not provided - old_name = name - if old_name is None and key in self._sessions: - old_name = self._sessions[key].name - - # Close existing session if any - if key in self._sessions: - logger.debug( - "Resetting terminal session %s for instance %s", - target_session_id, - instance_id, - ) - old_session = self._sessions.pop(key) - await old_session.close() - # Fire-and-forget DB update for old session - asyncio.create_task(self._mark_closed_in_db(old_session.session_id)) - - # Create new session preserving the same session_id slot - new_session_id = str(uuid.uuid4()) - new_session = TerminalSession( - session_id=new_session_id, - instance_id=instance_id, - container_id=container_id, - startup_command=startup_command, - name=old_name or ("Session 1" if target_session_id == "default" else None), - ) - await new_session.start(startup_command=startup_command) - self._sessions[key] = new_session - - # Fire-and-forget DB insert - asyncio.create_task( - self._insert_db_session_row(new_session_id, instance_id, new_session.name) - ) - - return new_session + async def _cleanup_session(self, session: TerminalSession) -> None: + """Clean up a session.""" + if session.session_id in self._sessions: + del self._sessions[session.session_id] + self._last_client_message.pop(session.session_id, None) + await session.close() async def close_all(self) -> None: """Close all active sessions.""" sessions = list(self._sessions.values()) self._sessions.clear() + self._last_client_message.clear() for session in sessions: await session.close() - if self._idle_check_task and not self._idle_check_task.done(): - self._idle_check_task.cancel() - # Global terminal manager instance terminal_manager = TerminalManager() diff --git a/apps/api/src/services/terminal_session.py b/apps/api/src/services/terminal_session.py index dff40cd..7f3a490 100644 --- a/apps/api/src/services/terminal_session.py +++ b/apps/api/src/services/terminal_session.py @@ -1,136 +1,44 @@ -"""High-performance terminal session with asyncio-native I/O. - -Replaces blocking select.select() with event-driven asyncio.add_reader() -for sub-frame latency. Includes output batching and flow control. -""" +"""Terminal session management for tool instances.""" import asyncio +import contextlib +import fcntl import logging import os import pty -import signal +import select import struct -import fcntl -import time +import termios import uuid -from collections import deque -from typing import Any logger = logging.getLogger(__name__) class TerminalSession: - """Manages a single terminal session with event-driven PTY I/O. - - Uses asyncio.add_reader() instead of polling for near-zero read latency. - Output is batched (2ms window) and sent as binary WebSocket frames. - Flow control prevents memory bloat on fast output. - """ - - # Circular buffer for replay (10KB) - BUFFER_SIZE = 10 * 1024 - - # Idle timeout in seconds (30 minutes) - IDLE_TIMEOUT = 30 * 60 - - # Output batching window in seconds - BATCH_WINDOW_S = 0.002 # 2ms - - # Flow control: pause PTY reads when unacknowledged bytes exceed this - FLOW_CONTROL_PAUSE = 64 * 1024 - - # Flow control: resume PTY reads when unacknowledged bytes drop below this - FLOW_CONTROL_RESUME = 32 * 1024 - - # Max WebSocket frame size - MAX_FRAME_SIZE = 64 * 1024 - - # Session number counter per instance_id for auto-naming - _instance_counters: dict[str, int] = {} + """Manages a single terminal session connected to a docker container.""" def __init__( self, session_id: str, instance_id: uuid.UUID, container_id: str, - startup_command: str | None = None, - name: str | None = None, ) -> None: + """Initialize a terminal session.""" self.session_id = session_id self.instance_id = instance_id self.container_id = container_id - self.startup_command = startup_command self.process: asyncio.subprocess.Process | None = None self._closed = False self._master_fd: int | None = None + self._slave_fd: int | None = None + self._echo_enabled = True + self._exit_reason: str | None = None - # Circular buffer for output replay - self._output_buffer: deque[bytes] = deque(maxlen=self.BUFFER_SIZE) - self._buffer_size = 0 - - # WebSocket connections - self._websockets: set[Any] = set() - - # Activity tracking - self.last_activity = time.time() - - # Terminal size - self._cols = 80 - self._rows = 24 - - # Session metadata - self.name = name or self._generate_name(str(instance_id)) - self.status: str = "active" - - # Output batching - self._batch_buffer = bytearray() - self._batch_timer: asyncio.TimerHandle | None = None - self._batch_lock = asyncio.Lock() - - # Flow control - self._unacknowledged_bytes = 0 - self._paused = False - self._read_handler_set = False - self._flow_control_lock = asyncio.Lock() - - # Ack timeout fallback - self._ack_timeout_handle: asyncio.TimerHandle | None = None - - @classmethod - def _generate_name(cls, instance_id: str) -> str: - """Generate an auto-incremented session name for the instance.""" - count = cls._instance_counters.get(instance_id, 0) + 1 - cls._instance_counters[instance_id] = count - return f"Session {count}" - - async def start(self, startup_command: str | None = None) -> None: + async def start(self) -> None: """Start the docker exec process with a shell using a PTY.""" - # Create a pseudo-terminal on the host - self._master_fd, slave_fd = pty.openpty() + self._master_fd, self._slave_fd = pty.openpty() + self._set_terminal_size(80, 24) - # Set the terminal size initially - self._set_terminal_size(self._cols, self._rows) - logger.debug( - "Starting terminal session %s for container %s with initial size %sx%s", - self.session_id, - self.container_id, - self._cols, - self._rows, - ) - - # Build the shell command - cmd = startup_command or self.startup_command - if cmd: - shell_cmd = f'bash -c "{cmd}" || true; exec bash -il' - logger.debug( - "Using startup command for session %s: %s", - self.session_id, - cmd, - ) - else: - shell_cmd = "bash -il" - - # Start docker exec with the slave fd as stdin/stdout/stderr self.process = await asyncio.create_subprocess_exec( "docker", "exec", @@ -139,287 +47,112 @@ class TerminalSession: "TERM=xterm-256color", self.container_id, "bash", - "-c", - shell_cmd, - stdin=slave_fd, - stdout=slave_fd, - stderr=slave_fd, + "-il", + stdin=self._slave_fd, + stdout=self._slave_fd, + stderr=self._slave_fd, ) - # Close slave fd in parent process - os.close(slave_fd) + os.close(self._slave_fd) + self._slave_fd = None + self._echo_enabled = self._detect_echo_state() - self.last_activity = time.time() - - # Start event-driven reading - self._start_reading() - - def _start_reading(self) -> None: - """Register PTY master fd with asyncio event loop for event-driven reads.""" - if self._read_handler_set or self._master_fd is None or self._closed: + def _set_terminal_size(self, cols: int, rows: int) -> None: + """Set the terminal size using TIOCSWINSZ.""" + if self._master_fd is None: return - try: - loop = asyncio.get_event_loop() - loop.add_reader(self._master_fd, self._on_fd_readable) - self._read_handler_set = True - logger.debug("Started event-driven reading for session %s", self.session_id) - except Exception as exc: - logger.error( - "Failed to start reading for session %s: %s", self.session_id, exc - ) + tiocswinsz = 0x5414 + size = struct.pack("HHHH", rows, cols, 0, 0) + with contextlib.suppress(OSError): + fcntl.ioctl(self._master_fd, tiocswinsz, size) - def _stop_reading(self) -> None: - """Unregister PTY master fd from asyncio event loop.""" - if not self._read_handler_set or self._master_fd is None: - return + def _detect_echo_state(self) -> bool: + """Detect whether the PTY has echo enabled via termios.""" + if self._master_fd is None: + return True try: - loop = asyncio.get_event_loop() - loop.remove_reader(self._master_fd) - self._read_handler_set = False - except Exception: - pass + attrs = termios.tcgetattr(self._master_fd) + return bool(attrs[3] & termios.ECHO) + except OSError: + return True - def _on_fd_readable(self) -> None: - """Callback when PTY master fd has data available (called by event loop).""" + async def check_echo_state(self) -> bool | None: + """Check if echo state changed. Returns new state if changed, None otherwise.""" + current = self._detect_echo_state() + if current != self._echo_enabled: + self._echo_enabled = current + return current + return None + + @property + def echo_enabled(self) -> bool: + """Return whether the PTY currently has echo enabled.""" + return self._echo_enabled + + @property + def closed(self) -> bool: + """Return whether the session has been closed.""" + return self._closed + + async def read_output(self, select_timeout: float = 0.1) -> bytes: + """Read output from the PTY master.""" if self._master_fd is None or self._closed: - return - + return b"" try: - data = os.read(self._master_fd, 4096) - except (OSError, IOError) as exc: - logger.debug("PTY read error for session %s: %s", self.session_id, exc) - self._handle_eof() - return - - if not data: - # EOF: docker exec process exited - logger.debug("PTY EOF for session %s", self.session_id) - self._handle_eof() - return - - self._add_to_buffer(data) - self.last_activity = time.time() - - # Queue for batching + flow control - self._queue_output(data) - - def _add_to_buffer(self, data: bytes) -> None: - """Add data to circular buffer, maintaining size limit.""" - self._output_buffer.append(data) - self._buffer_size += len(data) - while self._buffer_size > self.BUFFER_SIZE and self._output_buffer: - removed = self._output_buffer.popleft() - self._buffer_size -= len(removed) - - def _queue_output(self, data: bytes) -> None: - """Add output to batch buffer and schedule flush.""" - self._batch_buffer.extend(data) - self._unacknowledged_bytes += len(data) - - # Check flow control - if self._unacknowledged_bytes > self.FLOW_CONTROL_PAUSE and not self._paused: - self._pause_output() - - # Schedule batch flush if not already scheduled - if self._batch_timer is None: - loop = asyncio.get_event_loop() - self._batch_timer = loop.call_later( - self.BATCH_WINDOW_S, - self._flush_batch_sync, + readable, _, _ = select.select( + [self._master_fd], + [], + [], + select_timeout, ) - - def _flush_batch_sync(self) -> None: - """Synchronous entry point for batch flush (called from event loop).""" - self._batch_timer = None - if not self._batch_buffer or not self._websockets: - self._batch_buffer.clear() - return - - payload = bytes(self._batch_buffer) - self._batch_buffer.clear() - - # Send to all websockets (asyncio.create_task for async send) - dead_sockets = set() - for ws in list(self._websockets): - try: - asyncio.create_task(self._send_bytes(ws, payload)) - except Exception: - dead_sockets.add(ws) - - if dead_sockets: - self._websockets -= dead_sockets - - async def _send_bytes(self, ws: Any, payload: bytes) -> None: - """Send bytes to a single websocket, catching errors.""" - try: - await ws.send_bytes(payload) - except Exception: - self._websockets.discard(ws) - - def acknowledge_data(self, char_count: int) -> None: - """Client acknowledges processing char_count bytes. - - Called from the WebSocket handler when the client sends an 'ack' message. - """ - self._unacknowledged_bytes = max(0, self._unacknowledged_bytes - char_count) - - if self._paused and self._unacknowledged_bytes < self.FLOW_CONTROL_RESUME: - self._resume_output() - - # Reset ack timeout - if self._ack_timeout_handle: - self._ack_timeout_handle.cancel() - loop = asyncio.get_event_loop() - self._ack_timeout_handle = loop.call_later(5.0, self._ack_timeout_fallback) - - def _ack_timeout_fallback(self) -> None: - """If no ack received for 5s, assume client is dead and resume.""" - logger.warning( - "Flow control ack timeout for session %s, resuming output", - self.session_id, - ) - self._unacknowledged_bytes = 0 - if self._paused: - self._resume_output() - - def _pause_output(self) -> None: - """Pause reading from PTY due to flow control.""" - self._paused = True - self._stop_reading() - logger.debug( - "Paused output for session %s (%d unacked)", - self.session_id, - self._unacknowledged_bytes, - ) - - def _resume_output(self) -> None: - """Resume reading from PTY.""" - self._paused = False - self._start_reading() - logger.debug("Resumed output for session %s", self.session_id) - - def get_buffer(self) -> bytes: - """Get buffered output for replay.""" - return b"".join(self._output_buffer) - - def _handle_eof(self) -> None: - """Handle PTY EOF: process died, close websockets to force reconnect.""" - self._stop_reading() - # Mark process as done so is_alive() returns False - if self.process is not None and self.process.returncode is None: - # Force returncode to a non-None value since the process is dead - # but asyncio.subprocess may not have set it yet - try: - self.process._transport.close() # type: ignore[attr-defined] - except Exception: - pass - # Close all websockets to force frontend reconnection - dead_sockets = set(self._websockets) - self._websockets.clear() - for ws in dead_sockets: - try: - asyncio.create_task( - ws.close(code=4001, reason="Session process exited") - ) - except Exception: - pass - logger.info("Session %s EOF handled, websockets closed", self.session_id) + if readable: + return os.read(self._master_fd, 8192) + return b"" + except (OSError, ValueError): + return b"" async def write_input(self, data: bytes) -> None: """Write input to the PTY master.""" if self._master_fd is None or self._closed: return - try: + with contextlib.suppress(OSError): os.write(self._master_fd, data) - self.last_activity = time.time() - except (OSError, IOError) as exc: - logger.debug("PTY write error for session %s: %s", self.session_id, exc) - self._handle_eof() - - def _set_terminal_size(self, cols: int, rows: int) -> None: - """Set the terminal size using TIOCSWINSZ.""" - if self._master_fd is None: - logger.warning("Cannot resize: master_fd is None (session not started)") - return - TIOCSWINSZ = 0x5414 - size = struct.pack("HHHH", rows, cols, 0, 0) - try: - fcntl.ioctl(self._master_fd, TIOCSWINSZ, size) - logger.debug("Resized PTY to %sx%s (fd=%s)", cols, rows, self._master_fd) - except (OSError, IOError) as e: - logger.error("Failed to resize PTY: %s", e) async def resize(self, cols: int, rows: int) -> None: """Resize the terminal.""" if self._closed: - logger.warning("Cannot resize: session is closed") return - - if cols == self._cols and rows == self._rows: - return - - self._cols = cols - self._rows = rows - logger.debug( - "resize() called for session %s: %sx%s", self.session_id, cols, rows - ) self._set_terminal_size(cols, rows) - # Send SIGWINCH to docker exec process - if self.process and self.process.pid: - try: - os.kill(self.process.pid, signal.SIGWINCH) - except ProcessLookupError: - logger.warning("docker exec process %s not found", self.process.pid) - except Exception as e: - logger.warning("Failed to send SIGWINCH: %s", e) - - async def reset(self) -> None: - """Reset the session by killing the process and clearing state.""" - self.status = "resetting" - await self.close() - self._closed = False - self._output_buffer.clear() - self._buffer_size = 0 - self._websockets.clear() - self._batch_buffer.clear() - self._batch_timer = None - self._unacknowledged_bytes = 0 - self._paused = False - self._read_handler_set = False - self.process = None - self._master_fd = None - self.status = "active" + def get_exit_reason(self) -> str | None: + """Return the reason the session ended, if known.""" + return self._exit_reason async def close(self) -> None: """Close the session and cleanup.""" if self._closed: return self._closed = True - self.status = "closed" - self._stop_reading() - - if self._batch_timer: - self._batch_timer.cancel() - self._batch_timer = None - - if self._ack_timeout_handle: - self._ack_timeout_handle.cancel() - self._ack_timeout_handle = None + # Determine exit reason + if self.process is not None and self.process.returncode is not None: + if self.process.returncode == 0: + self._exit_reason = "process_exit" + else: + self._exit_reason = "process_exit" + else: + self._exit_reason = "timeout" if self._master_fd is not None: - try: + with contextlib.suppress(OSError): os.close(self._master_fd) - except OSError: - pass self._master_fd = None if self.process is not None: try: self.process.kill() await asyncio.wait_for(self.process.wait(), timeout=2.0) - except (asyncio.TimeoutError, ProcessLookupError): + except (TimeoutError, ProcessLookupError): pass def is_alive(self) -> bool: @@ -427,41 +160,3 @@ class TerminalSession: if self.process is None: return False return self.process.returncode is None - - def is_idle(self) -> bool: - """Check if the session has been idle for too long.""" - if self._websockets: - return False - return time.time() - self.last_activity > self.IDLE_TIMEOUT - - def attach_websocket(self, websocket: Any) -> None: - """Attach a WebSocket to this session.""" - self._websockets.add(websocket) - self.last_activity = time.time() - - def detach_websocket(self, websocket: Any) -> None: - """Detach a WebSocket from this session.""" - self._websockets.discard(websocket) - - def has_websockets(self) -> bool: - """Check if any WebSockets are attached.""" - return len(self._websockets) > 0 - - async def send_to_all(self, data: bytes) -> None: - """Send data to all attached WebSockets (used for control messages).""" - dead_sockets = set() - for ws in self._websockets: - try: - await ws.send_bytes(data) - except Exception: - dead_sockets.add(ws) - for ws in dead_sockets: - self._websockets.discard(ws) - - async def read_output(self) -> bytes: - """Legacy method: read output synchronously. - - With event-driven I/O, output is automatically sent to websockets. - This method returns any buffered data for callers that poll. - """ - return b"" diff --git a/apps/api/src/utils/git_history.py b/apps/api/src/utils/git_history.py index 7f18dcd..9001954 100644 --- a/apps/api/src/utils/git_history.py +++ b/apps/api/src/utils/git_history.py @@ -60,9 +60,12 @@ def get_commit_history(repo_path: str, branch: str | None = None, limit: int = 1 Returns structured data including commits, branches, and graph information. """ - # Get list of branches - branches_output = _run_git_command(repo_path, ["branch", "-a", "--format=%(refname:short)"]) - branches = [b.strip() for b in branches_output.strip().split("\n") if b.strip()] + # Get list of branches (may fail for empty repos) + try: + branches_output = _run_git_command(repo_path, ["branch", "-a", "--format=%(refname:short)"]) + branches = [b.strip() for b in branches_output.strip().split("\n") if b.strip()] + except RuntimeError: + branches = [] # Build git log command - use NULL bytes as separators to avoid parsing issues log_args = [ @@ -76,7 +79,16 @@ def get_commit_history(repo_path: str, branch: str | None = None, limit: int = 1 else: log_args.append("--all") - log_output = _run_git_command(repo_path, log_args) + try: + log_output = _run_git_command(repo_path, log_args) + except RuntimeError: + # Empty repo or no commits + return { + "commits": [], + "branches": branches, + "total_commits": 0, + "graph_data": {"nodes": [], "edges": []}, + } # Get branch info for each commit branch_map = _get_branch_map(repo_path) @@ -113,8 +125,11 @@ def get_commit_history(repo_path: str, branch: str | None = None, limit: int = 1 ) # Get total commit count - count_output = _run_git_command(repo_path, ["rev-list", "--all", "--count"]) - total_commits = int(count_output.strip()) if count_output.strip() else 0 + try: + count_output = _run_git_command(repo_path, ["rev-list", "--all", "--count"]) + total_commits = int(count_output.strip()) if count_output.strip() else 0 + except RuntimeError: + total_commits = 0 # Build graph data and generate graph symbols graph_data = _build_graph_data(commits) diff --git a/apps/api/tests/integration/test_config_profiles_api.py b/apps/api/tests/integration/test_config_profiles_api.py index b1a6011..06c745f 100644 --- a/apps/api/tests/integration/test_config_profiles_api.py +++ b/apps/api/tests/integration/test_config_profiles_api.py @@ -1,4 +1,7 @@ +"""Integration tests for config profiles API.""" + import uuid + import pytest from fastapi.testclient import TestClient @@ -17,7 +20,9 @@ class TestConfigProfilesAPI: response = authenticated_client.get("/config-profiles") assert response.status_code == 200 data = response.json() - assert isinstance(data, list) + assert isinstance(data, dict) + assert "profiles" in data + assert isinstance(data["profiles"], list) def test_create_config_profile_successfully(self, authenticated_client: TestClient) -> None: """Test creating a config profile.""" @@ -26,99 +31,48 @@ class TestConfigProfilesAPI: json={ "name": "test-profile", "description": "Test profile", - "env_vars": {"VAR": "value"}, - "runtime_hints": {"start_command": "npm start"}, - "mounts": [{"target": "/app", "mode": "rw", "files": {}}], - "files": {"test.txt": "hello"}, }, ) assert response.status_code == 201 data = response.json() assert data["name"] == "test-profile" - assert data["env_vars"] == {"VAR": "value"} - assert data["files"] == {"test.txt": "hello"} - assert data["mounts"][0]["target"] == "/app" + assert data["description"] == "Test profile" def test_create_config_profile_duplicate_name(self, authenticated_client: TestClient) -> None: """Test that duplicate profile names are rejected.""" - # Create first profile - response = authenticated_client.post( + authenticated_client.post( "/config-profiles", - json={ - "name": "duplicate-profile", - "env_vars": {}, - "files": {}, - }, + json={"name": "duplicate-profile"}, ) - assert response.status_code == 201 - # Try to create second with same name response = authenticated_client.post( "/config-profiles", - json={ - "name": "duplicate-profile", - "env_vars": {}, - "files": {}, - }, + json={"name": "duplicate-profile"}, ) assert response.status_code == 409 - def test_create_config_profile_exceeds_size_limit(self, authenticated_client: TestClient) -> None: - """Test that profiles exceeding 10MB are rejected.""" - large_content = "x" * (11 * 1024 * 1024) # 11MB + def test_create_config_profile_empty_name(self, authenticated_client: TestClient) -> None: + """Test that empty profile names are rejected.""" response = authenticated_client.post( "/config-profiles", - json={ - "name": "large-profile", - "env_vars": {}, - "files": {"large.txt": large_content}, - }, - ) - assert response.status_code == 413 - - def test_create_config_profile_invalid_file_path(self, authenticated_client: TestClient) -> None: - """Test that invalid file paths are rejected.""" - response = authenticated_client.post( - "/config-profiles", - json={ - "name": "bad-profile", - "env_vars": {}, - "files": {"../../../etc/passwd": "malicious"}, - }, - ) - assert response.status_code == 422 - - def test_create_config_profile_invalid_mount_target(self, authenticated_client: TestClient) -> None: - """Test that invalid mount targets are rejected.""" - response = authenticated_client.post( - "/config-profiles", - json={ - "name": "bad-mount-profile", - "env_vars": {}, - "files": {}, - "mounts": [{"target": "relative/path", "mode": "rw", "files": {}}], - }, + json={"name": " "}, ) assert response.status_code == 422 def test_get_config_profile_by_id(self, authenticated_client: TestClient) -> None: """Test getting a config profile by ID.""" - # Create profile first create_response = authenticated_client.post( "/config-profiles", - json={ - "name": "get-test", - "env_vars": {}, - "files": {}, - }, + json={"name": "get-test"}, ) profile_id = create_response.json()["id"] - # Get it back response = authenticated_client.get(f"/config-profiles/{profile_id}") assert response.status_code == 200 data = response.json() assert data["name"] == "get-test" + assert "includes" in data + assert "mounts" in data def test_get_config_profile_not_found(self, authenticated_client: TestClient) -> None: """Test getting a non-existent profile.""" @@ -127,327 +81,381 @@ class TestConfigProfilesAPI: def test_update_config_profile_successfully(self, authenticated_client: TestClient) -> None: """Test updating a config profile.""" - # Create profile first create_response = authenticated_client.post( "/config-profiles", - json={ - "name": "update-test", - "env_vars": {}, - "files": {}, - }, + json={"name": "update-test"}, ) profile_id = create_response.json()["id"] - # Update it response = authenticated_client.put( f"/config-profiles/{profile_id}", - json={ - "name": "updated-name", - "env_vars": {"NEW_VAR": "new_value"}, - }, + json={"name": "updated-name", "description": "updated desc"}, ) assert response.status_code == 200 data = response.json() assert data["name"] == "updated-name" - assert data["env_vars"] == {"NEW_VAR": "new_value"} + assert data["description"] == "updated desc" def test_delete_config_profile_successfully(self, authenticated_client: TestClient) -> None: """Test deleting a config profile.""" - # Create profile first create_response = authenticated_client.post( "/config-profiles", - json={ - "name": "delete-test", - "env_vars": {}, - "files": {}, - }, + json={"name": "delete-test"}, ) profile_id = create_response.json()["id"] - # Delete it response = authenticated_client.delete(f"/config-profiles/{profile_id}") assert response.status_code == 204 - # Verify it's gone get_response = authenticated_client.get(f"/config-profiles/{profile_id}") assert get_response.status_code == 404 - def test_update_profile_includes_successfully(self, authenticated_client: TestClient) -> None: - """Test updating profile includes.""" - # Create base profile - base_response = authenticated_client.post( + def test_profile_access_check(self, authenticated_client: TestClient) -> None: + """Test that users can only access their own profiles.""" + # Create a profile + create_response = authenticated_client.post( "/config-profiles", - json={ - "name": "base-profile", - "env_vars": {"BASE_VAR": "base_value"}, - "files": {}, - }, + json={"name": "access-test"}, ) - base_id = base_response.json()["id"] + profile_id = create_response.json()["id"] - # Create child profile - child_response = authenticated_client.post( - "/config-profiles", - json={ - "name": "child-profile", - "env_vars": {}, - "files": {}, - }, - ) - child_id = child_response.json()["id"] - - # Update includes - response = authenticated_client.put( - f"/config-profiles/{child_id}/includes", - json={"includes": [base_id]}, - ) + # The profile should be accessible + response = authenticated_client.get(f"/config-profiles/{profile_id}") assert response.status_code == 200 - data = response.json() - print(f"Response data: {data}") - print(f"Includes: {data.get('includes', 'NO INCLUDES KEY')}") - assert len(data["includes"]) == 1, f"Expected 1 include, got {len(data.get('includes', []))}: {data.get('includes', [])}" - assert data["includes"][0]["included_profile_id"] == base_id - def test_update_profile_includes_cycle_detection(self, authenticated_client: TestClient) -> None: - """Test that include cycles are detected.""" - # Create profile A - a_response = authenticated_client.post( + +@pytest.mark.integration +class TestConfigProfileIncludes: + """Integration tests for config profile includes.""" + + def test_add_include_successfully(self, authenticated_client: TestClient) -> None: + """Test adding an include to a profile.""" + # Create two profiles + profile1 = authenticated_client.post( "/config-profiles", - json={ - "name": "profile-a", - "env_vars": {}, - "files": {}, - }, - ) - a_id = a_response.json()["id"] - - # Create profile B - b_response = authenticated_client.post( + json={"name": "profile-1"}, + ).json() + profile2 = authenticated_client.post( "/config-profiles", - json={ - "name": "profile-b", - "env_vars": {}, - "files": {}, - }, - ) - b_id = b_response.json()["id"] - - # Make B include A - authenticated_client.put( - f"/config-profiles/{b_id}/includes", - json={"includes": [a_id]}, - ) - - # Try to make A include B (would create cycle) - response = authenticated_client.put( - f"/config-profiles/{a_id}/includes", - json={"includes": [b_id]}, - ) - assert response.status_code == 400 - - def test_preview_config_profile_successfully(self, authenticated_client: TestClient) -> None: - """Test previewing a resolved config profile.""" - # Create base profile - base_response = authenticated_client.post( - "/config-profiles", - json={ - "name": "preview-base", - "env_vars": {"BASE_VAR": "base"}, - "files": {}, - }, - ) - base_id = base_response.json()["id"] - - # Create child profile - child_response = authenticated_client.post( - "/config-profiles", - json={ - "name": "preview-child", - "env_vars": {"CHILD_VAR": "child"}, - "files": {}, - }, - ) - child_id = child_response.json()["id"] - - # Make child include base - authenticated_client.put( - f"/config-profiles/{child_id}/includes", - json={"includes": [base_id]}, - ) - - # Preview child - response = authenticated_client.get(f"/config-profiles/{child_id}/preview") - assert response.status_code == 200 - data = response.json() - assert data["profile_name"] == "preview-child" - assert data["env_vars"]["BASE_VAR"] == "base" - assert data["env_vars"]["CHILD_VAR"] == "child" - assert len(data["included_profiles"]) == 1 - - def test_resolve_default_profile(self, authenticated_client: TestClient) -> None: - """Test resolving default profile for project/tool.""" - # Create a global default profile (no project/tool scoping) - authenticated_client.post( - "/config-profiles", - json={ - "name": "default-profile", - "env_vars": {}, - "files": {}, - "is_default": True, - }, - ) - - # Resolve default with random project/tool (should fall back to global) - project_id = str(uuid.uuid4()) - tool_type_id = str(uuid.uuid4()) - response = authenticated_client.get( - "/config-profiles/defaults/resolve", - params={"project_id": project_id, "tool_type_id": tool_type_id}, - ) - assert response.status_code == 200 - data = response.json() - assert data["profile_name"] == "default-profile" - - def test_resolve_default_profile_no_match(self, authenticated_client: TestClient) -> None: - """Test resolving default profile when no profiles exist.""" - project_id = str(uuid.uuid4()) - tool_type_id = str(uuid.uuid4()) - - response = authenticated_client.get( - "/config-profiles/defaults/resolve", - params={"project_id": project_id, "tool_type_id": tool_type_id}, - ) - assert response.status_code == 200 - data = response.json() - assert data["profile_id"] is None - - def test_create_config_profile_with_git_mounts(self, authenticated_client: TestClient, test_project_and_repo) -> None: - """Test creating a config profile with git mounts.""" - _project_id, repo_id = test_project_and_repo + json={"name": "profile-2"}, + ).json() + # Add include response = authenticated_client.post( - "/config-profiles", - json={ - "name": "git-mount-profile", - "env_vars": {}, - "files": {}, - "git_mounts": [ - { - "remote_url": "https://github.com/user/repo.git", - "source_path": ".", - "target_path": "/app", - "branch": "main", - } - ], - }, + f"/config-profiles/{profile1['id']}/includes", + json={"included_profile_id": profile2["id"], "order_index": 0}, ) assert response.status_code == 201 data = response.json() - assert data["name"] == "git-mount-profile" - assert len(data["git_mounts"]) == 1 - assert data["git_mounts"][0]["target_path"] == "/app" - assert data["git_mounts"][0]["branch"] == "main" + assert data["included_profile_id"] == profile2["id"] + assert data["included_profile_name"] == "profile-2" - def test_update_config_profile_git_mounts(self, authenticated_client: TestClient, test_project_and_repo) -> None: - """Test updating git mounts on a config profile.""" - _project_id, repo_id = test_project_and_repo - - # Create profile first - create_response = authenticated_client.post( + def test_add_self_include_rejected(self, authenticated_client: TestClient) -> None: + """Test that self-includes are rejected.""" + profile = authenticated_client.post( "/config-profiles", - json={ - "name": "update-git-mounts", - "env_vars": {}, - "files": {}, - }, - ) - profile_id = create_response.json()["id"] + json={"name": "self-include-test"}, + ).json() + + response = authenticated_client.post( + f"/config-profiles/{profile['id']}/includes", + json={"included_profile_id": profile["id"], "order_index": 0}, + ) + assert response.status_code == 400 + + def test_add_include_cycle_rejected(self, authenticated_client: TestClient) -> None: + """Test that circular includes are rejected.""" + profile1 = authenticated_client.post( + "/config-profiles", + json={"name": "cycle-1"}, + ).json() + profile2 = authenticated_client.post( + "/config-profiles", + json={"name": "cycle-2"}, + ).json() + + # Add profile1 includes profile2 + authenticated_client.post( + f"/config-profiles/{profile1['id']}/includes", + json={"included_profile_id": profile2["id"], "order_index": 0}, + ) + + # Try to add profile2 includes profile1 (creates cycle) + response = authenticated_client.post( + f"/config-profiles/{profile2['id']}/includes", + json={"included_profile_id": profile1["id"], "order_index": 0}, + ) + assert response.status_code == 400 + + def test_add_deep_cycle_rejected(self, authenticated_client: TestClient) -> None: + """Test that deep circular includes are rejected.""" + p1 = authenticated_client.post( + "/config-profiles", json={"name": "deep-1"} + ).json() + p2 = authenticated_client.post( + "/config-profiles", json={"name": "deep-2"} + ).json() + p3 = authenticated_client.post( + "/config-profiles", json={"name": "deep-3"} + ).json() + + # p1 -> p2 -> p3 + authenticated_client.post( + f"/config-profiles/{p1['id']}/includes", + json={"included_profile_id": p2["id"], "order_index": 0}, + ) + authenticated_client.post( + f"/config-profiles/{p2['id']}/includes", + json={"included_profile_id": p3["id"], "order_index": 0}, + ) + + # Try p3 -> p1 (creates cycle) + response = authenticated_client.post( + f"/config-profiles/{p3['id']}/includes", + json={"included_profile_id": p1["id"], "order_index": 0}, + ) + assert response.status_code == 400 + + def test_add_duplicate_include_rejected(self, authenticated_client: TestClient) -> None: + """Test that duplicate includes are rejected.""" + p1 = authenticated_client.post( + "/config-profiles", json={"name": "dup-1"} + ).json() + p2 = authenticated_client.post( + "/config-profiles", json={"name": "dup-2"} + ).json() + + authenticated_client.post( + f"/config-profiles/{p1['id']}/includes", + json={"included_profile_id": p2["id"], "order_index": 0}, + ) + + response = authenticated_client.post( + f"/config-profiles/{p1['id']}/includes", + json={"included_profile_id": p2["id"], "order_index": 1}, + ) + assert response.status_code == 409 + + def test_list_includes(self, authenticated_client: TestClient) -> None: + """Test listing includes for a profile.""" + p1 = authenticated_client.post( + "/config-profiles", json={"name": "list-inc-1"} + ).json() + p2 = authenticated_client.post( + "/config-profiles", json={"name": "list-inc-2"} + ).json() + + authenticated_client.post( + f"/config-profiles/{p1['id']}/includes", + json={"included_profile_id": p2["id"], "order_index": 0}, + ) + + response = authenticated_client.get(f"/config-profiles/{p1['id']}/includes") + assert response.status_code == 200 + data = response.json() + assert len(data["includes"]) == 1 + + def test_update_include_order(self, authenticated_client: TestClient) -> None: + """Test updating include order index.""" + p1 = authenticated_client.post( + "/config-profiles", json={"name": "order-1"} + ).json() + p2 = authenticated_client.post( + "/config-profiles", json={"name": "order-2"} + ).json() + + inc = authenticated_client.post( + f"/config-profiles/{p1['id']}/includes", + json={"included_profile_id": p2["id"], "order_index": 0}, + ).json() - # Update with git mounts response = authenticated_client.put( - f"/config-profiles/{profile_id}", - json={ - "git_mounts": [ - { - "remote_url": "https://github.com/user/repo.git", - "source_path": "config", - "target_path": "/config", - } - ], - }, + f"/config-profiles/{p1['id']}/includes/{inc['id']}", + json={"order_index": 5}, ) assert response.status_code == 200 - data = response.json() - assert len(data["git_mounts"]) == 1 - assert data["git_mounts"][0]["source_path"] == "config" + assert response.json()["order_index"] == 5 - def test_create_config_profile_invalid_git_mount_source_path(self, authenticated_client: TestClient, test_project_and_repo) -> None: - """Test that invalid git mount source paths are rejected.""" - _project_id, repo_id = test_project_and_repo + def test_remove_include(self, authenticated_client: TestClient) -> None: + """Test removing an include.""" + p1 = authenticated_client.post( + "/config-profiles", json={"name": "rem-1"} + ).json() + p2 = authenticated_client.post( + "/config-profiles", json={"name": "rem-2"} + ).json() + + inc = authenticated_client.post( + f"/config-profiles/{p1['id']}/includes", + json={"included_profile_id": p2["id"], "order_index": 0}, + ).json() + + response = authenticated_client.delete( + f"/config-profiles/{p1['id']}/includes/{inc['id']}" + ) + assert response.status_code == 204 + + +@pytest.mark.integration +class TestConfigProfileMounts: + """Integration tests for config profile mounts.""" + + def test_add_mount_successfully(self, authenticated_client: TestClient) -> None: + """Test adding a mount to a profile.""" + profile = authenticated_client.post( + "/config-profiles", + json={"name": "mount-test"}, + ).json() response = authenticated_client.post( + f"/config-profiles/{profile['id']}/mounts", + json={"target_path": "/etc/config", "files": {"test.txt": "hello"}, "order_index": 0}, + ) + assert response.status_code == 201 + data = response.json() + assert data["target_path"] == "/etc/config" + assert data["files"] == {"test.txt": "hello"} + + def test_add_mount_relative_path_rejected(self, authenticated_client: TestClient) -> None: + """Test that relative mount paths are rejected.""" + profile = authenticated_client.post( "/config-profiles", - json={ - "name": "bad-git-mount", - "env_vars": {}, - "files": {}, - "git_mounts": [ - { - "remote_url": "https://github.com/user/repo.git", - "source_path": "/absolute/path", - "target_path": "/app", - } - ], - }, + json={"name": "rel-path-test"}, + ).json() + + response = authenticated_client.post( + f"/config-profiles/{profile['id']}/mounts", + json={"target_path": "etc/config", "files": {"test.txt": "hello"}}, ) assert response.status_code == 422 - def test_create_config_profile_invalid_git_mount_target_path_traversal(self, authenticated_client: TestClient, test_project_and_repo) -> None: - """Test that git mount target paths with traversal are rejected.""" - _project_id, repo_id = test_project_and_repo + def test_add_target_path_traversal_rejected(self, authenticated_client: TestClient) -> None: + """Test that path traversal in mount paths is rejected.""" + profile = authenticated_client.post( + "/config-profiles", + json={"name": "traversal-test"}, + ).json() response = authenticated_client.post( - "/config-profiles", - json={ - "name": "bad-git-mount-target", - "env_vars": {}, - "files": {}, - "git_mounts": [ - { - "remote_url": "https://github.com/user/repo.git", - "source_path": ".", - "target_path": "../../../etc/passwd", - } - ], - }, + f"/config-profiles/{profile['id']}/mounts", + json={"target_path": "/etc/../passwd", "files": {"test.txt": "hello"}}, ) assert response.status_code == 422 - def test_preview_config_profile_with_git_mounts(self, authenticated_client: TestClient, test_project_and_repo) -> None: - """Test previewing a profile with git mounts.""" - _project_id, repo_id = test_project_and_repo - - # Create profile with git mounts - create_response = authenticated_client.post( + def test_add_duplicate_mount_rejected(self, authenticated_client: TestClient) -> None: + """Test that duplicate mount paths are rejected.""" + profile = authenticated_client.post( "/config-profiles", - json={ - "name": "preview-git-mounts", - "env_vars": {}, - "files": {}, - "git_mounts": [ - { - "remote_url": "https://github.com/user/repo.git", - "source_path": ".", - "target_path": "/app", - } - ], - }, - ) - profile_id = create_response.json()["id"] + json={"name": "dup-mount-test"}, + ).json() - # Preview - response = authenticated_client.get(f"/config-profiles/{profile_id}/preview") + authenticated_client.post( + f"/config-profiles/{profile['id']}/mounts", + json={"target_path": "/etc/config", "files": {"test.txt": "hello"}}, + ) + + response = authenticated_client.post( + f"/config-profiles/{profile['id']}/mounts", + json={"target_path": "/etc/config", "files": {"test.txt": "world"}}, + ) + assert response.status_code == 409 + + def test_update_mount(self, authenticated_client: TestClient) -> None: + """Test updating a mount.""" + profile = authenticated_client.post( + "/config-profiles", + json={"name": "update-mount-test"}, + ).json() + + mount = authenticated_client.post( + f"/config-profiles/{profile['id']}/mounts", + json={"target_path": "/old/path", "files": {"test.txt": "old"}}, + ).json() + + response = authenticated_client.put( + f"/config-profiles/{profile['id']}/mounts/{mount['id']}", + json={"target_path": "/new/path", "files": {"test.txt": "new"}, "order_index": 2}, + ) assert response.status_code == 200 data = response.json() - assert len(data["git_mounts"]) == 1 - assert data["git_mounts"][0]["remote_url"] == "https://github.com/user/repo.git" + assert data["target_path"] == "/new/path" + assert data["files"] == {"test.txt": "new"} + assert data["order_index"] == 2 + + def test_remove_mount(self, authenticated_client: TestClient) -> None: + """Test removing a mount.""" + profile = authenticated_client.post( + "/config-profiles", + json={"name": "rem-mount-test"}, + ).json() + + mount = authenticated_client.post( + f"/config-profiles/{profile['id']}/mounts", + json={"target_path": "/tmp/test", "files": {"test.txt": "x"}}, + ).json() + + response = authenticated_client.delete( + f"/config-profiles/{profile['id']}/mounts/{mount['id']}" + ) + assert response.status_code == 204 + + +@pytest.mark.integration +class TestConfigProfileDefaults: + """Integration tests for default profile APIs.""" + + def test_get_default_profiles_empty(self, authenticated_client: TestClient) -> None: + """Test getting default profiles when none are set.""" + response = authenticated_client.get("/config-profiles/defaults") + assert response.status_code == 200 + data = response.json() + assert data["default_profiles"] == {} + + def test_set_default_profiles(self, authenticated_client: TestClient) -> None: + """Test setting default profiles.""" + profile = authenticated_client.post( + "/config-profiles", + json={"name": "default-test"}, + ).json() + + response = authenticated_client.put( + "/config-profiles/defaults", + json={"default_profiles": {"code-server": profile["id"]}}, + ) + assert response.status_code == 200 + data = response.json() + assert data["default_profiles"]["code-server"] == profile["id"] + + def test_set_default_profiles_invalid_profile(self, authenticated_client: TestClient) -> None: + """Test setting default profiles with invalid profile ID.""" + response = authenticated_client.put( + "/config-profiles/defaults", + json={"default_profiles": {"code-server": str(uuid.uuid4())}}, + ) + assert response.status_code == 404 + + def test_get_default_profile_for_tool_type(self, authenticated_client: TestClient) -> None: + """Test getting default profile for a specific tool type.""" + profile = authenticated_client.post( + "/config-profiles", + json={"name": "tool-default-test"}, + ).json() + + authenticated_client.put( + "/config-profiles/defaults", + json={"default_profiles": {"jupyter-notebook": profile["id"]}}, + ) + + response = authenticated_client.get("/config-profiles/defaults/jupyter-notebook") + assert response.status_code == 200 + data = response.json() + assert data["tool_type_id"] == "jupyter-notebook" + assert data["profile_id"] == profile["id"] + + def test_get_default_profile_for_tool_type_not_set(self, authenticated_client: TestClient) -> None: + """Test getting default profile when not set.""" + response = authenticated_client.get("/config-profiles/defaults/opencode") + assert response.status_code == 200 + data = response.json() + assert data["tool_type_id"] == "opencode" + assert data["profile_id"] is None diff --git a/apps/api/tests/integration/test_projects_api.py b/apps/api/tests/integration/test_projects_api.py index 6fc491a..3663249 100644 --- a/apps/api/tests/integration/test_projects_api.py +++ b/apps/api/tests/integration/test_projects_api.py @@ -1,5 +1,5 @@ import uuid -from datetime import UTC, datetime, timedelta +from datetime import datetime, timedelta, timezone import asyncio import pytest @@ -59,7 +59,7 @@ def _mint_token(user_id: str) -> str: subject=user_id, email="test@headquarter.local", name="Test User", - expires_at=datetime.now(UTC) + timedelta(minutes=15), + expires_at=datetime.now(timezone.utc) + timedelta(minutes=15), ) diff --git a/apps/api/tests/integration/test_tool_types_api.py b/apps/api/tests/integration/test_tool_types_api.py index b9df8dc..e5a0cc7 100644 --- a/apps/api/tests/integration/test_tool_types_api.py +++ b/apps/api/tests/integration/test_tool_types_api.py @@ -1,5 +1,5 @@ import uuid -from datetime import UTC, datetime, timedelta +from datetime import datetime, timedelta, timezone import asyncio import pytest @@ -59,7 +59,7 @@ def _mint_token(user_id: str) -> str: subject=user_id, email="test@headquarter.local", name="Test User", - expires_at=datetime.now(UTC) + timedelta(minutes=15), + expires_at=datetime.now(timezone.utc) + timedelta(minutes=15), ) diff --git a/apps/api/tests/integration/test_users_api.py b/apps/api/tests/integration/test_users_api.py index bb9a119..0ca300c 100644 --- a/apps/api/tests/integration/test_users_api.py +++ b/apps/api/tests/integration/test_users_api.py @@ -1,5 +1,5 @@ import uuid -from datetime import UTC, datetime, timedelta +from datetime import datetime, timedelta, timezone import asyncio import io @@ -83,7 +83,7 @@ def _create_auth_cookie(user_id: str) -> str: subject=user_id, email="test@headquarter.local", name="Test User", - expires_at=datetime.now(UTC) + timedelta(minutes=15), + expires_at=datetime.now(timezone.utc) + timedelta(minutes=15), ) diff --git a/apps/api/tests/unit/test_migration_metadata.py b/apps/api/tests/unit/test_migration_metadata.py index c39b91e..311fae1 100644 --- a/apps/api/tests/unit/test_migration_metadata.py +++ b/apps/api/tests/unit/test_migration_metadata.py @@ -39,3 +39,18 @@ def test_refresh_tokens_migration_has_expected_revision_chain() -> None: assert module.revision == "0002_refresh_tokens" assert module.down_revision == "0001_initial_schema" + + +@pytest.mark.unit +def test_config_profiles_migration_has_expected_revision_chain() -> None: + migration_path = Path(__file__).resolve().parents[2] / "alembic" / "versions" / "0013_add_config_profiles.py" + spec = spec_from_file_location("add_config_profiles", migration_path) + + assert spec is not None + assert spec.loader is not None + + module = module_from_spec(spec) + spec.loader.exec_module(module) + + assert module.revision == "0013_add_config_profiles" + assert module.down_revision == "0012_default_port_req" diff --git a/apps/api/tests/unit/test_profile_resolver.py b/apps/api/tests/unit/test_profile_resolver.py new file mode 100644 index 0000000..ac065a3 --- /dev/null +++ b/apps/api/tests/unit/test_profile_resolver.py @@ -0,0 +1,463 @@ +"""Unit tests for the profile resolver service.""" + +import uuid +from unittest.mock import MagicMock + +import pytest + +from src.services.profile_resolver import ( + ProfileCycleError, + ResolvedProfileOutput, + resolve_profile, +) + + +def _make_profile( + name: str, + env_vars: dict[str, str] | None = None, + start_command: str | None = None, + working_directory: str | None = None, + port: int | None = None, + mounts: list[MagicMock] | None = None, + includes: list[MagicMock] | None = None, +) -> MagicMock: + """Create a mock ConfigProfile for testing.""" + profile = MagicMock() + profile.id = uuid.uuid4() + profile.name = name + profile.environment_variables = env_vars or {} + profile.start_command = start_command + profile.working_directory = working_directory + profile.port = port + profile.mounts = mounts or [] + profile.includes = includes or [] + return profile + + +def _make_include(included_profile: MagicMock, order_index: int = 0) -> MagicMock: + """Create a mock ConfigInclude for testing.""" + include = MagicMock() + include.included_profile = included_profile + include.order_index = order_index + return include + + +def _make_mount( + target_path: str, + mode: str = "rw", + files: dict[str, str] | None = None, + order_index: int = 0, +) -> MagicMock: + """Create a mock ConfigMount for testing.""" + mount = MagicMock() + mount.target_path = target_path + mount.mode = mode + mount.files = files or {} + mount.order_index = order_index + return mount + + +class TestResolveProfileBasic: + """Tests for basic profile resolution without includes.""" + + def test_empty_profile(self) -> None: + """Resolving an empty profile returns empty output.""" + profile = _make_profile("empty") + result = resolve_profile(profile) + + assert isinstance(result, ResolvedProfileOutput) + assert result.profile_name == "empty" + assert result.environment_variables == {} + assert result.runtime_hints.start_command is None + assert result.runtime_hints.working_directory is None + assert result.runtime_hints.port is None + assert result.mounts == {} + assert result.resolution_order == ["empty"] + + def test_env_vars_only(self) -> None: + """Profile with env vars resolves correctly.""" + profile = _make_profile( + "env-only", + env_vars={"FOO": "bar", "BAZ": "qux"}, + ) + result = resolve_profile(profile) + + assert result.environment_variables == {"FOO": "bar", "BAZ": "qux"} + assert result.env_var_sources == { + "FOO": ["env-only"], + "BAZ": ["env-only"], + } + + def test_runtime_hints_only(self) -> None: + """Profile with runtime hints resolves correctly.""" + profile = _make_profile( + "hints-only", + start_command="python app.py", + working_directory="/app", + port=8080, + ) + result = resolve_profile(profile) + + assert result.runtime_hints.start_command == "python app.py" + assert result.runtime_hints.working_directory == "/app" + assert result.runtime_hints.port == 8080 + assert result.runtime_hints.overridden_hints == { + "start_command": "hints-only", + "working_directory": "hints-only", + "port": "hints-only", + } + + def test_mounts_only(self) -> None: + """Profile with mounts resolves correctly.""" + profile = _make_profile( + "mounts-only", + mounts=[ + _make_mount( + "/config", + mode="ro", + files={"settings.json": '{"key": "value"}'}, + ), + ], + ) + result = resolve_profile(profile) + + assert "/config" in result.mounts + mount = result.mounts["/config"] + assert mount.target_path == "/config" + assert mount.mode == "ro" + assert mount.files == {"settings.json": '{"key": "value"}'} + + +class TestResolveProfileIncludes: + """Tests for profile resolution with includes.""" + + def test_single_include(self) -> None: + """Profile with one include resolves in correct order.""" + base = _make_profile("base", env_vars={"FOO": "base"}) + derived = _make_profile( + "derived", + env_vars={"BAR": "derived"}, + includes=[_make_include(base, order_index=0)], + ) + result = resolve_profile(derived) + + assert result.resolution_order == ["derived", "base"] + assert result.environment_variables == { + "FOO": "base", + "BAR": "derived", + } + + def test_multiple_includes_ordered(self) -> None: + """Multiple includes are resolved in order_index order.""" + first = _make_profile("first", env_vars={"KEY": "first"}) + second = _make_profile("second", env_vars={"KEY": "second"}) + main = _make_profile( + "main", + includes=[ + _make_include(first, order_index=0), + _make_include(second, order_index=1), + ], + ) + result = resolve_profile(main) + + assert result.resolution_order == ["main", "first", "second"] + # second overrides first + assert result.environment_variables == {"KEY": "second"} + assert result.env_var_sources["KEY"] == ["first", "second"] + + def test_include_order_matters(self) -> None: + """Changing include order changes resolution.""" + a = _make_profile("a", env_vars={"KEY": "a"}) + b = _make_profile("b", env_vars={"KEY": "b"}) + main1 = _make_profile( + "main", + includes=[ + _make_include(a, order_index=0), + _make_include(b, order_index=1), + ], + ) + main2 = _make_profile( + "main", + includes=[ + _make_include(b, order_index=0), + _make_include(a, order_index=1), + ], + ) + + result1 = resolve_profile(main1) + result2 = resolve_profile(main2) + + assert result1.environment_variables["KEY"] == "b" + assert result2.environment_variables["KEY"] == "a" + + def test_nested_includes(self) -> None: + """Deeply nested includes resolve recursively.""" + deep = _make_profile("deep", env_vars={"DEEP": "value"}) + mid = _make_profile( + "mid", + env_vars={"MID": "value"}, + includes=[_make_include(deep, order_index=0)], + ) + top = _make_profile( + "top", + env_vars={"TOP": "value"}, + includes=[_make_include(mid, order_index=0)], + ) + result = resolve_profile(top) + + assert result.resolution_order == ["top", "mid", "deep"] + assert result.environment_variables == { + "TOP": "value", + "MID": "value", + "DEEP": "value", + } + + +class TestResolveProfileOverrides: + """Tests for deterministic override rules.""" + + def test_env_var_override(self) -> None: + """Later layers override earlier env vars.""" + base = _make_profile("base", env_vars={"KEY": "base"}) + override = _make_profile("override", env_vars={"KEY": "override"}) + main = _make_profile( + "main", + includes=[ + _make_include(base, order_index=0), + _make_include(override, order_index=1), + ], + ) + result = resolve_profile(main) + + assert result.environment_variables["KEY"] == "override" + assert result.env_var_sources["KEY"] == ["base", "override"] + + def test_main_profile_wins_over_includes(self) -> None: + """The main profile itself wins over all includes.""" + base = _make_profile("base", env_vars={"KEY": "base"}) + main = _make_profile( + "main", + env_vars={"KEY": "main"}, + includes=[_make_include(base, order_index=0)], + ) + result = resolve_profile(main) + + assert result.environment_variables["KEY"] == "main" + assert result.env_var_sources["KEY"] == ["base", "main"] + + def test_runtime_hint_override(self) -> None: + """Later layers override earlier runtime hints.""" + base = _make_profile("base", start_command="python old.py") + override = _make_profile("override", start_command="python new.py") + main = _make_profile( + "main", + includes=[ + _make_include(base, order_index=0), + _make_include(override, order_index=1), + ], + ) + result = resolve_profile(main) + + assert result.runtime_hints.start_command == "python new.py" + assert result.runtime_hints.overridden_hints["start_command"] == "override" + + def test_mount_file_override(self) -> None: + """Later layers override earlier files in the same mount.""" + base = _make_profile( + "base", + mounts=[ + _make_mount( + "/config", + files={"app.json": '{"v": 1}'}, + ), + ], + ) + override = _make_profile( + "override", + mounts=[ + _make_mount( + "/config", + files={"app.json": '{"v": 2}'}, + ), + ], + ) + main = _make_profile( + "main", + includes=[ + _make_include(base, order_index=0), + _make_include(override, order_index=1), + ], + ) + result = resolve_profile(main) + + mount = result.mounts["/config"] + assert mount.files["app.json"] == '{"v": 2}' + assert mount.overridden_files["app.json"] == ["override"] + + def test_mount_mode_override(self) -> None: + """Later layers override mount mode.""" + base = _make_profile( + "base", + mounts=[_make_mount("/data", mode="ro")], + ) + override = _make_profile( + "override", + mounts=[_make_mount("/data", mode="rw")], + ) + main = _make_profile( + "main", + includes=[ + _make_include(base, order_index=0), + _make_include(override, order_index=1), + ], + ) + result = resolve_profile(main) + + assert result.mounts["/data"].mode == "rw" + assert result.mounts["/data"].mode_overridden_by == "override" + + def test_mount_file_merge(self) -> None: + """Different files in the same mount are merged.""" + base = _make_profile( + "base", + mounts=[ + _make_mount( + "/config", + files={"a.json": "1"}, + ), + ], + ) + override = _make_profile( + "override", + mounts=[ + _make_mount( + "/config", + files={"b.json": "2"}, + ), + ], + ) + main = _make_profile( + "main", + includes=[ + _make_include(base, order_index=0), + _make_include(override, order_index=1), + ], + ) + result = resolve_profile(main) + + mount = result.mounts["/config"] + assert mount.files == {"a.json": "1", "b.json": "2"} + + +class TestResolveProfileCycles: + """Tests for cycle detection during resolution.""" + + def test_direct_cycle(self) -> None: + """A -> B -> A is detected.""" + a = _make_profile("a") + b = _make_profile("b", includes=[_make_include(a, order_index=0)]) + a.includes = [_make_include(b, order_index=0)] + + with pytest.raises(ProfileCycleError) as exc_info: + resolve_profile(a) + + assert "a" in exc_info.value.cycle_path + assert "b" in exc_info.value.cycle_path + + def test_indirect_cycle(self) -> None: + """A -> B -> C -> A is detected.""" + a = _make_profile("a") + c = _make_profile("c") + b = _make_profile("b", includes=[_make_include(c, order_index=0)]) + a.includes = [_make_include(b, order_index=0)] + c.includes = [_make_include(a, order_index=0)] + + with pytest.raises(ProfileCycleError) as exc_info: + resolve_profile(a) + + assert "a" in exc_info.value.cycle_path + assert "b" in exc_info.value.cycle_path + assert "c" in exc_info.value.cycle_path + + def test_self_cycle(self) -> None: + """A -> A is detected.""" + a = _make_profile("a") + a.includes = [_make_include(a, order_index=0)] + + with pytest.raises(ProfileCycleError) as exc_info: + resolve_profile(a) + + assert exc_info.value.cycle_path == ["a", "a"] + + def test_cycle_does_not_partially_resolve(self) -> None: + """Cycle detection prevents any partial resolution.""" + a = _make_profile("a", env_vars={"A": "a"}) + b = _make_profile("b", env_vars={"B": "b"}) + a.includes = [_make_include(b, order_index=0)] + b.includes = [_make_include(a, order_index=0)] + + with pytest.raises(ProfileCycleError): + resolve_profile(a) + + +class TestResolveProfileDiamond: + """Tests for diamond-shaped include graphs.""" + + def test_diamond_resolution(self) -> None: + """Diamond graph resolves correctly without duplication issues.""" + base = _make_profile("base", env_vars={"BASE": "base"}) + left = _make_profile( + "left", + env_vars={"LEFT": "left"}, + includes=[_make_include(base, order_index=0)], + ) + right = _make_profile( + "right", + env_vars={"RIGHT": "right"}, + includes=[_make_include(base, order_index=0)], + ) + top = _make_profile( + "top", + env_vars={"TOP": "top"}, + includes=[ + _make_include(left, order_index=0), + _make_include(right, order_index=1), + ], + ) + result = resolve_profile(top) + + # base should appear once (via left, then right skips because visited) + assert result.resolution_order == ["top", "left", "base", "right"] + assert result.environment_variables == { + "TOP": "top", + "LEFT": "left", + "RIGHT": "right", + "BASE": "base", + } + + def test_diamond_override(self) -> None: + """Diamond graph with conflicting overrides resolves correctly.""" + base = _make_profile("base", env_vars={"KEY": "base"}) + left = _make_profile( + "left", + env_vars={"KEY": "left"}, + includes=[_make_include(base, order_index=0)], + ) + right = _make_profile( + "right", + env_vars={"KEY": "right"}, + includes=[_make_include(base, order_index=0)], + ) + top = _make_profile( + "top", + includes=[ + _make_include(left, order_index=0), + _make_include(right, order_index=1), + ], + ) + result = resolve_profile(top) + + # right wins because it's later + assert result.environment_variables["KEY"] == "right" + assert result.env_var_sources["KEY"] == ["base", "left", "right"] + # Note: base appears once because visited set skips duplicate resolution in diamond graphs diff --git a/apps/api/tests/unit/test_terminal_manager.py b/apps/api/tests/unit/test_terminal_manager.py new file mode 100644 index 0000000..52c6838 --- /dev/null +++ b/apps/api/tests/unit/test_terminal_manager.py @@ -0,0 +1,112 @@ +"""Unit tests for TerminalManager.""" + +from unittest.mock import AsyncMock, MagicMock, patch + +import pytest + +from src.services.terminal_manager import TerminalManager +from src.services.terminal_session import TerminalSession + + +@pytest.fixture +def manager(): + return TerminalManager() + + +@pytest.fixture +def mock_websocket(): + ws = AsyncMock() + ws.send_bytes = AsyncMock() + ws.send_json = AsyncMock() + ws.close = AsyncMock() + ws.receive = AsyncMock() + return ws + + +@pytest.fixture +def mock_session(): + session = MagicMock(spec=TerminalSession) + session.session_id = "sess-123" + session.is_alive.return_value = True + session._closed = False + session.read_output = AsyncMock(return_value=b"") + session.write_input = AsyncMock() + session.resize = AsyncMock() + session.close = AsyncMock() + session.get_exit_reason.return_value = None + return session + + +class TestCreateSession: + @patch("src.services.terminal_manager.asyncio.create_task") + @patch("src.services.terminal_manager.uuid.uuid4", return_value="sess-123") + async def test_create_session_registers_and_starts_loops( + self, mock_uuid, mock_create_task, manager, mock_websocket + ): + instance_id = __import__("uuid").uuid4() + mock_sess = MagicMock() + mock_sess.session_id = "sess-123" + mock_sess.is_alive.return_value = True + mock_sess._closed = False + mock_sess.start = AsyncMock() + mock_sess.read_output = AsyncMock(return_value=b"") + mock_sess.write_input = AsyncMock() + mock_sess.resize = AsyncMock() + mock_sess.close = AsyncMock() + mock_sess.get_exit_reason.return_value = None + + with ( + patch.object(manager, "_read_loop", new=AsyncMock()), + patch.object(manager, "_write_loop", new=AsyncMock()), + patch.object(manager, "_heartbeat_loop", new=AsyncMock()), + patch( + "src.services.terminal_manager.TerminalSession", + return_value=mock_sess, + ), + ): + session = await manager.create_session( + instance_id, "container-abc", mock_websocket + ) + assert session.session_id == "sess-123" + assert "sess-123" in manager._sessions + assert "sess-123" in manager._last_client_message + + +class TestHandleControlMessage: + async def test_handle_resize(self, manager, mock_session, mock_websocket): + ctrl = {"type": "resize", "cols": 120, "rows": 40} + await manager._handle_control_message(mock_session, mock_websocket, ctrl) + mock_session.resize.assert_awaited_once_with(120, 40) + + async def test_handle_ping(self, manager, mock_session, mock_websocket): + ctrl = {"type": "ping", "id": 42} + await manager._handle_control_message(mock_session, mock_websocket, ctrl) + mock_websocket.send_json.assert_awaited_once_with({"type": "pong", "id": 42}) + + async def test_handle_unknown_type(self, manager, mock_session, mock_websocket): + ctrl = {"type": "unknown", "data": "test"} + await manager._handle_control_message(mock_session, mock_websocket, ctrl) + mock_websocket.send_json.assert_not_awaited() + mock_session.resize.assert_not_awaited() + + +class TestCleanupSession: + async def test_cleanup_removes_session(self, manager, mock_session): + manager._sessions["sess-123"] = mock_session + manager._last_client_message["sess-123"] = 123.0 + + await manager._cleanup_session(mock_session) + assert "sess-123" not in manager._sessions + assert "sess-123" not in manager._last_client_message + mock_session.close.assert_awaited_once() + + +class TestCloseAll: + async def test_close_all_clears_sessions(self, manager, mock_session): + manager._sessions["sess-123"] = mock_session + manager._last_client_message["sess-123"] = 123.0 + + await manager.close_all() + assert len(manager._sessions) == 0 + assert len(manager._last_client_message) == 0 + mock_session.close.assert_awaited_once() diff --git a/apps/api/tests/unit/test_terminal_session.py b/apps/api/tests/unit/test_terminal_session.py new file mode 100644 index 0000000..5dcbb75 --- /dev/null +++ b/apps/api/tests/unit/test_terminal_session.py @@ -0,0 +1,168 @@ +"""Unit tests for TerminalSession.""" + +import asyncio +from unittest.mock import MagicMock, patch + +import pytest + +from src.services.terminal_session import TerminalSession + + +@pytest.fixture +def mock_pty(): + """Mock pty.openpty to return predictable fds.""" + master_fd = 10 + slave_fd = 11 + with ( + patch( + "src.services.terminal_session.pty.openpty", + return_value=(master_fd, slave_fd), + ), + patch("src.services.terminal_session.os.close") as mock_close, + ): + yield master_fd, slave_fd, mock_close + + +class TestTerminalSessionStart: + def test_init_state(self, mock_pty): + session = TerminalSession("sess-1", __import__("uuid").uuid4(), "container-abc") + + assert session.session_id == "sess-1" + assert session.container_id == "container-abc" + assert session._echo_enabled is True + assert session._exit_reason is None + + +class TestTerminalSessionEchoDetection: + @patch("src.services.terminal_session.termios.tcgetattr") + def test_detect_echo_state_enabled(self, mock_tcgetattr): + session = TerminalSession("sess-1", __import__("uuid").uuid4(), "container-abc") + session._master_fd = 10 + + # termios.ECHO flag set + attrs = [[], [], [], __import__("termios").ECHO, [], [], []] + mock_tcgetattr.return_value = attrs + + result = session._detect_echo_state() + assert result is True + + @patch("src.services.terminal_session.termios.tcgetattr") + def test_detect_echo_state_disabled(self, mock_tcgetattr): + session = TerminalSession("sess-1", __import__("uuid").uuid4(), "container-abc") + session._master_fd = 10 + + # termios.ECHO flag NOT set + attrs = [[], [], [], 0, [], [], []] + mock_tcgetattr.return_value = attrs + + result = session._detect_echo_state() + assert result is False + + def test_detect_echo_state_no_master_fd(self): + session = TerminalSession("sess-1", __import__("uuid").uuid4(), "container-abc") + session._master_fd = None + + result = session._detect_echo_state() + assert result is True # default + + +class TestTerminalSessionResize: + @patch("src.services.terminal_session.fcntl.ioctl") + def test_resize_sets_size(self, mock_ioctl): + session = TerminalSession("sess-1", __import__("uuid").uuid4(), "container-abc") + session._master_fd = 10 + + # Should not raise + asyncio.run(session.resize(120, 40)) + mock_ioctl.assert_called_once() + + def test_resize_when_closed(self): + session = TerminalSession("sess-1", __import__("uuid").uuid4(), "container-abc") + session._closed = True + + # Should not raise + asyncio.run(session.resize(120, 40)) + + +class TestTerminalSessionWriteInput: + @patch("src.services.terminal_session.os.write") + def test_write_input(self, mock_write): + session = TerminalSession("sess-1", __import__("uuid").uuid4(), "container-abc") + session._master_fd = 10 + + asyncio.run(session.write_input(b"hello")) + mock_write.assert_called_once_with(10, b"hello") + + def test_write_input_when_closed(self): + session = TerminalSession("sess-1", __import__("uuid").uuid4(), "container-abc") + session._closed = True + + # Should not raise + asyncio.run(session.write_input(b"hello")) + + +class TestTerminalSessionReadOutput: + @patch("src.services.terminal_session.select.select") + @patch("src.services.terminal_session.os.read") + def test_read_output_with_data(self, mock_read, mock_select): + session = TerminalSession("sess-1", __import__("uuid").uuid4(), "container-abc") + session._master_fd = 10 + + mock_select.return_value = ([10], [], []) + mock_read.return_value = b"output" + + result = asyncio.run(session.read_output()) + assert result == b"output" + + @patch("src.services.terminal_session.select.select") + def test_read_output_no_data(self, mock_select): + session = TerminalSession("sess-1", __import__("uuid").uuid4(), "container-abc") + session._master_fd = 10 + + mock_select.return_value = ([], [], []) + + result = asyncio.run(session.read_output()) + assert result == b"" + + +class TestTerminalSessionClose: + @patch("src.services.terminal_session.os.close") + @patch("src.services.terminal_session.asyncio.wait_for") + async def test_close_sets_exit_reason(self, mock_wait_for, mock_close): + session = TerminalSession("sess-1", __import__("uuid").uuid4(), "container-abc") + session._master_fd = 10 + session.process = MagicMock() + session.process.returncode = 0 + + await session.close() + assert session._exit_reason == "process_exit" + assert session._closed is True + + async def test_close_idempotent(self): + session = TerminalSession("sess-1", __import__("uuid").uuid4(), "container-abc") + session._closed = True + + # Should not raise + await session.close() + + +class TestTerminalSessionIsAlive: + def test_is_alive_with_running_process(self): + session = TerminalSession("sess-1", __import__("uuid").uuid4(), "container-abc") + session.process = MagicMock() + session.process.returncode = None + + assert session.is_alive() is True + + def test_is_alive_with_exited_process(self): + session = TerminalSession("sess-1", __import__("uuid").uuid4(), "container-abc") + session.process = MagicMock() + session.process.returncode = 0 + + assert session.is_alive() is False + + def test_is_alive_no_process(self): + session = TerminalSession("sess-1", __import__("uuid").uuid4(), "container-abc") + session.process = None + + assert session.is_alive() is False diff --git a/apps/web/.pi-lens/cache/review-graph.json b/apps/web/.pi-lens/cache/review-graph.json new file mode 100644 index 0000000..9dc614c --- /dev/null +++ b/apps/web/.pi-lens/cache/review-graph.json @@ -0,0 +1 @@ +{"version":"v2","builtAt":"2026-06-03T09:11:46.559Z","signature":"/workspace/apps/web/.eslintrc.cjs:368:1780400866902.5977|/workspace/apps/web/scripts/check-structure.js:2645:1780477243155.7742|/workspace/apps/web/src/api/client.ts:1626:1780477234885.8682|/workspace/apps/web/src/api/config_profiles.ts:4632:1780476434295.989|/workspace/apps/web/src/api/config-folders.test.ts:3739:1780477243155.7742|/workspace/apps/web/src/api/config-folders.ts:1995:1780477243155.7742|/workspace/apps/web/src/api/dashboard.ts:350:1780440713833.9036|/workspace/apps/web/src/api/events.ts:598:1780476434295.989|/workspace/apps/web/src/api/git-repositories.ts:5138:1780478104541.959|/workspace/apps/web/src/api/notifications.ts:1813:1780476434295.989|/workspace/apps/web/src/api/profile.ts:880:1780440713832.9023|/workspace/apps/web/src/api/projects.ts:1300:1780477234885.8682|/workspace/apps/web/src/api/sessions.ts:3628:1780478408467.4954|/workspace/apps/web/src/api/settings.ts:965:1780477234885.8682|/workspace/apps/web/src/api/ssh-keys.ts:1368:1780477243155.7742|/workspace/apps/web/src/api/terminal.ts:1792:1780476434296.989|/workspace/apps/web/src/api/tool_definitions.ts:2552:1780476434296.989|/workspace/apps/web/src/api/tool-configs.ts:1454:1780477243155.7742|/workspace/apps/web/src/api/tool-types.test.ts:6355:1780477243155.7742|/workspace/apps/web/src/api/tool-types.ts:1354:1780477243156.774|/workspace/apps/web/src/api/workspace-files.ts:990:1780476434296.989|/workspace/apps/web/src/api/workspace-git.ts:1854:1780476434296.989|/workspace/apps/web/src/api/workspace-instances.ts:751:1780476434296.989|/workspace/apps/web/src/api/workspaces.ts:2293:1780476434296.989|/workspace/apps/web/src/components/create-session-form.tsx:19567:1780477606212.19|/workspace/apps/web/src/components/data-states.tsx:776:1780477606212.321|/workspace/apps/web/src/components/event-toast-bridge.test.tsx:6380:1780477606212.394|/workspace/apps/web/src/components/event-toast-bridge.tsx:2148:1780477606212.4731|/workspace/apps/web/src/components/features/dashboard/ActiveSessionsList.tsx:2215:1780477243157.0376|/workspace/apps/web/src/components/features/dashboard/DashboardSummary.tsx:893:1780477243157.102|/workspace/apps/web/src/components/features/dashboard/index.ts:285:1780477243157.102|/workspace/apps/web/src/components/features/dashboard/ProjectsSection.tsx:878:1780477243157.102|/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:3371:1780477243157.102|/workspace/apps/web/src/components/features/dashboard/RecentSessionsSection.tsx:1158:1780477243157.102|/workspace/apps/web/src/components/features/git/CommitDialog.tsx:4594:1780477243157.3982|/workspace/apps/web/src/components/features/git/CommitPanel.tsx:2495:1780477243157.3982|/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:1625:1780477243157.3982|/workspace/apps/web/src/components/features/git/FileBrowser.tsx:4014:1780477243157.3982|/workspace/apps/web/src/components/features/git/FileEditor.tsx:8375:1780477243157.3982|/workspace/apps/web/src/components/features/git/GitToolbar.tsx:7953:1780477473667.148|/workspace/apps/web/src/components/features/git/index.ts:100:1780477243157.774|/workspace/apps/web/src/components/features/git/MergeDialog.tsx:3961:1780477243157.774|/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:1938:1780477243157.774|/workspace/apps/web/src/components/features/git/WorkspaceSidebar.tsx:1562:1780477243157.774|/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:3993:1780477243157.774|/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:2692:1780477243158.303|/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:8612:1780477243158.303|/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:3338:1780477243158.303|/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:4390:1780477243158.5015|/workspace/apps/web/src/components/features/session/index.ts:147:1780477243158.5015|/workspace/apps/web/src/components/features/session/InstanceList.tsx:10555:1780477243158.5015|/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:1828:1780477243158.5015|/workspace/apps/web/src/components/features/session/SessionCard.tsx:4718:1780477243158.5015|/workspace/apps/web/src/components/features/session/SessionList.tsx:4688:1780477243158.5015|/workspace/apps/web/src/components/features/settings/SettingsTabLayout.tsx:982:1780477243158.9475|/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:9164:1780478503053.4172|/workspace/apps/web/src/components/features/tool-configs/index.ts:102:1780477243159.213|/workspace/apps/web/src/components/features/tool-configs/ToolConfigForm.tsx:3331:1780477243159.0618|/workspace/apps/web/src/components/features/tool-configs/ToolConfigList.tsx:2029:1780477243159.213|/workspace/apps/web/src/components/features/tool-types/index.ts:94:1780477243159.3945|/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:5803:1780477243159.213|/workspace/apps/web/src/components/features/tool-types/ToolTypeList.tsx:2291:1780477243159.3945|/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:7494:1780477243159.3945|/workspace/apps/web/src/components/features/tool-workshop/index.ts:153:1780477243159.6003|/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:12208:1780477243159.6003|/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:1679:1780477243159.6003|/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:15378:1780477243159.6003|/workspace/apps/web/src/components/features/workspace/WorkspaceHeader.tsx:1344:1780477243159.774|/workspace/apps/web/src/components/git-mount-editor.tsx:14988:1780477606212.5457|/workspace/apps/web/src/components/layout/AppShell.tsx:3901:1780477243160.009|/workspace/apps/web/src/components/manifest-editor.tsx:24496:1780477606212.681|/workspace/apps/web/src/components/mobile-action-sheet.tsx:2231:1780477606212.836|/workspace/apps/web/src/components/mobile-detail-view.tsx:2597:1780477606214.127|/workspace/apps/web/src/components/mobile-edit-view.tsx:5046:1780477606214.2078|/workspace/apps/web/src/components/mobile-fab.tsx:399:1780477606214.2798|/workspace/apps/web/src/components/mobile-list-view.tsx:2178:1780477606214.3455|/workspace/apps/web/src/components/mobile-nav.tsx:2634:1780477606214.41|/workspace/apps/web/src/components/mobile-page-header.tsx:899:1780477606214.4683|/workspace/apps/web/src/components/mobile-terminal-header.tsx:2672:1780477606214.5334|/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx:3664:1780477644037.8655|/workspace/apps/web/src/components/notification-center.test.tsx:5330:1780477606215.3801|/workspace/apps/web/src/components/notification-center.tsx:3019:1780477606215.4705|/workspace/apps/web/src/components/notification-item.test.tsx:2535:1780477606215.54|/workspace/apps/web/src/components/notification-item.tsx:1677:1780477606215.604|/workspace/apps/web/src/components/ProtectedRoute.test.tsx:1319:1780477606211.9646|/workspace/apps/web/src/components/ProtectedRoute.tsx:546:1780477606212.061|/workspace/apps/web/src/components/session-card.tsx:9578:1780477606215.6743|/workspace/apps/web/src/components/session-list.tsx:3783:1780477606215.7737|/workspace/apps/web/src/components/special-keys-panel.tsx:3034:1780477606215.8408|/workspace/apps/web/src/components/special-keys-strip.tsx:2641:1780477606215.9348|/workspace/apps/web/src/components/start-tool-fab.tsx:3106:1780477606216.8018|/workspace/apps/web/src/components/start-tool-modal.tsx:3199:1780477606216.92|/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:4615:1780477606217.0317|/workspace/apps/web/src/components/terminal-session-tabs.tsx:4515:1780477606217.1416|/workspace/apps/web/src/components/toast-rules.test.ts:2184:1780476434299.989|/workspace/apps/web/src/components/toast-rules.ts:2372:1780476434299.989|/workspace/apps/web/src/components/tool-starter.tsx:8532:1780477606217.2542|/workspace/apps/web/src/components/tools-bottom-sheet.tsx:1681:1780477606217.381|/workspace/apps/web/src/components/ui/CodeEditor.tsx:1364:1780477243160.009|/workspace/apps/web/src/components/ui/ConfirmDialog.tsx:858:1780477243160.136|/workspace/apps/web/src/components/ui/ErrorState.test.tsx:763:1780477243160.136|/workspace/apps/web/src/components/ui/ErrorState.tsx:358:1780477243160.136|/workspace/apps/web/src/components/ui/Icon.tsx:2927:1780477904079.2434|/workspace/apps/web/src/components/ui/index.ts:215:1780477243160.136|/workspace/apps/web/src/components/ui/LoadingState.test.tsx:496:1780477243160.136|/workspace/apps/web/src/components/ui/LoadingState.tsx:207:1780477243160.136|/workspace/apps/web/src/components/ui/StatusBadge.tsx:213:1780477243160.136|/workspace/apps/web/src/components/workspace-card.tsx:2012:1780477606217.46|/workspace/apps/web/src/components/workspace-create-form.tsx:8632:1780477606217.5525|/workspace/apps/web/src/components/workspace-instance-chips.tsx:1321:1780477606219.5337|/workspace/apps/web/src/hooks/use-async-data.ts:1029:1780477635012.6907|/workspace/apps/web/src/hooks/use-auto-hide.ts:1449:1780477635012.8135|/workspace/apps/web/src/hooks/use-dashboard-actions.ts:2569:1780477635012.9175|/workspace/apps/web/src/hooks/use-events.test.ts:4797:1780477635013.007|/workspace/apps/web/src/hooks/use-events.ts:2824:1780477635013.0852|/workspace/apps/web/src/hooks/use-git-repo.ts:7948:1780477635013.2231|/workspace/apps/web/src/hooks/use-instance-actions.ts:4593:1780477635013.356|/workspace/apps/web/src/hooks/use-mobile-viewport.ts:550:1780477635013.4517|/workspace/apps/web/src/hooks/use-notifications.test.tsx:8775:1780476434300.989|/workspace/apps/web/src/hooks/use-notifications.ts:301:1780477635013.513|/workspace/apps/web/src/hooks/use-special-keys.ts:4159:1780477635013.5833|/workspace/apps/web/src/hooks/use-start-tool.ts:1532:1780477635014.8633|/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:8112:1780477635014.9702|/workspace/apps/web/src/hooks/use-terminal-connection.ts:11162:1780477635015.109|/workspace/apps/web/src/hooks/use-terminal-sessions.ts:3744:1780477635015.2395|/workspace/apps/web/src/hooks/use-theme.ts:633:1780477635015.3298|/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:2021:1780477635015.3928|/workspace/apps/web/src/hooks/use-workspace-actions.ts:3320:1780477635015.464|/workspace/apps/web/src/hooks/use-workspace-files.ts:1802:1780477635015.5269|/workspace/apps/web/src/hooks/use-workspace-git.ts:2511:1780477635016.4097|/workspace/apps/web/src/hooks/use-workspace-instances.ts:1551:1780477635016.483|/workspace/apps/web/src/hooks/use-workspaces.ts:1135:1780477635016.5466|/workspace/apps/web/src/main.tsx:912:1780477243160.136|/workspace/apps/web/src/pages/config-profiles.tsx:44048:1780477635021.6716|/workspace/apps/web/src/pages/DashboardPage.test.tsx:2359:1780477635017.543|/workspace/apps/web/src/pages/DashboardPage.tsx:5333:1780477635017.6506|/workspace/apps/web/src/pages/GitHistoryPage.tsx:8465:1780477635017.7395|/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:4079:1780477635017.8372|/workspace/apps/web/src/pages/PlaceholderPage.tsx:1121:1780477635017.9238|/workspace/apps/web/src/pages/ProfilePage.tsx:5616:1780477635018.011|/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:4414:1780477635018.0933|/workspace/apps/web/src/pages/ProjectsPage.test.tsx:6158:1780477635018.1704|/workspace/apps/web/src/pages/ProjectsPage.tsx:6433:1780477635018.2515|/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:6068:1780477635019.3|/workspace/apps/web/src/pages/SessionsPage.tsx:5555:1780477635019.4263|/workspace/apps/web/src/pages/SettingsPage.tsx:5767:1780477635019.5686|/workspace/apps/web/src/pages/SshKeysPage.tsx:4052:1780477635019.6501|/workspace/apps/web/src/pages/TerminalPage.tsx:1013:1780477635019.7124|/workspace/apps/web/src/pages/ToolConfigsPage.tsx:4945:1780477635019.786|/workspace/apps/web/src/pages/ToolTypesPage.tsx:3319:1780477635019.866|/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:19959:1780477635019.971|/workspace/apps/web/src/pages/ToolWorkshopPage.tsx:1770:1780477635021.5085|/workspace/apps/web/src/pages/workspace-detail.tsx:11417:1780477635021.9846|/workspace/apps/web/src/pages/workspaces.tsx:2929:1780477635022.125|/workspace/apps/web/src/router.tsx:3006:1780477473669.148|/workspace/apps/web/src/state/auth.tsx:1666:1780440713837.2695|/workspace/apps/web/src/state/events.tsx:819:1780476434302.989|/workspace/apps/web/src/state/notifications.tsx:7752:1780476434302.989|/workspace/apps/web/src/state/sessions.tsx:964:1780477243161.774|/workspace/apps/web/src/state/toast.tsx:4463:1780476434303.989|/workspace/apps/web/src/test/setup.ts:43:1780440713827.5044|/workspace/apps/web/src/types.ts:254:1780478331651.3708|/workspace/apps/web/src/types/api-response.ts:153:1780477243162.2803|/workspace/apps/web/src/types/config-folder.ts:758:1780477243162.2803|/workspace/apps/web/src/types/events.ts:382:1780476434303.989|/workspace/apps/web/src/types/git-repository.ts:1592:1780478168094.2349|/workspace/apps/web/src/types/index.ts:807:1780478218148.6643|/workspace/apps/web/src/types/project.ts:504:1780478204977.8145|/workspace/apps/web/src/types/session.ts:344:1780478151324.4258|/workspace/apps/web/src/types/terminal.ts:1502:1780477243162.774|/workspace/apps/web/src/types/tool-config.ts:764:1780477243162.774|/workspace/apps/web/src/types/tool-instance.ts:247:1780477243162.774|/workspace/apps/web/src/types/tool-type.ts:1357:1780477243162.774|/workspace/apps/web/src/types/user.ts:158:1780477243162.774|/workspace/apps/web/src/types/workspace.ts:586:1780476434305.4553|/workspace/apps/web/src/utils/errors.ts:428:1780477635023.1575|/workspace/apps/web/src/utils/icons.ts:54:1780477635023.3152|/workspace/apps/web/src/utils/language.ts:1815:1780477635023.3909|/workspace/apps/web/src/utils/terminal-protocol.ts:1931:1780477635023.4639|/workspace/apps/web/src/utils/time.ts:720:1780477635023.528|/workspace/apps/web/vite.config.ts:337:1780477243162.774","fileSignatures":[["/workspace/apps/web/.eslintrc.cjs","368:1780400866902.5977"],["/workspace/apps/web/scripts/check-structure.js","2645:1780477243155.7742"],["/workspace/apps/web/src/api/client.ts","1626:1780477234885.8682"],["/workspace/apps/web/src/api/config-folders.test.ts","3739:1780477243155.7742"],["/workspace/apps/web/src/api/config-folders.ts","1995:1780477243155.7742"],["/workspace/apps/web/src/api/config_profiles.ts","4632:1780476434295.989"],["/workspace/apps/web/src/api/dashboard.ts","350:1780440713833.9036"],["/workspace/apps/web/src/api/events.ts","598:1780476434295.989"],["/workspace/apps/web/src/api/git-repositories.ts","5138:1780478104541.959"],["/workspace/apps/web/src/api/notifications.ts","1813:1780476434295.989"],["/workspace/apps/web/src/api/profile.ts","880:1780440713832.9023"],["/workspace/apps/web/src/api/projects.ts","1300:1780477234885.8682"],["/workspace/apps/web/src/api/sessions.ts","3628:1780478408467.4954"],["/workspace/apps/web/src/api/settings.ts","965:1780477234885.8682"],["/workspace/apps/web/src/api/ssh-keys.ts","1368:1780477243155.7742"],["/workspace/apps/web/src/api/terminal.ts","1792:1780476434296.989"],["/workspace/apps/web/src/api/tool-configs.ts","1454:1780477243155.7742"],["/workspace/apps/web/src/api/tool-types.test.ts","6355:1780477243155.7742"],["/workspace/apps/web/src/api/tool-types.ts","1354:1780477243156.774"],["/workspace/apps/web/src/api/tool_definitions.ts","2552:1780476434296.989"],["/workspace/apps/web/src/api/workspace-files.ts","990:1780476434296.989"],["/workspace/apps/web/src/api/workspace-git.ts","1854:1780476434296.989"],["/workspace/apps/web/src/api/workspace-instances.ts","751:1780476434296.989"],["/workspace/apps/web/src/api/workspaces.ts","2293:1780476434296.989"],["/workspace/apps/web/src/components/ProtectedRoute.test.tsx","1319:1780477606211.9646"],["/workspace/apps/web/src/components/ProtectedRoute.tsx","546:1780477606212.061"],["/workspace/apps/web/src/components/create-session-form.tsx","19567:1780477606212.19"],["/workspace/apps/web/src/components/data-states.tsx","776:1780477606212.321"],["/workspace/apps/web/src/components/event-toast-bridge.test.tsx","6380:1780477606212.394"],["/workspace/apps/web/src/components/event-toast-bridge.tsx","2148:1780477606212.4731"],["/workspace/apps/web/src/components/features/dashboard/ActiveSessionsList.tsx","2215:1780477243157.0376"],["/workspace/apps/web/src/components/features/dashboard/DashboardSummary.tsx","893:1780477243157.102"],["/workspace/apps/web/src/components/features/dashboard/ProjectsSection.tsx","878:1780477243157.102"],["/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx","3371:1780477243157.102"],["/workspace/apps/web/src/components/features/dashboard/RecentSessionsSection.tsx","1158:1780477243157.102"],["/workspace/apps/web/src/components/features/dashboard/index.ts","285:1780477243157.102"],["/workspace/apps/web/src/components/features/git/CommitDialog.tsx","4594:1780477243157.3982"],["/workspace/apps/web/src/components/features/git/CommitPanel.tsx","2495:1780477243157.3982"],["/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx","1625:1780477243157.3982"],["/workspace/apps/web/src/components/features/git/FileBrowser.tsx","4014:1780477243157.3982"],["/workspace/apps/web/src/components/features/git/FileEditor.tsx","8375:1780477243157.3982"],["/workspace/apps/web/src/components/features/git/GitToolbar.tsx","7953:1780477473667.148"],["/workspace/apps/web/src/components/features/git/MergeDialog.tsx","3961:1780477243157.774"],["/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx","1938:1780477243157.774"],["/workspace/apps/web/src/components/features/git/WorkspaceSidebar.tsx","1562:1780477243157.774"],["/workspace/apps/web/src/components/features/git/index.ts","100:1780477243157.774"],["/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx","3993:1780477243157.774"],["/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx","2692:1780477243158.303"],["/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","8612:1780477243158.303"],["/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx","3338:1780477243158.303"],["/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx","4390:1780477243158.5015"],["/workspace/apps/web/src/components/features/session/InstanceList.tsx","10555:1780477243158.5015"],["/workspace/apps/web/src/components/features/session/SessionCard.test.tsx","1828:1780477243158.5015"],["/workspace/apps/web/src/components/features/session/SessionCard.tsx","4718:1780477243158.5015"],["/workspace/apps/web/src/components/features/session/SessionList.tsx","4688:1780477243158.5015"],["/workspace/apps/web/src/components/features/session/index.ts","147:1780477243158.5015"],["/workspace/apps/web/src/components/features/settings/SettingsTabLayout.tsx","982:1780477243158.9475"],["/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","9164:1780478503053.4172"],["/workspace/apps/web/src/components/features/tool-configs/ToolConfigForm.tsx","3331:1780477243159.0618"],["/workspace/apps/web/src/components/features/tool-configs/ToolConfigList.tsx","2029:1780477243159.213"],["/workspace/apps/web/src/components/features/tool-configs/index.ts","102:1780477243159.213"],["/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx","5803:1780477243159.213"],["/workspace/apps/web/src/components/features/tool-types/ToolTypeList.tsx","2291:1780477243159.3945"],["/workspace/apps/web/src/components/features/tool-types/index.ts","94:1780477243159.3945"],["/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx","7494:1780477243159.3945"],["/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx","12208:1780477243159.6003"],["/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx","1679:1780477243159.6003"],["/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","15378:1780477243159.6003"],["/workspace/apps/web/src/components/features/tool-workshop/index.ts","153:1780477243159.6003"],["/workspace/apps/web/src/components/features/workspace/WorkspaceHeader.tsx","1344:1780477243159.774"],["/workspace/apps/web/src/components/git-mount-editor.tsx","14988:1780477606212.5457"],["/workspace/apps/web/src/components/layout/AppShell.tsx","3901:1780477243160.009"],["/workspace/apps/web/src/components/manifest-editor.tsx","24496:1780477606212.681"],["/workspace/apps/web/src/components/mobile-action-sheet.tsx","2231:1780477606212.836"],["/workspace/apps/web/src/components/mobile-detail-view.tsx","2597:1780477606214.127"],["/workspace/apps/web/src/components/mobile-edit-view.tsx","5046:1780477606214.2078"],["/workspace/apps/web/src/components/mobile-fab.tsx","399:1780477606214.2798"],["/workspace/apps/web/src/components/mobile-list-view.tsx","2178:1780477606214.3455"],["/workspace/apps/web/src/components/mobile-nav.tsx","2634:1780477606214.41"],["/workspace/apps/web/src/components/mobile-page-header.tsx","899:1780477606214.4683"],["/workspace/apps/web/src/components/mobile-terminal-header.tsx","2672:1780477606214.5334"],["/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx","3664:1780477644037.8655"],["/workspace/apps/web/src/components/notification-center.test.tsx","5330:1780477606215.3801"],["/workspace/apps/web/src/components/notification-center.tsx","3019:1780477606215.4705"],["/workspace/apps/web/src/components/notification-item.test.tsx","2535:1780477606215.54"],["/workspace/apps/web/src/components/notification-item.tsx","1677:1780477606215.604"],["/workspace/apps/web/src/components/session-card.tsx","9578:1780477606215.6743"],["/workspace/apps/web/src/components/session-list.tsx","3783:1780477606215.7737"],["/workspace/apps/web/src/components/special-keys-panel.tsx","3034:1780477606215.8408"],["/workspace/apps/web/src/components/special-keys-strip.tsx","2641:1780477606215.9348"],["/workspace/apps/web/src/components/start-tool-fab.tsx","3106:1780477606216.8018"],["/workspace/apps/web/src/components/start-tool-modal.tsx","3199:1780477606216.92"],["/workspace/apps/web/src/components/terminal-session-tabs.test.tsx","4615:1780477606217.0317"],["/workspace/apps/web/src/components/terminal-session-tabs.tsx","4515:1780477606217.1416"],["/workspace/apps/web/src/components/toast-rules.test.ts","2184:1780476434299.989"],["/workspace/apps/web/src/components/toast-rules.ts","2372:1780476434299.989"],["/workspace/apps/web/src/components/tool-starter.tsx","8532:1780477606217.2542"],["/workspace/apps/web/src/components/tools-bottom-sheet.tsx","1681:1780477606217.381"],["/workspace/apps/web/src/components/ui/CodeEditor.tsx","1364:1780477243160.009"],["/workspace/apps/web/src/components/ui/ConfirmDialog.tsx","858:1780477243160.136"],["/workspace/apps/web/src/components/ui/ErrorState.test.tsx","763:1780477243160.136"],["/workspace/apps/web/src/components/ui/ErrorState.tsx","358:1780477243160.136"],["/workspace/apps/web/src/components/ui/Icon.tsx","2927:1780477904079.2434"],["/workspace/apps/web/src/components/ui/LoadingState.test.tsx","496:1780477243160.136"],["/workspace/apps/web/src/components/ui/LoadingState.tsx","207:1780477243160.136"],["/workspace/apps/web/src/components/ui/StatusBadge.tsx","213:1780477243160.136"],["/workspace/apps/web/src/components/ui/index.ts","215:1780477243160.136"],["/workspace/apps/web/src/components/workspace-card.tsx","2012:1780477606217.46"],["/workspace/apps/web/src/components/workspace-create-form.tsx","8632:1780477606217.5525"],["/workspace/apps/web/src/components/workspace-instance-chips.tsx","1321:1780477606219.5337"],["/workspace/apps/web/src/hooks/use-async-data.ts","1029:1780477635012.6907"],["/workspace/apps/web/src/hooks/use-auto-hide.ts","1449:1780477635012.8135"],["/workspace/apps/web/src/hooks/use-dashboard-actions.ts","2569:1780477635012.9175"],["/workspace/apps/web/src/hooks/use-events.test.ts","4797:1780477635013.007"],["/workspace/apps/web/src/hooks/use-events.ts","2824:1780477635013.0852"],["/workspace/apps/web/src/hooks/use-git-repo.ts","7948:1780477635013.2231"],["/workspace/apps/web/src/hooks/use-instance-actions.ts","4593:1780477635013.356"],["/workspace/apps/web/src/hooks/use-mobile-viewport.ts","550:1780477635013.4517"],["/workspace/apps/web/src/hooks/use-notifications.test.tsx","8775:1780476434300.989"],["/workspace/apps/web/src/hooks/use-notifications.ts","301:1780477635013.513"],["/workspace/apps/web/src/hooks/use-special-keys.ts","4159:1780477635013.5833"],["/workspace/apps/web/src/hooks/use-start-tool.ts","1532:1780477635014.8633"],["/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","8112:1780477635014.9702"],["/workspace/apps/web/src/hooks/use-terminal-connection.ts","11162:1780477635015.109"],["/workspace/apps/web/src/hooks/use-terminal-sessions.ts","3744:1780477635015.2395"],["/workspace/apps/web/src/hooks/use-theme.ts","633:1780477635015.3298"],["/workspace/apps/web/src/hooks/use-virtual-keyboard.ts","2021:1780477635015.3928"],["/workspace/apps/web/src/hooks/use-workspace-actions.ts","3320:1780477635015.464"],["/workspace/apps/web/src/hooks/use-workspace-files.ts","1802:1780477635015.5269"],["/workspace/apps/web/src/hooks/use-workspace-git.ts","2511:1780477635016.4097"],["/workspace/apps/web/src/hooks/use-workspace-instances.ts","1551:1780477635016.483"],["/workspace/apps/web/src/hooks/use-workspaces.ts","1135:1780477635016.5466"],["/workspace/apps/web/src/main.tsx","912:1780477243160.136"],["/workspace/apps/web/src/pages/DashboardPage.test.tsx","2359:1780477635017.543"],["/workspace/apps/web/src/pages/DashboardPage.tsx","5333:1780477635017.6506"],["/workspace/apps/web/src/pages/GitHistoryPage.tsx","8465:1780477635017.7395"],["/workspace/apps/web/src/pages/GitRepositoriesPage.tsx","4079:1780477635017.8372"],["/workspace/apps/web/src/pages/PlaceholderPage.tsx","1121:1780477635017.9238"],["/workspace/apps/web/src/pages/ProfilePage.tsx","5616:1780477635018.011"],["/workspace/apps/web/src/pages/ProjectSettingsPage.tsx","4414:1780477635018.0933"],["/workspace/apps/web/src/pages/ProjectsPage.test.tsx","6158:1780477635018.1704"],["/workspace/apps/web/src/pages/ProjectsPage.tsx","6433:1780477635018.2515"],["/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","6068:1780477635019.3"],["/workspace/apps/web/src/pages/SessionsPage.tsx","5555:1780477635019.4263"],["/workspace/apps/web/src/pages/SettingsPage.tsx","5767:1780477635019.5686"],["/workspace/apps/web/src/pages/SshKeysPage.tsx","4052:1780477635019.6501"],["/workspace/apps/web/src/pages/TerminalPage.tsx","1013:1780477635019.7124"],["/workspace/apps/web/src/pages/ToolConfigsPage.tsx","4945:1780477635019.786"],["/workspace/apps/web/src/pages/ToolTypesPage.tsx","3319:1780477635019.866"],["/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","19959:1780477635019.971"],["/workspace/apps/web/src/pages/ToolWorkshopPage.tsx","1770:1780477635021.5085"],["/workspace/apps/web/src/pages/config-profiles.tsx","44048:1780477635021.6716"],["/workspace/apps/web/src/pages/workspace-detail.tsx","11417:1780477635021.9846"],["/workspace/apps/web/src/pages/workspaces.tsx","2929:1780477635022.125"],["/workspace/apps/web/src/router.tsx","3006:1780477473669.148"],["/workspace/apps/web/src/state/auth.tsx","1666:1780440713837.2695"],["/workspace/apps/web/src/state/events.tsx","819:1780476434302.989"],["/workspace/apps/web/src/state/notifications.tsx","7752:1780476434302.989"],["/workspace/apps/web/src/state/sessions.tsx","964:1780477243161.774"],["/workspace/apps/web/src/state/toast.tsx","4463:1780476434303.989"],["/workspace/apps/web/src/test/setup.ts","43:1780440713827.5044"],["/workspace/apps/web/src/types/api-response.ts","153:1780477243162.2803"],["/workspace/apps/web/src/types/config-folder.ts","758:1780477243162.2803"],["/workspace/apps/web/src/types/events.ts","382:1780476434303.989"],["/workspace/apps/web/src/types/git-repository.ts","1592:1780478168094.2349"],["/workspace/apps/web/src/types/index.ts","807:1780478218148.6643"],["/workspace/apps/web/src/types/project.ts","504:1780478204977.8145"],["/workspace/apps/web/src/types/session.ts","344:1780478151324.4258"],["/workspace/apps/web/src/types/terminal.ts","1502:1780477243162.774"],["/workspace/apps/web/src/types/tool-config.ts","764:1780477243162.774"],["/workspace/apps/web/src/types/tool-instance.ts","247:1780477243162.774"],["/workspace/apps/web/src/types/tool-type.ts","1357:1780477243162.774"],["/workspace/apps/web/src/types/user.ts","158:1780477243162.774"],["/workspace/apps/web/src/types/workspace.ts","586:1780476434305.4553"],["/workspace/apps/web/src/types.ts","254:1780478331651.3708"],["/workspace/apps/web/src/utils/errors.ts","428:1780477635023.1575"],["/workspace/apps/web/src/utils/icons.ts","54:1780477635023.3152"],["/workspace/apps/web/src/utils/language.ts","1815:1780477635023.3909"],["/workspace/apps/web/src/utils/terminal-protocol.ts","1931:1780477635023.4639"],["/workspace/apps/web/src/utils/time.ts","720:1780477635023.528"],["/workspace/apps/web/vite.config.ts","337:1780477243162.774"]],"nodes":[["file:/workspace/apps/web/.eslintrc.cjs",{"id":"file:/workspace/apps/web/.eslintrc.cjs","kind":"file","language":"jsts","filePath":"/workspace/apps/web/.eslintrc.cjs","metadata":{"lineCount":17,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/scripts/check-structure.js",{"id":"file:/workspace/apps/web/scripts/check-structure.js","kind":"file","language":"jsts","filePath":"/workspace/apps/web/scripts/check-structure.js","metadata":{"lineCount":84,"featureKind":"library","trustBoundaries":[]}}],["external:fs",{"id":"external:fs","kind":"external","language":"jsts","metadata":{"source":"fs"}}],["external:path",{"id":"external:path","kind":"external","language":"jsts","metadata":{"source":"path"}}],["external:url",{"id":"external:url","kind":"external","language":"jsts","metadata":{"source":"url"}}],["/workspace/apps/web/scripts/check-structure.js:checkFileSize",{"id":"/workspace/apps/web/scripts/check-structure.js:checkFileSize","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/scripts/check-structure.js","symbolName":"checkFileSize","symbolKind":"function","exported":false,"metadata":{"line":38,"column":1,"cyclomaticComplexity":3,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:fs.readFileSync",{"id":"external:fs.readFileSync","kind":"external","language":"jsts","metadata":{"unresolvedName":"fs.readFileSync"}}],["external:content.split",{"id":"external:content.split","kind":"external","language":"jsts","metadata":{"unresolvedName":"content.split"}}],["external:path.relative",{"id":"external:path.relative","kind":"external","language":"jsts","metadata":{"unresolvedName":"path.relative"}}],["external:OVERSIZE_ALLOWLIST.includes",{"id":"external:OVERSIZE_ALLOWLIST.includes","kind":"external","language":"jsts","metadata":{"unresolvedName":"OVERSIZE_ALLOWLIST.includes"}}],["external:console.warn",{"id":"external:console.warn","kind":"external","language":"jsts","metadata":{"unresolvedName":"console.warn"}}],["external:console.error",{"id":"external:console.error","kind":"external","language":"jsts","metadata":{"unresolvedName":"console.error"}}],["/workspace/apps/web/scripts/check-structure.js:walk",{"id":"/workspace/apps/web/scripts/check-structure.js:walk","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/scripts/check-structure.js","symbolName":"walk","symbolKind":"function","exported":false,"metadata":{"line":53,"column":1,"cyclomaticComplexity":5,"maxNestingDepth":3,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:fs.readdirSync",{"id":"external:fs.readdirSync","kind":"external","language":"jsts","metadata":{"unresolvedName":"fs.readdirSync"}}],["external:path.join",{"id":"external:path.join","kind":"external","language":"jsts","metadata":{"unresolvedName":"path.join"}}],["external:entry.isDirectory",{"id":"external:entry.isDirectory","kind":"external","language":"jsts","metadata":{"unresolvedName":"entry.isDirectory"}}],["external:entry.name.startsWith",{"id":"external:entry.name.startsWith","kind":"external","language":"jsts","metadata":{"unresolvedName":"entry.name.startsWith"}}],["symbol-name:walk",{"id":"symbol-name:walk","kind":"symbol","language":"jsts","symbolName":"walk","metadata":{"unresolvedName":"walk"}}],["symbol-name:callback",{"id":"symbol-name:callback","kind":"symbol","language":"jsts","symbolName":"callback","metadata":{"unresolvedName":"callback"}}],["/workspace/apps/web/scripts/check-structure.js:",{"id":"/workspace/apps/web/scripts/check-structure.js:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/scripts/check-structure.js","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":66,"column":15,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:path.extname",{"id":"external:path.extname","kind":"external","language":"jsts","metadata":{"unresolvedName":"path.extname"}}],["external:[\".ts\", \".tsx\", \".py\", \".css\"].includes",{"id":"external:[\".ts\", \".tsx\", \".py\", \".css\"].includes","kind":"external","language":"jsts","metadata":{"unresolvedName":"[\".ts\", \".tsx\", \".py\", \".css\"].includes"}}],["symbol-name:checkFileSize",{"id":"symbol-name:checkFileSize","kind":"symbol","language":"jsts","symbolName":"checkFileSize","metadata":{"unresolvedName":"checkFileSize"}}],["file:/workspace/apps/web/src/api/client.ts",{"id":"file:/workspace/apps/web/src/api/client.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/api/client.ts","metadata":{"lineCount":52,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["external:axios",{"id":"external:axios","kind":"external","language":"jsts","metadata":{"source":"axios"}}],["/workspace/apps/web/src/api/client.ts:shouldSkipAuthRedirect",{"id":"/workspace/apps/web/src/api/client.ts:shouldSkipAuthRedirect","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/client.ts","symbolName":"shouldSkipAuthRedirect","symbolKind":"function","exported":true,"metadata":{"line":13,"column":39,"cyclomaticComplexity":2,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization","auth","user-input"]}}],["external:path.startsWith",{"id":"external:path.startsWith","kind":"external","language":"jsts","metadata":{"unresolvedName":"path.startsWith"}}],["/workspace/apps/web/src/api/client.ts:",{"id":"/workspace/apps/web/src/api/client.ts:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/client.ts","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":26,"column":3,"cyclomaticComplexity":8,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["symbol-name:shouldSkipAuthRedirect",{"id":"symbol-name:shouldSkipAuthRedirect","kind":"symbol","language":"jsts","symbolName":"shouldSkipAuthRedirect","metadata":{"unresolvedName":"shouldSkipAuthRedirect"}}],["external:window.location.assign",{"id":"external:window.location.assign","kind":"external","language":"jsts","metadata":{"unresolvedName":"window.location.assign"}}],["external:Promise.reject",{"id":"external:Promise.reject","kind":"external","language":"jsts","metadata":{"unresolvedName":"Promise.reject"}}],["external:error.message?.includes",{"id":"external:error.message?.includes","kind":"external","language":"jsts","metadata":{"unresolvedName":"error.message?.includes"}}],["external:retryCount.get",{"id":"external:retryCount.get","kind":"external","language":"jsts","metadata":{"unresolvedName":"retryCount.get"}}],["external:retryCount.set",{"id":"external:retryCount.set","kind":"external","language":"jsts","metadata":{"unresolvedName":"retryCount.set"}}],["symbol-name:setTimeout",{"id":"symbol-name:setTimeout","kind":"symbol","language":"jsts","symbolName":"setTimeout","metadata":{"unresolvedName":"setTimeout"}}],["symbol-name:apiClient",{"id":"symbol-name:apiClient","kind":"symbol","language":"jsts","symbolName":"apiClient","metadata":{"unresolvedName":"apiClient"}}],["file:/workspace/apps/web/src/api/config-folders.test.ts",{"id":"file:/workspace/apps/web/src/api/config-folders.test.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/api/config-folders.test.ts","metadata":{"lineCount":132,"featureKind":"service","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["external:vitest",{"id":"external:vitest","kind":"external","language":"jsts","metadata":{"source":"vitest"}}],["file:/workspace/apps/web/src/api/config-folders.ts",{"id":"file:/workspace/apps/web/src/api/config-folders.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/api/config-folders.ts","metadata":{"lineCount":78,"featureKind":"service","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/config-folders.test.ts:",{"id":"/workspace/apps/web/src/api/config-folders.test.ts:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/config-folders.test.ts","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":123,"column":26,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["symbol-name:describe",{"id":"symbol-name:describe","kind":"symbol","language":"jsts","symbolName":"describe","metadata":{"unresolvedName":"describe"}}],["symbol-name:it",{"id":"symbol-name:it","kind":"symbol","language":"jsts","symbolName":"it","metadata":{"unresolvedName":"it"}}],["external:mockGet.mockResolvedValue",{"id":"external:mockGet.mockResolvedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockGet.mockResolvedValue"}}],["symbol-name:listConfigFolders",{"id":"symbol-name:listConfigFolders","kind":"symbol","language":"jsts","symbolName":"listConfigFolders","metadata":{"unresolvedName":"listConfigFolders"}}],["external:expect(result[0].name).toBe",{"id":"external:expect(result[0].name).toBe","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(result[0].name).toBe"}}],["symbol-name:expect",{"id":"symbol-name:expect","kind":"symbol","language":"jsts","symbolName":"expect","metadata":{"unresolvedName":"expect"}}],["external:expect(result[0].files).toEqual",{"id":"external:expect(result[0].files).toEqual","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(result[0].files).toEqual"}}],["external:expect(mockGet).toHaveBeenCalledWith",{"id":"external:expect(mockGet).toHaveBeenCalledWith","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(mockGet).toHaveBeenCalledWith"}}],["external:mockPost.mockResolvedValue",{"id":"external:mockPost.mockResolvedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockPost.mockResolvedValue"}}],["symbol-name:createConfigFolder",{"id":"symbol-name:createConfigFolder","kind":"symbol","language":"jsts","symbolName":"createConfigFolder","metadata":{"unresolvedName":"createConfigFolder"}}],["external:expect(result.name).toBe",{"id":"external:expect(result.name).toBe","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(result.name).toBe"}}],["external:expect(mockPost).toHaveBeenCalledWith",{"id":"external:expect(mockPost).toHaveBeenCalledWith","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(mockPost).toHaveBeenCalledWith"}}],["external:expect.objectContaining",{"id":"external:expect.objectContaining","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect.objectContaining"}}],["external:mockPut.mockResolvedValue",{"id":"external:mockPut.mockResolvedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockPut.mockResolvedValue"}}],["symbol-name:updateConfigFolder",{"id":"symbol-name:updateConfigFolder","kind":"symbol","language":"jsts","symbolName":"updateConfigFolder","metadata":{"unresolvedName":"updateConfigFolder"}}],["external:expect(result.files).toEqual",{"id":"external:expect(result.files).toEqual","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(result.files).toEqual"}}],["external:expect(mockPut).toHaveBeenCalledWith",{"id":"external:expect(mockPut).toHaveBeenCalledWith","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(mockPut).toHaveBeenCalledWith"}}],["external:mockDelete.mockResolvedValue",{"id":"external:mockDelete.mockResolvedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockDelete.mockResolvedValue"}}],["symbol-name:deleteConfigFolder",{"id":"symbol-name:deleteConfigFolder","kind":"symbol","language":"jsts","symbolName":"deleteConfigFolder","metadata":{"unresolvedName":"deleteConfigFolder"}}],["external:expect(mockDelete).toHaveBeenCalledWith",{"id":"external:expect(mockDelete).toHaveBeenCalledWith","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(mockDelete).toHaveBeenCalledWith"}}],["file:/workspace/apps/web/src/types/config-folder.ts",{"id":"file:/workspace/apps/web/src/types/config-folder.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/types/config-folder.ts","metadata":{"lineCount":37,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/api/config-folders.ts:listConfigFolders",{"id":"/workspace/apps/web/src/api/config-folders.ts:listConfigFolders","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/config-folders.ts","symbolName":"listConfigFolders","symbolKind":"function","exported":true,"metadata":{"line":16,"column":34,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["external:apiClient.get",{"id":"external:apiClient.get","kind":"external","language":"jsts","metadata":{"unresolvedName":"apiClient.get"}}],["/workspace/apps/web/src/api/config-folders.ts:getConfigFolder",{"id":"/workspace/apps/web/src/api/config-folders.ts:getConfigFolder","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/config-folders.ts","symbolName":"getConfigFolder","symbolKind":"function","exported":true,"metadata":{"line":21,"column":32,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/config-folders.ts:createConfigFolder",{"id":"/workspace/apps/web/src/api/config-folders.ts:createConfigFolder","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/config-folders.ts","symbolName":"createConfigFolder","symbolKind":"function","exported":true,"metadata":{"line":26,"column":35,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["external:apiClient.post",{"id":"external:apiClient.post","kind":"external","language":"jsts","metadata":{"unresolvedName":"apiClient.post"}}],["/workspace/apps/web/src/api/config-folders.ts:updateConfigFolder",{"id":"/workspace/apps/web/src/api/config-folders.ts:updateConfigFolder","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/config-folders.ts","symbolName":"updateConfigFolder","symbolKind":"function","exported":true,"metadata":{"line":33,"column":35,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["external:apiClient.put",{"id":"external:apiClient.put","kind":"external","language":"jsts","metadata":{"unresolvedName":"apiClient.put"}}],["/workspace/apps/web/src/api/config-folders.ts:deleteConfigFolder",{"id":"/workspace/apps/web/src/api/config-folders.ts:deleteConfigFolder","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/config-folders.ts","symbolName":"deleteConfigFolder","symbolKind":"function","exported":true,"metadata":{"line":44,"column":35,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["external:apiClient.delete",{"id":"external:apiClient.delete","kind":"external","language":"jsts","metadata":{"unresolvedName":"apiClient.delete"}}],["/workspace/apps/web/src/api/config-folders.ts:addProjectOverride",{"id":"/workspace/apps/web/src/api/config-folders.ts:addProjectOverride","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/config-folders.ts","symbolName":"addProjectOverride","symbolKind":"function","exported":true,"metadata":{"line":48,"column":35,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/config-folders.ts:updateProjectOverride",{"id":"/workspace/apps/web/src/api/config-folders.ts:updateProjectOverride","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/config-folders.ts","symbolName":"updateProjectOverride","symbolKind":"function","exported":true,"metadata":{"line":60,"column":38,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/config-folders.ts:deleteProjectOverride",{"id":"/workspace/apps/web/src/api/config-folders.ts:deleteProjectOverride","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/config-folders.ts","symbolName":"deleteProjectOverride","symbolKind":"function","exported":true,"metadata":{"line":72,"column":38,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["file:/workspace/apps/web/src/api/config_profiles.ts",{"id":"file:/workspace/apps/web/src/api/config_profiles.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/api/config_profiles.ts","metadata":{"lineCount":190,"featureKind":"service","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/config_profiles.ts:listConfigProfiles",{"id":"/workspace/apps/web/src/api/config_profiles.ts:listConfigProfiles","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/config_profiles.ts","symbolName":"listConfigProfiles","symbolKind":"function","exported":true,"metadata":{"line":101,"column":35,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/config_profiles.ts:getConfigProfile",{"id":"/workspace/apps/web/src/api/config_profiles.ts:getConfigProfile","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/config_profiles.ts","symbolName":"getConfigProfile","symbolKind":"function","exported":true,"metadata":{"line":111,"column":33,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/config_profiles.ts:createConfigProfile",{"id":"/workspace/apps/web/src/api/config_profiles.ts:createConfigProfile","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/config_profiles.ts","symbolName":"createConfigProfile","symbolKind":"function","exported":true,"metadata":{"line":116,"column":36,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/config_profiles.ts:updateConfigProfile",{"id":"/workspace/apps/web/src/api/config_profiles.ts:updateConfigProfile","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/config_profiles.ts","symbolName":"updateConfigProfile","symbolKind":"function","exported":true,"metadata":{"line":126,"column":36,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/config_profiles.ts:deleteConfigProfile",{"id":"/workspace/apps/web/src/api/config_profiles.ts:deleteConfigProfile","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/config_profiles.ts","symbolName":"deleteConfigProfile","symbolKind":"function","exported":true,"metadata":{"line":137,"column":36,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/config_profiles.ts:updateProfileIncludes",{"id":"/workspace/apps/web/src/api/config_profiles.ts:updateProfileIncludes","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/config_profiles.ts","symbolName":"updateProfileIncludes","symbolKind":"function","exported":true,"metadata":{"line":141,"column":38,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/config_profiles.ts:previewConfigProfile",{"id":"/workspace/apps/web/src/api/config_profiles.ts:previewConfigProfile","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/config_profiles.ts","symbolName":"previewConfigProfile","symbolKind":"function","exported":true,"metadata":{"line":152,"column":37,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/config_profiles.ts:resolveDefaultProfile",{"id":"/workspace/apps/web/src/api/config_profiles.ts:resolveDefaultProfile","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/config_profiles.ts","symbolName":"resolveDefaultProfile","symbolKind":"function","exported":true,"metadata":{"line":161,"column":38,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/config_profiles.ts:validateGitUrl",{"id":"/workspace/apps/web/src/api/config_profiles.ts:validateGitUrl","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/config_profiles.ts","symbolName":"validateGitUrl","symbolKind":"function","exported":true,"metadata":{"line":180,"column":31,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["file:/workspace/apps/web/src/api/dashboard.ts",{"id":"file:/workspace/apps/web/src/api/dashboard.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/api/dashboard.ts","metadata":{"lineCount":14,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/dashboard.ts:getDashboardSummary",{"id":"/workspace/apps/web/src/api/dashboard.ts:getDashboardSummary","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/dashboard.ts","symbolName":"getDashboardSummary","symbolKind":"function","exported":true,"metadata":{"line":10,"column":36,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["file:/workspace/apps/web/src/api/events.ts",{"id":"file:/workspace/apps/web/src/api/events.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/api/events.ts","metadata":{"lineCount":23,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/events.ts:createEventSource",{"id":"/workspace/apps/web/src/api/events.ts:createEventSource","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/events.ts","symbolName":"createEventSource","symbolKind":"function","exported":true,"metadata":{"line":3,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/events.ts:probeEventStreamStatus",{"id":"/workspace/apps/web/src/api/events.ts:probeEventStreamStatus","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/events.ts","symbolName":"probeEventStreamStatus","symbolKind":"function","exported":true,"metadata":{"line":9,"column":1,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["external:controller.abort",{"id":"external:controller.abort","kind":"external","language":"jsts","metadata":{"unresolvedName":"controller.abort"}}],["symbol-name:fetch",{"id":"symbol-name:fetch","kind":"symbol","language":"jsts","symbolName":"fetch","metadata":{"unresolvedName":"fetch"}}],["symbol-name:clearTimeout",{"id":"symbol-name:clearTimeout","kind":"symbol","language":"jsts","symbolName":"clearTimeout","metadata":{"unresolvedName":"clearTimeout"}}],["external:searchParams.set",{"id":"external:searchParams.set","kind":"external","language":"jsts","metadata":{"unresolvedName":"searchParams.set"}}],["symbol-name:String",{"id":"symbol-name:String","kind":"symbol","language":"jsts","symbolName":"String","metadata":{"unresolvedName":"String"}}],["external:searchParams.toString",{"id":"external:searchParams.toString","kind":"external","language":"jsts","metadata":{"unresolvedName":"searchParams.toString"}}],["file:/workspace/apps/web/src/api/notifications.ts",{"id":"file:/workspace/apps/web/src/api/notifications.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/api/notifications.ts","metadata":{"lineCount":74,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/notifications.ts:getNotifications",{"id":"/workspace/apps/web/src/api/notifications.ts:getNotifications","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/notifications.ts","symbolName":"getNotifications","symbolKind":"function","exported":true,"metadata":{"line":37,"column":33,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/notifications.ts:getUnreadCount",{"id":"/workspace/apps/web/src/api/notifications.ts:getUnreadCount","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/notifications.ts","symbolName":"getUnreadCount","symbolKind":"function","exported":true,"metadata":{"line":43,"column":31,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/notifications.ts:markNotificationRead",{"id":"/workspace/apps/web/src/api/notifications.ts:markNotificationRead","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/notifications.ts","symbolName":"markNotificationRead","symbolKind":"function","exported":true,"metadata":{"line":50,"column":37,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["external:apiClient.patch",{"id":"external:apiClient.patch","kind":"external","language":"jsts","metadata":{"unresolvedName":"apiClient.patch"}}],["/workspace/apps/web/src/api/notifications.ts:markAllNotificationsRead",{"id":"/workspace/apps/web/src/api/notifications.ts:markAllNotificationsRead","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/notifications.ts","symbolName":"markAllNotificationsRead","symbolKind":"function","exported":true,"metadata":{"line":59,"column":41,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/notifications.ts:dismissNotification",{"id":"/workspace/apps/web/src/api/notifications.ts:dismissNotification","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/notifications.ts","symbolName":"dismissNotification","symbolKind":"function","exported":true,"metadata":{"line":66,"column":36,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/notifications.ts:clearAllNotifications",{"id":"/workspace/apps/web/src/api/notifications.ts:clearAllNotifications","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/notifications.ts","symbolName":"clearAllNotifications","symbolKind":"function","exported":true,"metadata":{"line":70,"column":38,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["file:/workspace/apps/web/src/api/profile.ts",{"id":"file:/workspace/apps/web/src/api/profile.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/api/profile.ts","metadata":{"lineCount":31,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["file:/workspace/apps/web/src/types",{"id":"file:/workspace/apps/web/src/types","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/types"}],["/workspace/apps/web/src/api/profile.ts:getProfile",{"id":"/workspace/apps/web/src/api/profile.ts:getProfile","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/profile.ts","symbolName":"getProfile","symbolKind":"function","exported":true,"metadata":{"line":11,"column":27,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/profile.ts:updateProfile",{"id":"/workspace/apps/web/src/api/profile.ts:updateProfile","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/profile.ts","symbolName":"updateProfile","symbolKind":"function","exported":true,"metadata":{"line":16,"column":30,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/profile.ts:uploadAvatar",{"id":"/workspace/apps/web/src/api/profile.ts:uploadAvatar","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/profile.ts","symbolName":"uploadAvatar","symbolKind":"function","exported":true,"metadata":{"line":21,"column":29,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["external:formData.append",{"id":"external:formData.append","kind":"external","language":"jsts","metadata":{"unresolvedName":"formData.append"}}],["file:/workspace/apps/web/src/api/projects.ts",{"id":"file:/workspace/apps/web/src/api/projects.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/api/projects.ts","metadata":{"lineCount":55,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/projects.ts:listProjects",{"id":"/workspace/apps/web/src/api/projects.ts:listProjects","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/projects.ts","symbolName":"listProjects","symbolKind":"function","exported":true,"metadata":{"line":18,"column":29,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/projects.ts:createProject",{"id":"/workspace/apps/web/src/api/projects.ts:createProject","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/projects.ts","symbolName":"createProject","symbolKind":"function","exported":true,"metadata":{"line":23,"column":30,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/projects.ts:updateProject",{"id":"/workspace/apps/web/src/api/projects.ts:updateProject","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/projects.ts","symbolName":"updateProject","symbolKind":"function","exported":true,"metadata":{"line":30,"column":30,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/projects.ts:deleteProject",{"id":"/workspace/apps/web/src/api/projects.ts:deleteProject","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/projects.ts","symbolName":"deleteProject","symbolKind":"function","exported":true,"metadata":{"line":41,"column":30,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/projects.ts:setDefaultSSHKey",{"id":"/workspace/apps/web/src/api/projects.ts:setDefaultSSHKey","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/projects.ts","symbolName":"setDefaultSSHKey","symbolKind":"function","exported":true,"metadata":{"line":45,"column":33,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["file:/workspace/apps/web/src/types/tool-instance.ts",{"id":"file:/workspace/apps/web/src/types/tool-instance.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/types/tool-instance.ts","metadata":{"lineCount":13,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/api/settings.ts",{"id":"file:/workspace/apps/web/src/api/settings.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/api/settings.ts","metadata":{"lineCount":34,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/settings.ts:getUserConfig",{"id":"/workspace/apps/web/src/api/settings.ts:getUserConfig","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/settings.ts","symbolName":"getUserConfig","symbolKind":"function","exported":true,"metadata":{"line":23,"column":30,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/settings.ts:updateUserConfig",{"id":"/workspace/apps/web/src/api/settings.ts:updateUserConfig","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/settings.ts","symbolName":"updateUserConfig","symbolKind":"function","exported":true,"metadata":{"line":28,"column":33,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["file:/workspace/apps/web/src/api/ssh-keys.ts",{"id":"file:/workspace/apps/web/src/api/ssh-keys.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/api/ssh-keys.ts","metadata":{"lineCount":54,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/ssh-keys.ts:listSSHKeys",{"id":"/workspace/apps/web/src/api/ssh-keys.ts:listSSHKeys","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/ssh-keys.ts","symbolName":"listSSHKeys","symbolKind":"function","exported":true,"metadata":{"line":14,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/ssh-keys.ts:createSSHKey",{"id":"/workspace/apps/web/src/api/ssh-keys.ts:createSSHKey","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/ssh-keys.ts","symbolName":"createSSHKey","symbolKind":"function","exported":true,"metadata":{"line":19,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/ssh-keys.ts:deleteSSHKey",{"id":"/workspace/apps/web/src/api/ssh-keys.ts:deleteSSHKey","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/ssh-keys.ts","symbolName":"deleteSSHKey","symbolKind":"function","exported":true,"metadata":{"line":24,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/ssh-keys.ts:signPayload",{"id":"/workspace/apps/web/src/api/ssh-keys.ts:signPayload","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/ssh-keys.ts","symbolName":"signPayload","symbolKind":"function","exported":true,"metadata":{"line":45,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/ssh-keys.ts:verifySignature",{"id":"/workspace/apps/web/src/api/ssh-keys.ts:verifySignature","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/ssh-keys.ts","symbolName":"verifySignature","symbolKind":"function","exported":true,"metadata":{"line":50,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["file:/workspace/apps/web/src/api/terminal.ts",{"id":"file:/workspace/apps/web/src/api/terminal.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/api/terminal.ts","metadata":{"lineCount":78,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/terminal.ts:listTerminalSessions",{"id":"/workspace/apps/web/src/api/terminal.ts:listTerminalSessions","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/terminal.ts","symbolName":"listTerminalSessions","symbolKind":"function","exported":true,"metadata":{"line":27,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization","auth","user-input"]}}],["/workspace/apps/web/src/api/terminal.ts:createTerminalSession",{"id":"/workspace/apps/web/src/api/terminal.ts:createTerminalSession","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/terminal.ts","symbolName":"createTerminalSession","symbolKind":"function","exported":true,"metadata":{"line":36,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization","auth","user-input"]}}],["/workspace/apps/web/src/api/terminal.ts:closeTerminalSession",{"id":"/workspace/apps/web/src/api/terminal.ts:closeTerminalSession","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/terminal.ts","symbolName":"closeTerminalSession","symbolKind":"function","exported":true,"metadata":{"line":47,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization","auth","user-input"]}}],["/workspace/apps/web/src/api/terminal.ts:resetTerminalSession",{"id":"/workspace/apps/web/src/api/terminal.ts:resetTerminalSession","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/terminal.ts","symbolName":"resetTerminalSession","symbolKind":"function","exported":true,"metadata":{"line":57,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization","auth","user-input"]}}],["/workspace/apps/web/src/api/terminal.ts:renameTerminalSession",{"id":"/workspace/apps/web/src/api/terminal.ts:renameTerminalSession","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/terminal.ts","symbolName":"renameTerminalSession","symbolKind":"function","exported":true,"metadata":{"line":67,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization","auth","user-input"]}}],["file:/workspace/apps/web/src/api/tool-configs.ts",{"id":"file:/workspace/apps/web/src/api/tool-configs.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/api/tool-configs.ts","metadata":{"lineCount":53,"featureKind":"service","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["file:/workspace/apps/web/src/types/tool-config.ts",{"id":"file:/workspace/apps/web/src/types/tool-config.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/types/tool-config.ts","metadata":{"lineCount":29,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/api/tool-configs.ts:listToolConfigs",{"id":"/workspace/apps/web/src/api/tool-configs.ts:listToolConfigs","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/tool-configs.ts","symbolName":"listToolConfigs","symbolKind":"function","exported":true,"metadata":{"line":6,"column":32,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["external:params.append",{"id":"external:params.append","kind":"external","language":"jsts","metadata":{"unresolvedName":"params.append"}}],["external:params.toString",{"id":"external:params.toString","kind":"external","language":"jsts","metadata":{"unresolvedName":"params.toString"}}],["/workspace/apps/web/src/api/tool-configs.ts:createToolConfig",{"id":"/workspace/apps/web/src/api/tool-configs.ts:createToolConfig","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/tool-configs.ts","symbolName":"createToolConfig","symbolKind":"function","exported":true,"metadata":{"line":20,"column":33,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/tool-configs.ts:updateToolConfig",{"id":"/workspace/apps/web/src/api/tool-configs.ts:updateToolConfig","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/tool-configs.ts","symbolName":"updateToolConfig","symbolKind":"function","exported":true,"metadata":{"line":30,"column":33,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/tool-configs.ts:deleteToolConfig",{"id":"/workspace/apps/web/src/api/tool-configs.ts:deleteToolConfig","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/tool-configs.ts","symbolName":"deleteToolConfig","symbolKind":"function","exported":true,"metadata":{"line":41,"column":33,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/tool-configs.ts:getToolConfigDefaults",{"id":"/workspace/apps/web/src/api/tool-configs.ts:getToolConfigDefaults","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/tool-configs.ts","symbolName":"getToolConfigDefaults","symbolKind":"function","exported":true,"metadata":{"line":45,"column":38,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["file:/workspace/apps/web/src/api/tool-types.test.ts",{"id":"file:/workspace/apps/web/src/api/tool-types.test.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/api/tool-types.test.ts","metadata":{"lineCount":228,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["file:/workspace/apps/web/src/api/tool-types.ts",{"id":"file:/workspace/apps/web/src/api/tool-types.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/api/tool-types.ts","metadata":{"lineCount":52,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/tool-types.test.ts:",{"id":"/workspace/apps/web/src/api/tool-types.test.ts:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/tool-types.test.ts","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":219,"column":29,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["symbol-name:listToolTypes",{"id":"symbol-name:listToolTypes","kind":"symbol","language":"jsts","symbolName":"listToolTypes","metadata":{"unresolvedName":"listToolTypes"}}],["external:expect(result[0].definition_type).toBe",{"id":"external:expect(result[0].definition_type).toBe","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(result[0].definition_type).toBe"}}],["external:expect(result[0].dockerfile_template).toBe",{"id":"external:expect(result[0].dockerfile_template).toBe","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(result[0].dockerfile_template).toBe"}}],["external:expect(result[0].readiness_probe).toEqual",{"id":"external:expect(result[0].readiness_probe).toEqual","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(result[0].readiness_probe).toEqual"}}],["external:expect(result[0].dockerfile_template).toBeNull",{"id":"external:expect(result[0].dockerfile_template).toBeNull","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(result[0].dockerfile_template).toBeNull"}}],["symbol-name:createToolType",{"id":"symbol-name:createToolType","kind":"symbol","language":"jsts","symbolName":"createToolType","metadata":{"unresolvedName":"createToolType"}}],["external:expect(result.definition_type).toBe",{"id":"external:expect(result.definition_type).toBe","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(result.definition_type).toBe"}}],["external:expect(result.readiness_probe).toEqual",{"id":"external:expect(result.readiness_probe).toEqual","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(result.readiness_probe).toEqual"}}],["symbol-name:validateToolType",{"id":"symbol-name:validateToolType","kind":"symbol","language":"jsts","symbolName":"validateToolType","metadata":{"unresolvedName":"validateToolType"}}],["external:expect(result.valid).toBe",{"id":"external:expect(result.valid).toBe","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(result.valid).toBe"}}],["external:expect(result.errors).toContain",{"id":"external:expect(result.errors).toContain","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(result.errors).toContain"}}],["symbol-name:updateToolType",{"id":"symbol-name:updateToolType","kind":"symbol","language":"jsts","symbolName":"updateToolType","metadata":{"unresolvedName":"updateToolType"}}],["symbol-name:deleteToolType",{"id":"symbol-name:deleteToolType","kind":"symbol","language":"jsts","symbolName":"deleteToolType","metadata":{"unresolvedName":"deleteToolType"}}],["file:/workspace/apps/web/src/types/tool-type.ts",{"id":"file:/workspace/apps/web/src/types/tool-type.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/types/tool-type.ts","metadata":{"lineCount":55,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/api/tool-types.ts:listToolTypes",{"id":"/workspace/apps/web/src/api/tool-types.ts:listToolTypes","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/tool-types.ts","symbolName":"listToolTypes","symbolKind":"function","exported":true,"metadata":{"line":15,"column":30,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/tool-types.ts:getToolType",{"id":"/workspace/apps/web/src/api/tool-types.ts:getToolType","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/tool-types.ts","symbolName":"getToolType","symbolKind":"function","exported":true,"metadata":{"line":20,"column":28,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/tool-types.ts:createToolType",{"id":"/workspace/apps/web/src/api/tool-types.ts:createToolType","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/tool-types.ts","symbolName":"createToolType","symbolKind":"function","exported":true,"metadata":{"line":25,"column":31,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/tool-types.ts:updateToolType",{"id":"/workspace/apps/web/src/api/tool-types.ts:updateToolType","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/tool-types.ts","symbolName":"updateToolType","symbolKind":"function","exported":true,"metadata":{"line":32,"column":31,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/tool-types.ts:deleteToolType",{"id":"/workspace/apps/web/src/api/tool-types.ts:deleteToolType","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/tool-types.ts","symbolName":"deleteToolType","symbolKind":"function","exported":true,"metadata":{"line":40,"column":31,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/tool-types.ts:validateToolType",{"id":"/workspace/apps/web/src/api/tool-types.ts:validateToolType","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/tool-types.ts","symbolName":"validateToolType","symbolKind":"function","exported":true,"metadata":{"line":44,"column":33,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["file:/workspace/apps/web/src/api/tool_definitions.ts",{"id":"file:/workspace/apps/web/src/api/tool_definitions.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/api/tool_definitions.ts","metadata":{"lineCount":109,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/tool_definitions.ts:listToolDefinitions",{"id":"/workspace/apps/web/src/api/tool_definitions.ts:listToolDefinitions","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/tool_definitions.ts","symbolName":"listToolDefinitions","symbolKind":"function","exported":true,"metadata":{"line":51,"column":36,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/tool_definitions.ts:getToolDefinition",{"id":"/workspace/apps/web/src/api/tool_definitions.ts:getToolDefinition","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/tool_definitions.ts","symbolName":"getToolDefinition","symbolKind":"function","exported":true,"metadata":{"line":62,"column":34,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/tool_definitions.ts:createToolDefinition",{"id":"/workspace/apps/web/src/api/tool_definitions.ts:createToolDefinition","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/tool_definitions.ts","symbolName":"createToolDefinition","symbolKind":"function","exported":true,"metadata":{"line":71,"column":37,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/tool_definitions.ts:updateToolDefinition",{"id":"/workspace/apps/web/src/api/tool_definitions.ts:updateToolDefinition","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/tool_definitions.ts","symbolName":"updateToolDefinition","symbolKind":"function","exported":true,"metadata":{"line":81,"column":37,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/tool_definitions.ts:deleteToolDefinition",{"id":"/workspace/apps/web/src/api/tool_definitions.ts:deleteToolDefinition","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/tool_definitions.ts","symbolName":"deleteToolDefinition","symbolKind":"function","exported":true,"metadata":{"line":92,"column":37,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/tool_definitions.ts:compileToolDefinition",{"id":"/workspace/apps/web/src/api/tool_definitions.ts:compileToolDefinition","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/tool_definitions.ts","symbolName":"compileToolDefinition","symbolKind":"function","exported":true,"metadata":{"line":101,"column":38,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["file:/workspace/apps/web/src/api/workspace-files.ts",{"id":"file:/workspace/apps/web/src/api/workspace-files.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/api/workspace-files.ts","metadata":{"lineCount":46,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/workspace-files.ts:listWorkspaceFiles",{"id":"/workspace/apps/web/src/api/workspace-files.ts:listWorkspaceFiles","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/workspace-files.ts","symbolName":"listWorkspaceFiles","symbolKind":"function","exported":true,"metadata":{"line":12,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/workspace-files.ts:getWorkspaceFileContent",{"id":"/workspace/apps/web/src/api/workspace-files.ts:getWorkspaceFileContent","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/workspace-files.ts","symbolName":"getWorkspaceFileContent","symbolKind":"function","exported":true,"metadata":{"line":23,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/workspace-files.ts:saveWorkspaceFile",{"id":"/workspace/apps/web/src/api/workspace-files.ts:saveWorkspaceFile","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/workspace-files.ts","symbolName":"saveWorkspaceFile","symbolKind":"function","exported":true,"metadata":{"line":34,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["file:/workspace/apps/web/src/api/workspace-git.ts",{"id":"file:/workspace/apps/web/src/api/workspace-git.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/api/workspace-git.ts","metadata":{"lineCount":76,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/workspace-git.ts:getGitStatus",{"id":"/workspace/apps/web/src/api/workspace-git.ts:getGitStatus","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/workspace-git.ts","symbolName":"getGitStatus","symbolKind":"function","exported":true,"metadata":{"line":22,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/workspace-git.ts:getGitBranches",{"id":"/workspace/apps/web/src/api/workspace-git.ts:getGitBranches","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/workspace-git.ts","symbolName":"getGitBranches","symbolKind":"function","exported":true,"metadata":{"line":29,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/workspace-git.ts:gitCommit",{"id":"/workspace/apps/web/src/api/workspace-git.ts:gitCommit","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/workspace-git.ts","symbolName":"gitCommit","symbolKind":"function","exported":true,"metadata":{"line":39,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/workspace-git.ts:gitPush",{"id":"/workspace/apps/web/src/api/workspace-git.ts:gitPush","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/workspace-git.ts","symbolName":"gitPush","symbolKind":"function","exported":true,"metadata":{"line":46,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/workspace-git.ts:gitPull",{"id":"/workspace/apps/web/src/api/workspace-git.ts:gitPull","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/workspace-git.ts","symbolName":"gitPull","symbolKind":"function","exported":true,"metadata":{"line":50,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/workspace-git.ts:gitFetch",{"id":"/workspace/apps/web/src/api/workspace-git.ts:gitFetch","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/workspace-git.ts","symbolName":"gitFetch","symbolKind":"function","exported":true,"metadata":{"line":54,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/workspace-git.ts:gitCheckout",{"id":"/workspace/apps/web/src/api/workspace-git.ts:gitCheckout","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/workspace-git.ts","symbolName":"gitCheckout","symbolKind":"function","exported":true,"metadata":{"line":58,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/workspace-git.ts:getGitHistory",{"id":"/workspace/apps/web/src/api/workspace-git.ts:getGitHistory","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/workspace-git.ts","symbolName":"getGitHistory","symbolKind":"function","exported":true,"metadata":{"line":65,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["file:/workspace/apps/web/src/api/workspace-instances.ts",{"id":"file:/workspace/apps/web/src/api/workspace-instances.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/api/workspace-instances.ts","metadata":{"lineCount":31,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/workspace-instances.ts:listWorkspaceInstances",{"id":"/workspace/apps/web/src/api/workspace-instances.ts:listWorkspaceInstances","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/workspace-instances.ts","symbolName":"listWorkspaceInstances","symbolKind":"function","exported":true,"metadata":{"line":6,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/workspace-instances.ts:createWorkspaceInstance",{"id":"/workspace/apps/web/src/api/workspace-instances.ts:createWorkspaceInstance","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/workspace-instances.ts","symbolName":"createWorkspaceInstance","symbolKind":"function","exported":true,"metadata":{"line":15,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["file:/workspace/apps/web/src/api/workspaces.ts",{"id":"file:/workspace/apps/web/src/api/workspaces.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/api/workspaces.ts","metadata":{"lineCount":93,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["file:/workspace/apps/web/src/types/workspace.ts",{"id":"file:/workspace/apps/web/src/types/workspace.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/types/workspace.ts","metadata":{"lineCount":31,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/api/workspaces.ts:workspaceUrl",{"id":"/workspace/apps/web/src/api/workspaces.ts:workspaceUrl","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/workspaces.ts","symbolName":"workspaceUrl","symbolKind":"function","exported":false,"metadata":{"line":10,"column":1,"cyclomaticComplexity":2,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/workspaces.ts:listWorkspaces",{"id":"/workspace/apps/web/src/api/workspaces.ts:listWorkspaces","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/workspaces.ts","symbolName":"listWorkspaces","symbolKind":"function","exported":true,"metadata":{"line":15,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["symbol-name:workspaceUrl",{"id":"symbol-name:workspaceUrl","kind":"symbol","language":"jsts","symbolName":"workspaceUrl","metadata":{"unresolvedName":"workspaceUrl"}}],["/workspace/apps/web/src/api/workspaces.ts:listAllWorkspaces",{"id":"/workspace/apps/web/src/api/workspaces.ts:listAllWorkspaces","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/workspaces.ts","symbolName":"listAllWorkspaces","symbolKind":"function","exported":true,"metadata":{"line":25,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/workspaces.ts:createWorkspace",{"id":"/workspace/apps/web/src/api/workspaces.ts:createWorkspace","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/workspaces.ts","symbolName":"createWorkspace","symbolKind":"function","exported":true,"metadata":{"line":30,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/workspaces.ts:createWorkspaceTopLevel",{"id":"/workspace/apps/web/src/api/workspaces.ts:createWorkspaceTopLevel","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/workspaces.ts","symbolName":"createWorkspaceTopLevel","symbolKind":"function","exported":true,"metadata":{"line":42,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/workspaces.ts:getWorkspace",{"id":"/workspace/apps/web/src/api/workspaces.ts:getWorkspace","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/workspaces.ts","symbolName":"getWorkspace","symbolKind":"function","exported":true,"metadata":{"line":49,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/workspaces.ts:updateWorkspace",{"id":"/workspace/apps/web/src/api/workspaces.ts:updateWorkspace","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/workspaces.ts","symbolName":"updateWorkspace","symbolKind":"function","exported":true,"metadata":{"line":60,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/workspaces.ts:deleteWorkspace",{"id":"/workspace/apps/web/src/api/workspaces.ts:deleteWorkspace","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/workspaces.ts","symbolName":"deleteWorkspace","symbolKind":"function","exported":true,"metadata":{"line":73,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/api/workspaces.ts:syncWorkspace",{"id":"/workspace/apps/web/src/api/workspaces.ts:syncWorkspace","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/workspaces.ts","symbolName":"syncWorkspace","symbolKind":"function","exported":true,"metadata":{"line":83,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["network","external-api","serialization"]}}],["file:/workspace/apps/web/src/components/ProtectedRoute.test.tsx",{"id":"file:/workspace/apps/web/src/components/ProtectedRoute.test.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/ProtectedRoute.test.tsx","metadata":{"lineCount":50,"featureKind":"library","trustBoundaries":[]}}],["external:@testing-library/react",{"id":"external:@testing-library/react","kind":"external","language":"jsts","metadata":{"source":"@testing-library/react"}}],["external:react-router-dom",{"id":"external:react-router-dom","kind":"external","language":"jsts","metadata":{"source":"react-router-dom"}}],["file:/workspace/apps/web/src/components/ProtectedRoute.tsx",{"id":"file:/workspace/apps/web/src/components/ProtectedRoute.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/ProtectedRoute.tsx","metadata":{"lineCount":20,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/ProtectedRoute.test.tsx:",{"id":"/workspace/apps/web/src/components/ProtectedRoute.test.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/ProtectedRoute.test.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":28,"column":50,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:mockUseAuth.mockReturnValue",{"id":"external:mockUseAuth.mockReturnValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockUseAuth.mockReturnValue"}}],["symbol-name:render",{"id":"symbol-name:render","kind":"symbol","language":"jsts","symbolName":"render","metadata":{"unresolvedName":"render"}}],["external:expect(screen.getByText(\"Checking session...\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"Checking session...\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"Checking session...\")).toBeInTheDocument"}}],["external:screen.getByText",{"id":"external:screen.getByText","kind":"external","language":"jsts","metadata":{"unresolvedName":"screen.getByText"}}],["external:expect(screen.getByText(\"login page\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"login page\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"login page\")).toBeInTheDocument"}}],["file:/workspace/apps/web/src/state/auth.tsx",{"id":"file:/workspace/apps/web/src/state/auth.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/state/auth.tsx","metadata":{"lineCount":64,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/components/ProtectedRoute.tsx:ProtectedRoute",{"id":"/workspace/apps/web/src/components/ProtectedRoute.tsx:ProtectedRoute","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/ProtectedRoute.tsx","symbolName":"ProtectedRoute","symbolKind":"function","exported":true,"metadata":{"line":5,"column":31,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:useAuth",{"id":"symbol-name:useAuth","kind":"symbol","language":"jsts","symbolName":"useAuth","metadata":{"unresolvedName":"useAuth"}}],["symbol-name:useLocation",{"id":"symbol-name:useLocation","kind":"symbol","language":"jsts","symbolName":"useLocation","metadata":{"unresolvedName":"useLocation"}}],["symbol-name:encodeURIComponent",{"id":"symbol-name:encodeURIComponent","kind":"symbol","language":"jsts","symbolName":"encodeURIComponent","metadata":{"unresolvedName":"encodeURIComponent"}}],["file:/workspace/apps/web/src/components/create-session-form.tsx",{"id":"file:/workspace/apps/web/src/components/create-session-form.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/create-session-form.tsx","metadata":{"lineCount":576,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["external:react",{"id":"external:react","kind":"external","language":"jsts","metadata":{"source":"react"}}],["external:@/components/ui/Icon",{"id":"external:@/components/ui/Icon","kind":"external","language":"jsts","metadata":{"source":"@/components/ui/Icon"}}],["external:@/api/git-repositories",{"id":"external:@/api/git-repositories","kind":"external","language":"jsts","metadata":{"source":"@/api/git-repositories"}}],["external:@/api/tool-types",{"id":"external:@/api/tool-types","kind":"external","language":"jsts","metadata":{"source":"@/api/tool-types"}}],["external:@/api/ssh-keys",{"id":"external:@/api/ssh-keys","kind":"external","language":"jsts","metadata":{"source":"@/api/ssh-keys"}}],["/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm",{"id":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/create-session-form.tsx","symbolName":"CreateSessionForm","symbolKind":"function","exported":true,"metadata":{"line":27,"column":34,"cyclomaticComplexity":81,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["symbol-name:useState",{"id":"symbol-name:useState","kind":"symbol","language":"jsts","symbolName":"useState","metadata":{"unresolvedName":"useState"}}],["symbol-name:useEffect",{"id":"symbol-name:useEffect","kind":"symbol","language":"jsts","symbolName":"useEffect","metadata":{"unresolvedName":"useEffect"}}],["symbol-name:listSSHKeys",{"id":"symbol-name:listSSHKeys","kind":"symbol","language":"jsts","symbolName":"listSSHKeys","metadata":{"unresolvedName":"listSSHKeys"}}],["symbol-name:setSshKeys",{"id":"symbol-name:setSshKeys","kind":"symbol","language":"jsts","symbolName":"setSshKeys","metadata":{"unresolvedName":"setSshKeys"}}],["symbol-name:loadKeys",{"id":"symbol-name:loadKeys","kind":"symbol","language":"jsts","symbolName":"loadKeys","metadata":{"unresolvedName":"loadKeys"}}],["symbol-name:setConfigProfiles",{"id":"symbol-name:setConfigProfiles","kind":"symbol","language":"jsts","symbolName":"setConfigProfiles","metadata":{"unresolvedName":"setConfigProfiles"}}],["symbol-name:setSelectedConfigProfile",{"id":"symbol-name:setSelectedConfigProfile","kind":"symbol","language":"jsts","symbolName":"setSelectedConfigProfile","metadata":{"unresolvedName":"setSelectedConfigProfile"}}],["symbol-name:listConfigProfiles",{"id":"symbol-name:listConfigProfiles","kind":"symbol","language":"jsts","symbolName":"listConfigProfiles","metadata":{"unresolvedName":"listConfigProfiles"}}],["external:profiles.find",{"id":"external:profiles.find","kind":"external","language":"jsts","metadata":{"unresolvedName":"profiles.find"}}],["symbol-name:loadProfiles",{"id":"symbol-name:loadProfiles","kind":"symbol","language":"jsts","symbolName":"loadProfiles","metadata":{"unresolvedName":"loadProfiles"}}],["symbol-name:setBranches",{"id":"symbol-name:setBranches","kind":"symbol","language":"jsts","symbolName":"setBranches","metadata":{"unresolvedName":"setBranches"}}],["symbol-name:setIsLoadingBranches",{"id":"symbol-name:setIsLoadingBranches","kind":"symbol","language":"jsts","symbolName":"setIsLoadingBranches","metadata":{"unresolvedName":"setIsLoadingBranches"}}],["symbol-name:listRepositoryBranches",{"id":"symbol-name:listRepositoryBranches","kind":"symbol","language":"jsts","symbolName":"listRepositoryBranches","metadata":{"unresolvedName":"listRepositoryBranches"}}],["symbol-name:setBranch",{"id":"symbol-name:setBranch","kind":"symbol","language":"jsts","symbolName":"setBranch","metadata":{"unresolvedName":"setBranch"}}],["symbol-name:setBaseBranch",{"id":"symbol-name:setBaseBranch","kind":"symbol","language":"jsts","symbolName":"setBaseBranch","metadata":{"unresolvedName":"setBaseBranch"}}],["symbol-name:loadBranches",{"id":"symbol-name:loadBranches","kind":"symbol","language":"jsts","symbolName":"loadBranches","metadata":{"unresolvedName":"loadBranches"}}],["external:repositories.filter",{"id":"external:repositories.filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"repositories.filter"}}],["external:event.preventDefault",{"id":"external:event.preventDefault","kind":"external","language":"jsts","metadata":{"unresolvedName":"event.preventDefault"}}],["symbol-name:setError",{"id":"symbol-name:setError","kind":"symbol","language":"jsts","symbolName":"setError","metadata":{"unresolvedName":"setError"}}],["external:repositories.find",{"id":"external:repositories.find","kind":"external","language":"jsts","metadata":{"unresolvedName":"repositories.find"}}],["symbol-name:setStatus",{"id":"symbol-name:setStatus","kind":"symbol","language":"jsts","symbolName":"setStatus","metadata":{"unresolvedName":"setStatus"}}],["symbol-name:setProgress",{"id":"symbol-name:setProgress","kind":"symbol","language":"jsts","symbolName":"setProgress","metadata":{"unresolvedName":"setProgress"}}],["symbol-name:createInstance",{"id":"symbol-name:createInstance","kind":"symbol","language":"jsts","symbolName":"createInstance","metadata":{"unresolvedName":"createInstance"}}],["symbol-name:startInstance",{"id":"symbol-name:startInstance","kind":"symbol","language":"jsts","symbolName":"startInstance","metadata":{"unresolvedName":"startInstance"}}],["symbol-name:setSelectedProject",{"id":"symbol-name:setSelectedProject","kind":"symbol","language":"jsts","symbolName":"setSelectedProject","metadata":{"unresolvedName":"setSelectedProject"}}],["symbol-name:setSelectedRepo",{"id":"symbol-name:setSelectedRepo","kind":"symbol","language":"jsts","symbolName":"setSelectedRepo","metadata":{"unresolvedName":"setSelectedRepo"}}],["symbol-name:setSelectedToolType",{"id":"symbol-name:setSelectedToolType","kind":"symbol","language":"jsts","symbolName":"setSelectedToolType","metadata":{"unresolvedName":"setSelectedToolType"}}],["symbol-name:setDisplayName",{"id":"symbol-name:setDisplayName","kind":"symbol","language":"jsts","symbolName":"setDisplayName","metadata":{"unresolvedName":"setDisplayName"}}],["symbol-name:setCloneMode",{"id":"symbol-name:setCloneMode","kind":"symbol","language":"jsts","symbolName":"setCloneMode","metadata":{"unresolvedName":"setCloneMode"}}],["symbol-name:setIsCreatingNewBranch",{"id":"symbol-name:setIsCreatingNewBranch","kind":"symbol","language":"jsts","symbolName":"setIsCreatingNewBranch","metadata":{"unresolvedName":"setIsCreatingNewBranch"}}],["symbol-name:setNewBranchName",{"id":"symbol-name:setNewBranchName","kind":"symbol","language":"jsts","symbolName":"setNewBranchName","metadata":{"unresolvedName":"setNewBranchName"}}],["symbol-name:setSelectedSshKeyIds",{"id":"symbol-name:setSelectedSshKeyIds","kind":"symbol","language":"jsts","symbolName":"setSelectedSshKeyIds","metadata":{"unresolvedName":"setSelectedSshKeyIds"}}],["symbol-name:onSuccess",{"id":"symbol-name:onSuccess","kind":"symbol","language":"jsts","symbolName":"onSuccess","metadata":{"unresolvedName":"onSuccess"}}],["symbol-name:renderStep",{"id":"symbol-name:renderStep","kind":"symbol","language":"jsts","symbolName":"renderStep","metadata":{"unresolvedName":"renderStep"}}],["external:projects.find",{"id":"external:projects.find","kind":"external","language":"jsts","metadata":{"unresolvedName":"projects.find"}}],["symbol-name:onProjectChange",{"id":"symbol-name:onProjectChange","kind":"symbol","language":"jsts","symbolName":"onProjectChange","metadata":{"unresolvedName":"onProjectChange"}}],["external:projects.map",{"id":"external:projects.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"projects.map"}}],["external:availableRepos.map",{"id":"external:availableRepos.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"availableRepos.map"}}],["external:toolTypes.map",{"id":"external:toolTypes.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"toolTypes.map"}}],["external:configProfiles.map",{"id":"external:configProfiles.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"configProfiles.map"}}],["external:sshKeys.map",{"id":"external:sshKeys.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"sshKeys.map"}}],["external:selectedSshKeyIds.includes",{"id":"external:selectedSshKeyIds.includes","kind":"external","language":"jsts","metadata":{"unresolvedName":"selectedSshKeyIds.includes"}}],["external:prev.filter",{"id":"external:prev.filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"prev.filter"}}],["external:branches.map",{"id":"external:branches.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"branches.map"}}],["external:sshKeys.find",{"id":"external:sshKeys.find","kind":"external","language":"jsts","metadata":{"unresolvedName":"sshKeys.find"}}],["/workspace/apps/web/src/components/create-session-form.tsx:",{"id":"/workspace/apps/web/src/components/create-session-form.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/create-session-form.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":501,"column":23,"cyclomaticComplexity":4,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/components/create-session-form.tsx:loadKeys",{"id":"/workspace/apps/web/src/components/create-session-form.tsx:loadKeys","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/create-session-form.tsx","symbolName":"loadKeys","symbolKind":"function","exported":false,"metadata":{"line":66,"column":22,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/components/create-session-form.tsx:loadProfiles",{"id":"/workspace/apps/web/src/components/create-session-form.tsx:loadProfiles","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/create-session-form.tsx","symbolName":"loadProfiles","symbolKind":"function","exported":false,"metadata":{"line":85,"column":26,"cyclomaticComplexity":3,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/components/create-session-form.tsx:loadBranches",{"id":"/workspace/apps/web/src/components/create-session-form.tsx:loadBranches","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/create-session-form.tsx","symbolName":"loadBranches","symbolKind":"function","exported":false,"metadata":{"line":108,"column":26,"cyclomaticComplexity":3,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database","auth","user-input"]}}],["/workspace/apps/web/src/components/create-session-form.tsx:handleSubmit",{"id":"/workspace/apps/web/src/components/create-session-form.tsx:handleSubmit","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/create-session-form.tsx","symbolName":"handleSubmit","symbolKind":"function","exported":false,"metadata":{"line":131,"column":24,"cyclomaticComplexity":24,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/components/create-session-form.tsx:renderStep",{"id":"/workspace/apps/web/src/components/create-session-form.tsx:renderStep","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/create-session-form.tsx","symbolName":"renderStep","symbolKind":"function","exported":false,"metadata":{"line":211,"column":22,"cyclomaticComplexity":3,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["file:/workspace/apps/web/src/components/data-states.tsx",{"id":"file:/workspace/apps/web/src/components/data-states.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/data-states.tsx","metadata":{"lineCount":35,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx",{"id":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx","metadata":{"lineCount":221,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/event-toast-bridge.tsx",{"id":"file:/workspace/apps/web/src/components/event-toast-bridge.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/event-toast-bridge.tsx","metadata":{"lineCount":78,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/state/events.tsx",{"id":"file:/workspace/apps/web/src/state/events.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/state/events.tsx","metadata":{"lineCount":31,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/toast-rules.ts",{"id":"file:/workspace/apps/web/src/components/toast-rules.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/toast-rules.ts","metadata":{"lineCount":84,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/types/events.ts",{"id":"file:/workspace/apps/web/src/types/events.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/types/events.ts","metadata":{"lineCount":18,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/event-toast-bridge.test.tsx:",{"id":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":209,"column":75,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:importOriginal",{"id":"symbol-name:importOriginal","kind":"symbol","language":"jsts","symbolName":"importOriginal","metadata":{"unresolvedName":"importOriginal"}}],["external:vi.fn",{"id":"external:vi.fn","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.fn"}}],["/workspace/apps/web/src/components/event-toast-bridge.test.tsx:makeEvent",{"id":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:makeEvent","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx","symbolName":"makeEvent","symbolKind":"function","exported":false,"metadata":{"line":30,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/event-toast-bridge.test.tsx:flushPromises",{"id":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:flushPromises","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx","symbolName":"flushPromises","symbolKind":"function","exported":false,"metadata":{"line":46,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:act",{"id":"symbol-name:act","kind":"symbol","language":"jsts","symbolName":"act","metadata":{"unresolvedName":"act"}}],["external:Promise.resolve",{"id":"external:Promise.resolve","kind":"external","language":"jsts","metadata":{"unresolvedName":"Promise.resolve"}}],["symbol-name:beforeEach",{"id":"symbol-name:beforeEach","kind":"symbol","language":"jsts","symbolName":"beforeEach","metadata":{"unresolvedName":"beforeEach"}}],["external:vi.clearAllMocks",{"id":"external:vi.clearAllMocks","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.clearAllMocks"}}],["external:mockedUseEventContext.mockReturnValue",{"id":"external:mockedUseEventContext.mockReturnValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockedUseEventContext.mockReturnValue"}}],["external:mockedGetUserConfig.mockResolvedValue",{"id":"external:mockedGetUserConfig.mockResolvedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockedGetUserConfig.mockResolvedValue"}}],["symbol-name:afterEach",{"id":"symbol-name:afterEach","kind":"symbol","language":"jsts","symbolName":"afterEach","metadata":{"unresolvedName":"afterEach"}}],["external:vi.restoreAllMocks",{"id":"external:vi.restoreAllMocks","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.restoreAllMocks"}}],["symbol-name:makeEvent",{"id":"symbol-name:makeEvent","kind":"symbol","language":"jsts","symbolName":"makeEvent","metadata":{"unresolvedName":"makeEvent"}}],["symbol-name:flushPromises",{"id":"symbol-name:flushPromises","kind":"symbol","language":"jsts","symbolName":"flushPromises","metadata":{"unresolvedName":"flushPromises"}}],["external:expect(mockedHandleEventToast).toHaveBeenCalledWith",{"id":"external:expect(mockedHandleEventToast).toHaveBeenCalledWith","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(mockedHandleEventToast).toHaveBeenCalledWith"}}],["external:expect(mockedHandleEventToast).not.toHaveBeenCalled",{"id":"external:expect(mockedHandleEventToast).not.toHaveBeenCalled","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(mockedHandleEventToast).not.toHaveBeenCalled"}}],["external:expect(mockedHandleEventToast).toHaveBeenCalledTimes",{"id":"external:expect(mockedHandleEventToast).toHaveBeenCalledTimes","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(mockedHandleEventToast).toHaveBeenCalledTimes"}}],["external:window.dispatchEvent",{"id":"external:window.dispatchEvent","kind":"external","language":"jsts","metadata":{"unresolvedName":"window.dispatchEvent"}}],["symbol-name:rerender",{"id":"symbol-name:rerender","kind":"symbol","language":"jsts","symbolName":"rerender","metadata":{"unresolvedName":"rerender"}}],["/workspace/apps/web/src/components/event-toast-bridge.tsx:EventToastBridge",{"id":"/workspace/apps/web/src/components/event-toast-bridge.tsx:EventToastBridge","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/event-toast-bridge.tsx","symbolName":"EventToastBridge","symbolKind":"function","exported":true,"metadata":{"line":16,"column":1,"cyclomaticComplexity":15,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:useEventContext",{"id":"symbol-name:useEventContext","kind":"symbol","language":"jsts","symbolName":"useEventContext","metadata":{"unresolvedName":"useEventContext"}}],["symbol-name:useRef",{"id":"symbol-name:useRef","kind":"symbol","language":"jsts","symbolName":"useRef","metadata":{"unresolvedName":"useRef"}}],["external:getUserConfig()\n\t\t\t.then",{"id":"external:getUserConfig()\n\t\t\t.then","kind":"external","language":"jsts","metadata":{"unresolvedName":"getUserConfig()\n\t\t\t.then"}}],["symbol-name:getUserConfig",{"id":"symbol-name:getUserConfig","kind":"symbol","language":"jsts","symbolName":"getUserConfig","metadata":{"unresolvedName":"getUserConfig"}}],["symbol-name:setConfig",{"id":"symbol-name:setConfig","kind":"symbol","language":"jsts","symbolName":"setConfig","metadata":{"unresolvedName":"setConfig"}}],["external:window.addEventListener",{"id":"external:window.addEventListener","kind":"external","language":"jsts","metadata":{"unresolvedName":"window.addEventListener"}}],["external:window.removeEventListener",{"id":"external:window.removeEventListener","kind":"external","language":"jsts","metadata":{"unresolvedName":"window.removeEventListener"}}],["external:processedRef.current.has",{"id":"external:processedRef.current.has","kind":"external","language":"jsts","metadata":{"unresolvedName":"processedRef.current.has"}}],["external:processedRef.current.add",{"id":"external:processedRef.current.add","kind":"external","language":"jsts","metadata":{"unresolvedName":"processedRef.current.add"}}],["symbol-name:mapEventToCategory",{"id":"symbol-name:mapEventToCategory","kind":"symbol","language":"jsts","symbolName":"mapEventToCategory","metadata":{"unresolvedName":"mapEventToCategory"}}],["symbol-name:mapEventToSeverity",{"id":"symbol-name:mapEventToSeverity","kind":"symbol","language":"jsts","symbolName":"mapEventToSeverity","metadata":{"unresolvedName":"mapEventToSeverity"}}],["external:config.notification_mute_categories.includes",{"id":"external:config.notification_mute_categories.includes","kind":"external","language":"jsts","metadata":{"unresolvedName":"config.notification_mute_categories.includes"}}],["symbol-name:handleEventToast",{"id":"symbol-name:handleEventToast","kind":"symbol","language":"jsts","symbolName":"handleEventToast","metadata":{"unresolvedName":"handleEventToast"}}],["/workspace/apps/web/src/components/event-toast-bridge.tsx:",{"id":"/workspace/apps/web/src/components/event-toast-bridge.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/event-toast-bridge.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":56,"column":12,"cyclomaticComplexity":8,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/event-toast-bridge.tsx:handler",{"id":"/workspace/apps/web/src/components/event-toast-bridge.tsx:handler","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/event-toast-bridge.tsx","symbolName":"handler","symbolKind":"function","exported":false,"metadata":{"line":36,"column":19,"cyclomaticComplexity":6,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/features/dashboard/ActiveSessionsList.tsx",{"id":"file:/workspace/apps/web/src/components/features/dashboard/ActiveSessionsList.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/dashboard/ActiveSessionsList.tsx","metadata":{"lineCount":87,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["file:/workspace/apps/web/src/components/ui/Icon.tsx",{"id":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/ui/Icon.tsx","metadata":{"lineCount":174,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/dashboard/ActiveSessionsList.tsx:ActiveSessionsList",{"id":"/workspace/apps/web/src/components/features/dashboard/ActiveSessionsList.tsx:ActiveSessionsList","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/dashboard/ActiveSessionsList.tsx","symbolName":"ActiveSessionsList","symbolKind":"function","exported":true,"metadata":{"line":13,"column":35,"cyclomaticComplexity":4,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["external:sessions.map",{"id":"external:sessions.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"sessions.map"}}],["symbol-name:onOpen",{"id":"symbol-name:onOpen","kind":"symbol","language":"jsts","symbolName":"onOpen","metadata":{"unresolvedName":"onOpen"}}],["symbol-name:onRecreateTunnel",{"id":"symbol-name:onRecreateTunnel","kind":"symbol","language":"jsts","symbolName":"onRecreateTunnel","metadata":{"unresolvedName":"onRecreateTunnel"}}],["symbol-name:onStop",{"id":"symbol-name:onStop","kind":"symbol","language":"jsts","symbolName":"onStop","metadata":{"unresolvedName":"onStop"}}],["symbol-name:onDelete",{"id":"symbol-name:onDelete","kind":"symbol","language":"jsts","symbolName":"onDelete","metadata":{"unresolvedName":"onDelete"}}],["file:/workspace/apps/web/src/components/features/dashboard/DashboardSummary.tsx",{"id":"file:/workspace/apps/web/src/components/features/dashboard/DashboardSummary.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/dashboard/DashboardSummary.tsx","metadata":{"lineCount":35,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/dashboard/DashboardSummary.tsx:DashboardSummary",{"id":"/workspace/apps/web/src/components/features/dashboard/DashboardSummary.tsx:DashboardSummary","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/dashboard/DashboardSummary.tsx","symbolName":"DashboardSummary","symbolKind":"function","exported":true,"metadata":{"line":14,"column":33,"cyclomaticComplexity":3,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:summaryCards.map",{"id":"external:summaryCards.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"summaryCards.map"}}],["file:/workspace/apps/web/src/components/features/dashboard/ProjectsSection.tsx",{"id":"file:/workspace/apps/web/src/components/features/dashboard/ProjectsSection.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/dashboard/ProjectsSection.tsx","metadata":{"lineCount":41,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/dashboard/ProjectsSection.tsx:ProjectsSection",{"id":"/workspace/apps/web/src/components/features/dashboard/ProjectsSection.tsx:ProjectsSection","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/dashboard/ProjectsSection.tsx","symbolName":"ProjectsSection","symbolKind":"function","exported":true,"metadata":{"line":8,"column":32,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:onOpenProject",{"id":"symbol-name:onOpenProject","kind":"symbol","language":"jsts","symbolName":"onOpenProject","metadata":{"unresolvedName":"onOpenProject"}}],["file:/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx",{"id":"file:/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx","metadata":{"lineCount":130,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:QuickCreateForm",{"id":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:QuickCreateForm","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx","symbolName":"QuickCreateForm","symbolKind":"function","exported":true,"metadata":{"line":21,"column":32,"cyclomaticComplexity":6,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:onSubmit",{"id":"symbol-name:onSubmit","kind":"symbol","language":"jsts","symbolName":"onSubmit","metadata":{"unresolvedName":"onSubmit"}}],["symbol-name:handleProjectChange",{"id":"symbol-name:handleProjectChange","kind":"symbol","language":"jsts","symbolName":"handleProjectChange","metadata":{"unresolvedName":"handleProjectChange"}}],["external:repositories.map",{"id":"external:repositories.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"repositories.map"}}],["/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:handleProjectChange",{"id":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:handleProjectChange","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx","symbolName":"handleProjectChange","symbolKind":"function","exported":false,"metadata":{"line":34,"column":30,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:handleSubmit",{"id":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:handleSubmit","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx","symbolName":"handleSubmit","symbolKind":"function","exported":false,"metadata":{"line":40,"column":23,"cyclomaticComplexity":4,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/features/dashboard/RecentSessionsSection.tsx",{"id":"file:/workspace/apps/web/src/components/features/dashboard/RecentSessionsSection.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/dashboard/RecentSessionsSection.tsx","metadata":{"lineCount":48,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/components/features/dashboard/RecentSessionsSection.tsx:RecentSessionsSection",{"id":"/workspace/apps/web/src/components/features/dashboard/RecentSessionsSection.tsx:RecentSessionsSection","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/dashboard/RecentSessionsSection.tsx","symbolName":"RecentSessionsSection","symbolKind":"function","exported":true,"metadata":{"line":8,"column":38,"cyclomaticComplexity":4,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["file:/workspace/apps/web/src/components/features/dashboard/index.ts",{"id":"file:/workspace/apps/web/src/components/features/dashboard/index.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/dashboard/index.ts","metadata":{"lineCount":6,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/features/git/CommitDialog.tsx",{"id":"file:/workspace/apps/web/src/components/features/git/CommitDialog.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx","metadata":{"lineCount":161,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/features/git/CommitDialog.module.css",{"id":"file:/workspace/apps/web/src/components/features/git/CommitDialog.module.css","kind":"file","language":"css","filePath":"/workspace/apps/web/src/components/features/git/CommitDialog.module.css"}],["/workspace/apps/web/src/components/features/git/CommitDialog.tsx:CommitDialog",{"id":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:CommitDialog","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx","symbolName":"CommitDialog","symbolKind":"function","exported":true,"metadata":{"line":15,"column":58,"cyclomaticComplexity":21,"maxNestingDepth":3,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:originalContent.split",{"id":"external:originalContent.split","kind":"external","language":"jsts","metadata":{"unresolvedName":"originalContent.split"}}],["external:newContent.split",{"id":"external:newContent.split","kind":"external","language":"jsts","metadata":{"unresolvedName":"newContent.split"}}],["external:Math.max",{"id":"external:Math.max","kind":"external","language":"jsts","metadata":{"unresolvedName":"Math.max"}}],["external:diff.push",{"id":"external:diff.push","kind":"external","language":"jsts","metadata":{"unresolvedName":"diff.push"}}],["external:message.trim",{"id":"external:message.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"message.trim"}}],["symbol-name:setLoading",{"id":"symbol-name:setLoading","kind":"symbol","language":"jsts","symbolName":"setLoading","metadata":{"unresolvedName":"setLoading"}}],["symbol-name:onCommit",{"id":"symbol-name:onCommit","kind":"symbol","language":"jsts","symbolName":"onCommit","metadata":{"unresolvedName":"onCommit"}}],["symbol-name:generateDiff",{"id":"symbol-name:generateDiff","kind":"symbol","language":"jsts","symbolName":"generateDiff","metadata":{"unresolvedName":"generateDiff"}}],["external:diff.some",{"id":"external:diff.some","kind":"external","language":"jsts","metadata":{"unresolvedName":"diff.some"}}],["external:diff.map",{"id":"external:diff.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"diff.map"}}],["symbol-name:setMessage",{"id":"symbol-name:setMessage","kind":"symbol","language":"jsts","symbolName":"setMessage","metadata":{"unresolvedName":"setMessage"}}],["/workspace/apps/web/src/components/features/git/CommitDialog.tsx:generateDiff",{"id":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:generateDiff","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx","symbolName":"generateDiff","symbolKind":"function","exported":false,"metadata":{"line":29,"column":24,"cyclomaticComplexity":7,"maxNestingDepth":3,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/git/CommitDialog.tsx:handleCommit",{"id":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:handleCommit","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx","symbolName":"handleCommit","symbolKind":"function","exported":false,"metadata":{"line":54,"column":24,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/features/git/CommitPanel.tsx",{"id":"file:/workspace/apps/web/src/components/features/git/CommitPanel.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/CommitPanel.tsx","metadata":{"lineCount":104,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/features/git/CommitPanel.module.css",{"id":"file:/workspace/apps/web/src/components/features/git/CommitPanel.module.css","kind":"file","language":"css","filePath":"/workspace/apps/web/src/components/features/git/CommitPanel.module.css"}],["/workspace/apps/web/src/components/features/git/CommitPanel.tsx:CommitPanel",{"id":"/workspace/apps/web/src/components/features/git/CommitPanel.tsx:CommitPanel","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/CommitPanel.tsx","symbolName":"CommitPanel","symbolKind":"function","exported":true,"metadata":{"line":16,"column":28,"cyclomaticComplexity":7,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:commitChanges",{"id":"symbol-name:commitChanges","kind":"symbol","language":"jsts","symbolName":"commitChanges","metadata":{"unresolvedName":"commitChanges"}}],["external:modified.map",{"id":"external:modified.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"modified.map"}}],["external:added.map",{"id":"external:added.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"added.map"}}],["external:deleted.map",{"id":"external:deleted.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"deleted.map"}}],["external:untracked.map",{"id":"external:untracked.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"untracked.map"}}],["/workspace/apps/web/src/components/features/git/CommitPanel.tsx:handleCommit",{"id":"/workspace/apps/web/src/components/features/git/CommitPanel.tsx:handleCommit","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/CommitPanel.tsx","symbolName":"handleCommit","symbolKind":"function","exported":false,"metadata":{"line":32,"column":23,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx",{"id":"file:/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx","metadata":{"lineCount":63,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/features/git/FileBrowser.tsx",{"id":"file:/workspace/apps/web/src/components/features/git/FileBrowser.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx","metadata":{"lineCount":151,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:",{"id":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":58,"column":17,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:expect(screen.getByText(/loading files/i)).toBeInTheDocument",{"id":"external:expect(screen.getByText(/loading files/i)).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(/loading files/i)).toBeInTheDocument"}}],["external:mockedGet.mockResolvedValueOnce",{"id":"external:mockedGet.mockResolvedValueOnce","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockedGet.mockResolvedValueOnce"}}],["symbol-name:waitFor",{"id":"symbol-name:waitFor","kind":"symbol","language":"jsts","symbolName":"waitFor","metadata":{"unresolvedName":"waitFor"}}],["external:expect(screen.getByText(\"src\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"src\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"src\")).toBeInTheDocument"}}],["external:expect(screen.getByText(\"README.md\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"README.md\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"README.md\")).toBeInTheDocument"}}],["external:mockedGet.mockRejectedValueOnce",{"id":"external:mockedGet.mockRejectedValueOnce","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockedGet.mockRejectedValueOnce"}}],["external:expect(screen.getByText(/failed to load files/i)).toBeInTheDocument",{"id":"external:expect(screen.getByText(/failed to load files/i)).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(/failed to load files/i)).toBeInTheDocument"}}],["/workspace/apps/web/src/components/features/git/FileBrowser.tsx:FileBrowser",{"id":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:FileBrowser","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx","symbolName":"FileBrowser","symbolKind":"function","exported":true,"metadata":{"line":27,"column":56,"cyclomaticComplexity":26,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:useSearchParams",{"id":"symbol-name:useSearchParams","kind":"symbol","language":"jsts","symbolName":"useSearchParams","metadata":{"unresolvedName":"useSearchParams"}}],["external:searchParams.get",{"id":"external:searchParams.get","kind":"external","language":"jsts","metadata":{"unresolvedName":"searchParams.get"}}],["symbol-name:useCallback",{"id":"symbol-name:useCallback","kind":"symbol","language":"jsts","symbolName":"useCallback","metadata":{"unresolvedName":"useCallback"}}],["symbol-name:setEntries",{"id":"symbol-name:setEntries","kind":"symbol","language":"jsts","symbolName":"setEntries","metadata":{"unresolvedName":"setEntries"}}],["symbol-name:loadFiles",{"id":"symbol-name:loadFiles","kind":"symbol","language":"jsts","symbolName":"loadFiles","metadata":{"unresolvedName":"loadFiles"}}],["external:newParams.set",{"id":"external:newParams.set","kind":"external","language":"jsts","metadata":{"unresolvedName":"newParams.set"}}],["symbol-name:setSearchParams",{"id":"symbol-name:setSearchParams","kind":"symbol","language":"jsts","symbolName":"setSearchParams","metadata":{"unresolvedName":"setSearchParams"}}],["external:path.split(\"/\").slice(0, -1).join",{"id":"external:path.split(\"/\").slice(0, -1).join","kind":"external","language":"jsts","metadata":{"unresolvedName":"path.split(\"/\").slice(0, -1).join"}}],["external:path.split(\"/\").slice",{"id":"external:path.split(\"/\").slice","kind":"external","language":"jsts","metadata":{"unresolvedName":"path.split(\"/\").slice"}}],["external:path.split",{"id":"external:path.split","kind":"external","language":"jsts","metadata":{"unresolvedName":"path.split"}}],["external:newParams.delete",{"id":"external:newParams.delete","kind":"external","language":"jsts","metadata":{"unresolvedName":"newParams.delete"}}],["external:gitStatus.modified.includes",{"id":"external:gitStatus.modified.includes","kind":"external","language":"jsts","metadata":{"unresolvedName":"gitStatus.modified.includes"}}],["external:gitStatus.added.includes",{"id":"external:gitStatus.added.includes","kind":"external","language":"jsts","metadata":{"unresolvedName":"gitStatus.added.includes"}}],["external:gitStatus.deleted.includes",{"id":"external:gitStatus.deleted.includes","kind":"external","language":"jsts","metadata":{"unresolvedName":"gitStatus.deleted.includes"}}],["external:gitStatus.untracked.includes",{"id":"external:gitStatus.untracked.includes","kind":"external","language":"jsts","metadata":{"unresolvedName":"gitStatus.untracked.includes"}}],["external:entries.map",{"id":"external:entries.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"entries.map"}}],["symbol-name:getFileStatus",{"id":"symbol-name:getFileStatus","kind":"symbol","language":"jsts","symbolName":"getFileStatus","metadata":{"unresolvedName":"getFileStatus"}}],["symbol-name:handleEntryClick",{"id":"symbol-name:handleEntryClick","kind":"symbol","language":"jsts","symbolName":"handleEntryClick","metadata":{"unresolvedName":"handleEntryClick"}}],["/workspace/apps/web/src/components/features/git/FileBrowser.tsx:",{"id":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":122,"column":17,"cyclomaticComplexity":10,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/git/FileBrowser.tsx:handleEntryClick",{"id":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:handleEntryClick","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx","symbolName":"handleEntryClick","symbolKind":"function","exported":false,"metadata":{"line":72,"column":27,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/git/FileBrowser.tsx:navigateUp",{"id":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:navigateUp","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx","symbolName":"navigateUp","symbolKind":"function","exported":false,"metadata":{"line":84,"column":21,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/git/FileBrowser.tsx:getFileStatus",{"id":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:getFileStatus","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx","symbolName":"getFileStatus","symbolKind":"function","exported":false,"metadata":{"line":96,"column":24,"cyclomaticComplexity":6,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/features/git/FileEditor.tsx",{"id":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/FileEditor.tsx","metadata":{"lineCount":306,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/features/git/FileEditor.module.css",{"id":"file:/workspace/apps/web/src/components/features/git/FileEditor.module.css","kind":"file","language":"css","filePath":"/workspace/apps/web/src/components/features/git/FileEditor.module.css"}],["file:/workspace/apps/web/src/components/ui/CodeEditor.tsx",{"id":"file:/workspace/apps/web/src/components/ui/CodeEditor.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/ui/CodeEditor.tsx","metadata":{"lineCount":57,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx",{"id":"file:/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx","metadata":{"lineCount":78,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/utils/language.ts",{"id":"file:/workspace/apps/web/src/utils/language.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/utils/language.ts","metadata":{"lineCount":91,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor",{"id":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/FileEditor.tsx","symbolName":"FileEditor","symbolKind":"function","exported":true,"metadata":{"line":25,"column":54,"cyclomaticComplexity":49,"maxNestingDepth":3,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:setIsBinary",{"id":"symbol-name:setIsBinary","kind":"symbol","language":"jsts","symbolName":"setIsBinary","metadata":{"unresolvedName":"setIsBinary"}}],["symbol-name:setContent",{"id":"symbol-name:setContent","kind":"symbol","language":"jsts","symbolName":"setContent","metadata":{"unresolvedName":"setContent"}}],["symbol-name:setOriginalContent",{"id":"symbol-name:setOriginalContent","kind":"symbol","language":"jsts","symbolName":"setOriginalContent","metadata":{"unresolvedName":"setOriginalContent"}}],["symbol-name:setMode",{"id":"symbol-name:setMode","kind":"symbol","language":"jsts","symbolName":"setMode","metadata":{"unresolvedName":"setMode"}}],["symbol-name:setLanguage",{"id":"symbol-name:setLanguage","kind":"symbol","language":"jsts","symbolName":"setLanguage","metadata":{"unresolvedName":"setLanguage"}}],["symbol-name:detectLanguage",{"id":"symbol-name:detectLanguage","kind":"symbol","language":"jsts","symbolName":"detectLanguage","metadata":{"unresolvedName":"detectLanguage"}}],["symbol-name:loadFile",{"id":"symbol-name:loadFile","kind":"symbol","language":"jsts","symbolName":"loadFile","metadata":{"unresolvedName":"loadFile"}}],["symbol-name:setShowCommitDialog",{"id":"symbol-name:setShowCommitDialog","kind":"symbol","language":"jsts","symbolName":"setShowCommitDialog","metadata":{"unresolvedName":"setShowCommitDialog"}}],["symbol-name:setSaving",{"id":"symbol-name:setSaving","kind":"symbol","language":"jsts","symbolName":"setSaving","metadata":{"unresolvedName":"setSaving"}}],["external:e.preventDefault",{"id":"external:e.preventDefault","kind":"external","language":"jsts","metadata":{"unresolvedName":"e.preventDefault"}}],["symbol-name:handleEdit",{"id":"symbol-name:handleEdit","kind":"symbol","language":"jsts","symbolName":"handleEdit","metadata":{"unresolvedName":"handleEdit"}}],["symbol-name:handleCancel",{"id":"symbol-name:handleCancel","kind":"symbol","language":"jsts","symbolName":"handleCancel","metadata":{"unresolvedName":"handleCancel"}}],["symbol-name:handleSave",{"id":"symbol-name:handleSave","kind":"symbol","language":"jsts","symbolName":"handleSave","metadata":{"unresolvedName":"handleSave"}}],["external:filePath?.split(\"/\").map",{"id":"external:filePath?.split(\"/\").map","kind":"external","language":"jsts","metadata":{"unresolvedName":"filePath?.split(\"/\").map"}}],["external:filePath?.split",{"id":"external:filePath?.split","kind":"external","language":"jsts","metadata":{"unresolvedName":"filePath?.split"}}],["/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleDiscard",{"id":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleDiscard","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/FileEditor.tsx","symbolName":"handleDiscard","symbolKind":"function","exported":false,"metadata":{"line":43,"column":25,"cyclomaticComplexity":4,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/git/FileEditor.tsx:",{"id":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/FileEditor.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":173,"column":13,"cyclomaticComplexity":11,"maxNestingDepth":3,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleEdit",{"id":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleEdit","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/FileEditor.tsx","symbolName":"handleEdit","symbolKind":"function","exported":false,"metadata":{"line":127,"column":22,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleCancel",{"id":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleCancel","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/FileEditor.tsx","symbolName":"handleCancel","symbolKind":"function","exported":false,"metadata":{"line":132,"column":24,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleSave",{"id":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleSave","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/FileEditor.tsx","symbolName":"handleSave","symbolKind":"function","exported":false,"metadata":{"line":138,"column":22,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleCommit",{"id":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleCommit","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/FileEditor.tsx","symbolName":"handleCommit","symbolKind":"function","exported":false,"metadata":{"line":146,"column":24,"cyclomaticComplexity":6,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleKeyDown",{"id":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleKeyDown","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/FileEditor.tsx","symbolName":"handleKeyDown","symbolKind":"function","exported":false,"metadata":{"line":174,"column":27,"cyclomaticComplexity":11,"maxNestingDepth":3,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/features/git/GitToolbar.tsx",{"id":"file:/workspace/apps/web/src/components/features/git/GitToolbar.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx","metadata":{"lineCount":270,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/features/git/MergeDialog.tsx",{"id":"file:/workspace/apps/web/src/components/features/git/MergeDialog.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx","metadata":{"lineCount":146,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/features/git/GitToolbar.module.css",{"id":"file:/workspace/apps/web/src/components/features/git/GitToolbar.module.css","kind":"file","language":"css","filePath":"/workspace/apps/web/src/components/features/git/GitToolbar.module.css"}],["/workspace/apps/web/src/components/features/git/GitToolbar.tsx:GitToolbar",{"id":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:GitToolbar","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx","symbolName":"GitToolbar","symbolKind":"function","exported":true,"metadata":{"line":26,"column":27,"cyclomaticComplexity":33,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:getRepositoryStatus",{"id":"symbol-name:getRepositoryStatus","kind":"symbol","language":"jsts","symbolName":"getRepositoryStatus","metadata":{"unresolvedName":"getRepositoryStatus"}}],["symbol-name:loadStatus",{"id":"symbol-name:loadStatus","kind":"symbol","language":"jsts","symbolName":"loadStatus","metadata":{"unresolvedName":"loadStatus"}}],["symbol-name:setInterval",{"id":"symbol-name:setInterval","kind":"symbol","language":"jsts","symbolName":"setInterval","metadata":{"unresolvedName":"setInterval"}}],["symbol-name:clearInterval",{"id":"symbol-name:clearInterval","kind":"symbol","language":"jsts","symbolName":"clearInterval","metadata":{"unresolvedName":"clearInterval"}}],["symbol-name:fetchRepository",{"id":"symbol-name:fetchRepository","kind":"symbol","language":"jsts","symbolName":"fetchRepository","metadata":{"unresolvedName":"fetchRepository"}}],["symbol-name:pullRepository",{"id":"symbol-name:pullRepository","kind":"symbol","language":"jsts","symbolName":"pullRepository","metadata":{"unresolvedName":"pullRepository"}}],["symbol-name:onRefresh",{"id":"symbol-name:onRefresh","kind":"symbol","language":"jsts","symbolName":"onRefresh","metadata":{"unresolvedName":"onRefresh"}}],["symbol-name:pushRepository",{"id":"symbol-name:pushRepository","kind":"symbol","language":"jsts","symbolName":"pushRepository","metadata":{"unresolvedName":"pushRepository"}}],["symbol-name:checkoutBranch",{"id":"symbol-name:checkoutBranch","kind":"symbol","language":"jsts","symbolName":"checkoutBranch","metadata":{"unresolvedName":"checkoutBranch"}}],["symbol-name:onBranchChange",{"id":"symbol-name:onBranchChange","kind":"symbol","language":"jsts","symbolName":"onBranchChange","metadata":{"unresolvedName":"onBranchChange"}}],["external:newBranchName.trim",{"id":"external:newBranchName.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"newBranchName.trim"}}],["symbol-name:createBranch",{"id":"symbol-name:createBranch","kind":"symbol","language":"jsts","symbolName":"createBranch","metadata":{"unresolvedName":"createBranch"}}],["symbol-name:setShowNewBranch",{"id":"symbol-name:setShowNewBranch","kind":"symbol","language":"jsts","symbolName":"setShowNewBranch","metadata":{"unresolvedName":"setShowNewBranch"}}],["symbol-name:setNewBranchBase",{"id":"symbol-name:setNewBranchBase","kind":"symbol","language":"jsts","symbolName":"setNewBranchBase","metadata":{"unresolvedName":"setNewBranchBase"}}],["symbol-name:handleCheckout",{"id":"symbol-name:handleCheckout","kind":"symbol","language":"jsts","symbolName":"handleCheckout","metadata":{"unresolvedName":"handleCheckout"}}],["symbol-name:setShowMergeDialog",{"id":"symbol-name:setShowMergeDialog","kind":"symbol","language":"jsts","symbolName":"setShowMergeDialog","metadata":{"unresolvedName":"setShowMergeDialog"}}],["/workspace/apps/web/src/components/features/git/GitToolbar.tsx:",{"id":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":262,"column":18,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handleFetch",{"id":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handleFetch","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx","symbolName":"handleFetch","symbolKind":"function","exported":false,"metadata":{"line":60,"column":23,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handlePull",{"id":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handlePull","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx","symbolName":"handlePull","symbolKind":"function","exported":false,"metadata":{"line":73,"column":22,"cyclomaticComplexity":4,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handlePush",{"id":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handlePush","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx","symbolName":"handlePush","symbolKind":"function","exported":false,"metadata":{"line":87,"column":22,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handleCheckout",{"id":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handleCheckout","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx","symbolName":"handleCheckout","symbolKind":"function","exported":false,"metadata":{"line":99,"column":26,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handleCreateBranch",{"id":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handleCreateBranch","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx","symbolName":"handleCreateBranch","symbolKind":"function","exported":false,"metadata":{"line":112,"column":30,"cyclomaticComplexity":5,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/features/git/MergeDialog.module.css",{"id":"file:/workspace/apps/web/src/components/features/git/MergeDialog.module.css","kind":"file","language":"css","filePath":"/workspace/apps/web/src/components/features/git/MergeDialog.module.css"}],["/workspace/apps/web/src/components/features/git/MergeDialog.tsx:MergeDialog",{"id":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:MergeDialog","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx","symbolName":"MergeDialog","symbolKind":"function","exported":true,"metadata":{"line":17,"column":28,"cyclomaticComplexity":10,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:branches.filter",{"id":"external:branches.filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"branches.filter"}}],["symbol-name:setSuccess",{"id":"symbol-name:setSuccess","kind":"symbol","language":"jsts","symbolName":"setSuccess","metadata":{"unresolvedName":"setSuccess"}}],["symbol-name:mergeBranches",{"id":"symbol-name:mergeBranches","kind":"symbol","language":"jsts","symbolName":"mergeBranches","metadata":{"unresolvedName":"mergeBranches"}}],["symbol-name:onMerge",{"id":"symbol-name:onMerge","kind":"symbol","language":"jsts","symbolName":"onMerge","metadata":{"unresolvedName":"onMerge"}}],["symbol-name:onClose",{"id":"symbol-name:onClose","kind":"symbol","language":"jsts","symbolName":"onClose","metadata":{"unresolvedName":"onClose"}}],["external:e.stopPropagation",{"id":"external:e.stopPropagation","kind":"external","language":"jsts","metadata":{"unresolvedName":"e.stopPropagation"}}],["symbol-name:setSourceBranch",{"id":"symbol-name:setSourceBranch","kind":"symbol","language":"jsts","symbolName":"setSourceBranch","metadata":{"unresolvedName":"setSourceBranch"}}],["external:availableBranches.map",{"id":"external:availableBranches.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"availableBranches.map"}}],["symbol-name:setCommitMessage",{"id":"symbol-name:setCommitMessage","kind":"symbol","language":"jsts","symbolName":"setCommitMessage","metadata":{"unresolvedName":"setCommitMessage"}}],["/workspace/apps/web/src/components/features/git/MergeDialog.tsx:handleMerge",{"id":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:handleMerge","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx","symbolName":"handleMerge","symbolKind":"function","exported":false,"metadata":{"line":36,"column":23,"cyclomaticComplexity":4,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/git/MergeDialog.tsx:",{"id":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":53,"column":18,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:SyntaxHighlighter",{"id":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:SyntaxHighlighter","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx","symbolName":"SyntaxHighlighter","symbolKind":"function","exported":true,"metadata":{"line":12,"column":68,"cyclomaticComplexity":3,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:loadLanguage",{"id":"symbol-name:loadLanguage","kind":"symbol","language":"jsts","symbolName":"loadLanguage","metadata":{"unresolvedName":"loadLanguage"}}],["symbol-name:setHighlighted",{"id":"symbol-name:setHighlighted","kind":"symbol","language":"jsts","symbolName":"setHighlighted","metadata":{"unresolvedName":"setHighlighted"}}],["symbol-name:highlightCode",{"id":"symbol-name:highlightCode","kind":"symbol","language":"jsts","symbolName":"highlightCode","metadata":{"unresolvedName":"highlightCode"}}],["symbol-name:highlight",{"id":"symbol-name:highlight","kind":"symbol","language":"jsts","symbolName":"highlight","metadata":{"unresolvedName":"highlight"}}],["external:navigator.clipboard.writeText",{"id":"external:navigator.clipboard.writeText","kind":"external","language":"jsts","metadata":{"unresolvedName":"navigator.clipboard.writeText"}}],["symbol-name:setCopied",{"id":"symbol-name:setCopied","kind":"symbol","language":"jsts","symbolName":"setCopied","metadata":{"unresolvedName":"setCopied"}}],["external:code.split",{"id":"external:code.split","kind":"external","language":"jsts","metadata":{"unresolvedName":"code.split"}}],["external:lines.map",{"id":"external:lines.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"lines.map"}}],["/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:",{"id":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":20,"column":13,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:highlight",{"id":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:highlight","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx","symbolName":"highlight","symbolKind":"function","exported":false,"metadata":{"line":21,"column":23,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:handleCopy",{"id":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:handleCopy","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx","symbolName":"handleCopy","symbolKind":"function","exported":false,"metadata":{"line":28,"column":22,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/features/git/WorkspaceSidebar.tsx",{"id":"file:/workspace/apps/web/src/components/features/git/WorkspaceSidebar.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/WorkspaceSidebar.tsx","metadata":{"lineCount":65,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/features/session/InstanceList.tsx",{"id":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/session/InstanceList.tsx","metadata":{"lineCount":393,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/components/features/git/WorkspaceSidebar.tsx:WorkspaceSidebar",{"id":"/workspace/apps/web/src/components/features/git/WorkspaceSidebar.tsx:WorkspaceSidebar","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/WorkspaceSidebar.tsx","symbolName":"WorkspaceSidebar","symbolKind":"function","exported":true,"metadata":{"line":18,"column":33,"cyclomaticComplexity":2,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:onRepoChange",{"id":"symbol-name:onRepoChange","kind":"symbol","language":"jsts","symbolName":"onRepoChange","metadata":{"unresolvedName":"onRepoChange"}}],["file:/workspace/apps/web/src/components/features/git/index.ts",{"id":"file:/workspace/apps/web/src/components/features/git/index.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git/index.ts","metadata":{"lineCount":3,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx",{"id":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx","metadata":{"lineCount":115,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx",{"id":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx","metadata":{"lineCount":102,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:",{"id":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":99,"column":19,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["external:vi.importActual",{"id":"external:vi.importActual","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.importActual"}}],["symbol-name:cleanup",{"id":"symbol-name:cleanup","kind":"symbol","language":"jsts","symbolName":"cleanup","metadata":{"unresolvedName":"cleanup"}}],["external:vi.spyOn(gitRepositoriesApi, \"listRepositories\").mockResolvedValue",{"id":"external:vi.spyOn(gitRepositoriesApi, \"listRepositories\").mockResolvedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.spyOn(gitRepositoriesApi, \"listRepositories\").mockResolvedValue"}}],["external:vi.spyOn",{"id":"external:vi.spyOn","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.spyOn"}}],["external:vi.spyOn(gitRepositoriesApi, \"createRepository\").mockResolvedValue",{"id":"external:vi.spyOn(gitRepositoriesApi, \"createRepository\").mockResolvedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.spyOn(gitRepositoriesApi, \"createRepository\").mockResolvedValue"}}],["external:expect(screen.getByText(\"Main Repo\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"Main Repo\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"Main Repo\")).toBeInTheDocument"}}],["external:fireEvent.click",{"id":"external:fireEvent.click","kind":"external","language":"jsts","metadata":{"unresolvedName":"fireEvent.click"}}],["external:screen.getByRole",{"id":"external:screen.getByRole","kind":"external","language":"jsts","metadata":{"unresolvedName":"screen.getByRole"}}],["external:fireEvent.change",{"id":"external:fireEvent.change","kind":"external","language":"jsts","metadata":{"unresolvedName":"fireEvent.change"}}],["external:screen.getByPlaceholderText",{"id":"external:screen.getByPlaceholderText","kind":"external","language":"jsts","metadata":{"unresolvedName":"screen.getByPlaceholderText"}}],["external:expect(createMock).toHaveBeenCalledWith",{"id":"external:expect(createMock).toHaveBeenCalledWith","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(createMock).toHaveBeenCalledWith"}}],["external:expect(listMock).toHaveBeenCalledTimes",{"id":"external:expect(listMock).toHaveBeenCalledTimes","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(listMock).toHaveBeenCalledTimes"}}],["file:/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx",{"id":"file:/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","metadata":{"lineCount":327,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:RepositoriesSettingsTab",{"id":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:RepositoriesSettingsTab","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx","symbolName":"RepositoriesSettingsTab","symbolKind":"function","exported":true,"metadata":{"line":9,"column":50,"cyclomaticComplexity":11,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["symbol-name:useParams",{"id":"symbol-name:useParams","kind":"symbol","language":"jsts","symbolName":"useParams","metadata":{"unresolvedName":"useParams"}}],["symbol-name:listRepositories",{"id":"symbol-name:listRepositories","kind":"symbol","language":"jsts","symbolName":"listRepositories","metadata":{"unresolvedName":"listRepositories"}}],["symbol-name:setRepositories",{"id":"symbol-name:setRepositories","kind":"symbol","language":"jsts","symbolName":"setRepositories","metadata":{"unresolvedName":"setRepositories"}}],["symbol-name:loadRepositories",{"id":"symbol-name:loadRepositories","kind":"symbol","language":"jsts","symbolName":"loadRepositories","metadata":{"unresolvedName":"loadRepositories"}}],["external:window.confirm",{"id":"external:window.confirm","kind":"external","language":"jsts","metadata":{"unresolvedName":"window.confirm"}}],["symbol-name:deleteRepository",{"id":"symbol-name:deleteRepository","kind":"symbol","language":"jsts","symbolName":"deleteRepository","metadata":{"unresolvedName":"deleteRepository"}}],["external:current.filter",{"id":"external:current.filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"current.filter"}}],["symbol-name:setShowCreate",{"id":"symbol-name:setShowCreate","kind":"symbol","language":"jsts","symbolName":"setShowCreate","metadata":{"unresolvedName":"setShowCreate"}}],["symbol-name:handleDelete",{"id":"symbol-name:handleDelete","kind":"symbol","language":"jsts","symbolName":"handleDelete","metadata":{"unresolvedName":"handleDelete"}}],["/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:",{"id":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":33,"column":12,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:handleDelete",{"id":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:handleDelete","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx","symbolName":"handleDelete","symbolKind":"function","exported":false,"metadata":{"line":37,"column":23,"cyclomaticComplexity":4,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:RepositoryCreateDialog",{"id":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:RepositoryCreateDialog","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","symbolName":"RepositoryCreateDialog","symbolKind":"function","exported":true,"metadata":{"line":26,"column":39,"cyclomaticComplexity":37,"maxNestingDepth":4,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["symbol-name:setUrlValidation",{"id":"symbol-name:setUrlValidation","kind":"symbol","language":"jsts","symbolName":"setUrlValidation","metadata":{"unresolvedName":"setUrlValidation"}}],["external:advancedUrl.trim",{"id":"external:advancedUrl.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"advancedUrl.trim"}}],["symbol-name:parseGitUrl",{"id":"symbol-name:parseGitUrl","kind":"symbol","language":"jsts","symbolName":"parseGitUrl","metadata":{"unresolvedName":"parseGitUrl"}}],["symbol-name:setCreateMode",{"id":"symbol-name:setCreateMode","kind":"symbol","language":"jsts","symbolName":"setCreateMode","metadata":{"unresolvedName":"setCreateMode"}}],["symbol-name:setFormName",{"id":"symbol-name:setFormName","kind":"symbol","language":"jsts","symbolName":"setFormName","metadata":{"unresolvedName":"setFormName"}}],["symbol-name:setOwner",{"id":"symbol-name:setOwner","kind":"symbol","language":"jsts","symbolName":"setOwner","metadata":{"unresolvedName":"setOwner"}}],["symbol-name:setRepoName",{"id":"symbol-name:setRepoName","kind":"symbol","language":"jsts","symbolName":"setRepoName","metadata":{"unresolvedName":"setRepoName"}}],["symbol-name:setAdvancedUrl",{"id":"symbol-name:setAdvancedUrl","kind":"symbol","language":"jsts","symbolName":"setAdvancedUrl","metadata":{"unresolvedName":"setAdvancedUrl"}}],["symbol-name:setUseAdvancedUrl",{"id":"symbol-name:setUseAdvancedUrl","kind":"symbol","language":"jsts","symbolName":"setUseAdvancedUrl","metadata":{"unresolvedName":"setUseAdvancedUrl"}}],["symbol-name:setFormError",{"id":"symbol-name:setFormError","kind":"symbol","language":"jsts","symbolName":"setFormError","metadata":{"unresolvedName":"setFormError"}}],["symbol-name:resetForm",{"id":"symbol-name:resetForm","kind":"symbol","language":"jsts","symbolName":"resetForm","metadata":{"unresolvedName":"resetForm"}}],["external:formName.trim",{"id":"external:formName.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"formName.trim"}}],["external:owner.trim",{"id":"external:owner.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"owner.trim"}}],["external:repoName.trim",{"id":"external:repoName.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"repoName.trim"}}],["symbol-name:createRepository",{"id":"symbol-name:createRepository","kind":"symbol","language":"jsts","symbolName":"createRepository","metadata":{"unresolvedName":"createRepository"}}],["symbol-name:handleClose",{"id":"symbol-name:handleClose","kind":"symbol","language":"jsts","symbolName":"handleClose","metadata":{"unresolvedName":"handleClose"}}],["symbol-name:onCreated",{"id":"symbol-name:onCreated","kind":"symbol","language":"jsts","symbolName":"onCreated","metadata":{"unresolvedName":"onCreated"}}],["symbol-name:getUrlInputClass",{"id":"symbol-name:getUrlInputClass","kind":"symbol","language":"jsts","symbolName":"getUrlInputClass","metadata":{"unresolvedName":"getUrlInputClass"}}],["/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:",{"id":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":86,"column":10,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:resetForm",{"id":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:resetForm","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","symbolName":"resetForm","symbolKind":"function","exported":false,"metadata":{"line":93,"column":20,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:handleClose",{"id":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:handleClose","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","symbolName":"handleClose","symbolKind":"function","exported":false,"metadata":{"line":104,"column":22,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:handleSubmit",{"id":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:handleSubmit","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","symbolName":"handleSubmit","symbolKind":"function","exported":false,"metadata":{"line":109,"column":23,"cyclomaticComplexity":9,"maxNestingDepth":4,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:handleUseSuggestedUrl",{"id":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:handleUseSuggestedUrl","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","symbolName":"handleUseSuggestedUrl","symbolKind":"function","exported":false,"metadata":{"line":152,"column":32,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:getUrlInputClass",{"id":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:getUrlInputClass","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","symbolName":"getUrlInputClass","symbolKind":"function","exported":false,"metadata":{"line":160,"column":27,"cyclomaticComplexity":4,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["file:/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx",{"id":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx","metadata":{"lineCount":148,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["file:/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx",{"id":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx","metadata":{"lineCount":168,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["external:@/types/project",{"id":"external:@/types/project","kind":"external","language":"jsts","metadata":{"source":"@/types/project"}}],["external:@/types/tool-type",{"id":"external:@/types/tool-type","kind":"external","language":"jsts","metadata":{"source":"@/types/tool-type"}}],["external:@/api/sessions",{"id":"external:@/api/sessions","kind":"external","language":"jsts","metadata":{"source":"@/api/sessions"}}],["/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:",{"id":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":143,"column":17,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["external:expect(screen.getByText(\"Create New Session\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"Create New Session\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"Create New Session\")).toBeInTheDocument"}}],["external:expect(screen.getByText(\"Create Session\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"Create Session\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"Create Session\")).toBeInTheDocument"}}],["external:container.querySelector",{"id":"external:container.querySelector","kind":"external","language":"jsts","metadata":{"unresolvedName":"container.querySelector"}}],["external:expect(createInstance).not.toHaveBeenCalled",{"id":"external:expect(createInstance).not.toHaveBeenCalled","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(createInstance).not.toHaveBeenCalled"}}],["external:mockedList.mockResolvedValueOnce",{"id":"external:mockedList.mockResolvedValueOnce","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockedList.mockResolvedValueOnce"}}],["external:expect(listRepositories).toHaveBeenCalledWith",{"id":"external:expect(listRepositories).toHaveBeenCalledWith","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(listRepositories).toHaveBeenCalledWith"}}],["external:@/api/settings",{"id":"external:@/api/settings","kind":"external","language":"jsts","metadata":{"source":"@/api/settings"}}],["external:@/types/git-repository",{"id":"external:@/types/git-repository","kind":"external","language":"jsts","metadata":{"source":"@/types/git-repository"}}],["/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:CreateSessionForm",{"id":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:CreateSessionForm","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx","symbolName":"CreateSessionForm","symbolKind":"function","exported":true,"metadata":{"line":18,"column":68,"cyclomaticComplexity":10,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["symbol-name:loadRepos",{"id":"symbol-name:loadRepos","kind":"symbol","language":"jsts","symbolName":"loadRepos","metadata":{"unresolvedName":"loadRepos"}}],["symbol-name:setCreateError",{"id":"symbol-name:setCreateError","kind":"symbol","language":"jsts","symbolName":"setCreateError","metadata":{"unresolvedName":"setCreateError"}}],["symbol-name:setCreateStatus",{"id":"symbol-name:setCreateStatus","kind":"symbol","language":"jsts","symbolName":"setCreateStatus","metadata":{"unresolvedName":"setCreateStatus"}}],["symbol-name:updateUserConfig",{"id":"symbol-name:updateUserConfig","kind":"symbol","language":"jsts","symbolName":"updateUserConfig","metadata":{"unresolvedName":"updateUserConfig"}}],["/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:",{"id":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":87,"column":18,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:loadRepos",{"id":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:loadRepos","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx","symbolName":"loadRepos","symbolKind":"function","exported":false,"metadata":{"line":36,"column":21,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database","auth","user-input"]}}],["/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:handleCreate",{"id":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:handleCreate","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx","symbolName":"handleCreate","symbolKind":"function","exported":false,"metadata":{"line":47,"column":23,"cyclomaticComplexity":6,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["file:/workspace/apps/web/src/components/features/session/InstanceList.module.css",{"id":"file:/workspace/apps/web/src/components/features/session/InstanceList.module.css","kind":"file","language":"css","filePath":"/workspace/apps/web/src/components/features/session/InstanceList.module.css"}],["/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList",{"id":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/session/InstanceList.tsx","symbolName":"InstanceList","symbolKind":"function","exported":true,"metadata":{"line":27,"column":29,"cyclomaticComplexity":39,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["symbol-name:useNavigate",{"id":"symbol-name:useNavigate","kind":"symbol","language":"jsts","symbolName":"useNavigate","metadata":{"unresolvedName":"useNavigate"}}],["symbol-name:listInstances",{"id":"symbol-name:listInstances","kind":"symbol","language":"jsts","symbolName":"listInstances","metadata":{"unresolvedName":"listInstances"}}],["symbol-name:setInstances",{"id":"symbol-name:setInstances","kind":"symbol","language":"jsts","symbolName":"setInstances","metadata":{"unresolvedName":"setInstances"}}],["symbol-name:loadInstances",{"id":"symbol-name:loadInstances","kind":"symbol","language":"jsts","symbolName":"loadInstances","metadata":{"unresolvedName":"loadInstances"}}],["external:instances.filter",{"id":"external:instances.filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"instances.filter"}}],["external:i.url?.startsWith",{"id":"external:i.url?.startsWith","kind":"external","language":"jsts","metadata":{"unresolvedName":"i.url?.startsWith"}}],["symbol-name:checkInstanceHealth",{"id":"symbol-name:checkInstanceHealth","kind":"symbol","language":"jsts","symbolName":"checkInstanceHealth","metadata":{"unresolvedName":"checkInstanceHealth"}}],["symbol-name:setHealthStatus",{"id":"symbol-name:setHealthStatus","kind":"symbol","language":"jsts","symbolName":"setHealthStatus","metadata":{"unresolvedName":"setHealthStatus"}}],["external:Date.now",{"id":"external:Date.now","kind":"external","language":"jsts","metadata":{"unresolvedName":"Date.now"}}],["symbol-name:checkHealth",{"id":"symbol-name:checkHealth","kind":"symbol","language":"jsts","symbolName":"checkHealth","metadata":{"unresolvedName":"checkHealth"}}],["symbol-name:stopInstance",{"id":"symbol-name:stopInstance","kind":"symbol","language":"jsts","symbolName":"stopInstance","metadata":{"unresolvedName":"stopInstance"}}],["symbol-name:setStopConfirmId",{"id":"symbol-name:setStopConfirmId","kind":"symbol","language":"jsts","symbolName":"setStopConfirmId","metadata":{"unresolvedName":"setStopConfirmId"}}],["symbol-name:restartInstance",{"id":"symbol-name:restartInstance","kind":"symbol","language":"jsts","symbolName":"restartInstance","metadata":{"unresolvedName":"restartInstance"}}],["symbol-name:confirm",{"id":"symbol-name:confirm","kind":"symbol","language":"jsts","symbolName":"confirm","metadata":{"unresolvedName":"confirm"}}],["symbol-name:deleteInstance",{"id":"symbol-name:deleteInstance","kind":"symbol","language":"jsts","symbolName":"deleteInstance","metadata":{"unresolvedName":"deleteInstance"}}],["symbol-name:recreateInstanceTunnel",{"id":"symbol-name:recreateInstanceTunnel","kind":"symbol","language":"jsts","symbolName":"recreateInstanceTunnel","metadata":{"unresolvedName":"recreateInstanceTunnel"}}],["external:instance.url?.startsWith",{"id":"external:instance.url?.startsWith","kind":"external","language":"jsts","metadata":{"unresolvedName":"instance.url?.startsWith"}}],["external:instances.map",{"id":"external:instances.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"instances.map"}}],["symbol-name:getStatusColor",{"id":"symbol-name:getStatusColor","kind":"symbol","language":"jsts","symbolName":"getStatusColor","metadata":{"unresolvedName":"getStatusColor"}}],["symbol-name:isTunnelUnhealthy",{"id":"symbol-name:isTunnelUnhealthy","kind":"symbol","language":"jsts","symbolName":"isTunnelUnhealthy","metadata":{"unresolvedName":"isTunnelUnhealthy"}}],["external:instance.tool_type_interfaces.includes",{"id":"external:instance.tool_type_interfaces.includes","kind":"external","language":"jsts","metadata":{"unresolvedName":"instance.tool_type_interfaces.includes"}}],["external:instance.url.startsWith",{"id":"external:instance.url.startsWith","kind":"external","language":"jsts","metadata":{"unresolvedName":"instance.url.startsWith"}}],["symbol-name:handleRecreateTunnel",{"id":"symbol-name:handleRecreateTunnel","kind":"symbol","language":"jsts","symbolName":"handleRecreateTunnel","metadata":{"unresolvedName":"handleRecreateTunnel"}}],["symbol-name:navigate",{"id":"symbol-name:navigate","kind":"symbol","language":"jsts","symbolName":"navigate","metadata":{"unresolvedName":"navigate"}}],["symbol-name:handleStart",{"id":"symbol-name:handleStart","kind":"symbol","language":"jsts","symbolName":"handleStart","metadata":{"unresolvedName":"handleStart"}}],["symbol-name:handleStop",{"id":"symbol-name:handleStop","kind":"symbol","language":"jsts","symbolName":"handleStop","metadata":{"unresolvedName":"handleStop"}}],["symbol-name:handleRestart",{"id":"symbol-name:handleRestart","kind":"symbol","language":"jsts","symbolName":"handleRestart","metadata":{"unresolvedName":"handleRestart"}}],["symbol-name:handleCreate",{"id":"symbol-name:handleCreate","kind":"symbol","language":"jsts","symbolName":"handleCreate","metadata":{"unresolvedName":"handleCreate"}}],["/workspace/apps/web/src/components/features/session/InstanceList.tsx:",{"id":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/session/InstanceList.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":65,"column":12,"cyclomaticComplexity":5,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/components/features/session/InstanceList.tsx:checkHealth",{"id":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:checkHealth","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/session/InstanceList.tsx","symbolName":"checkHealth","symbolKind":"function","exported":false,"metadata":{"line":71,"column":23,"cyclomaticComplexity":3,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleCreate",{"id":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleCreate","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/session/InstanceList.tsx","symbolName":"handleCreate","symbolKind":"function","exported":false,"metadata":{"line":100,"column":23,"cyclomaticComplexity":4,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleStart",{"id":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleStart","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/session/InstanceList.tsx","symbolName":"handleStart","symbolKind":"function","exported":false,"metadata":{"line":119,"column":22,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleStop",{"id":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleStop","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/session/InstanceList.tsx","symbolName":"handleStop","symbolKind":"function","exported":false,"metadata":{"line":128,"column":21,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleRestart",{"id":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleRestart","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/session/InstanceList.tsx","symbolName":"handleRestart","symbolKind":"function","exported":false,"metadata":{"line":138,"column":24,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleDelete",{"id":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleDelete","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/session/InstanceList.tsx","symbolName":"handleDelete","symbolKind":"function","exported":false,"metadata":{"line":147,"column":23,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleRecreateTunnel",{"id":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleRecreateTunnel","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/session/InstanceList.tsx","symbolName":"handleRecreateTunnel","symbolKind":"function","exported":false,"metadata":{"line":158,"column":31,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/components/features/session/InstanceList.tsx:getStatusColor",{"id":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:getStatusColor","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/session/InstanceList.tsx","symbolName":"getStatusColor","symbolKind":"function","exported":false,"metadata":{"line":167,"column":25,"cyclomaticComplexity":5,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/components/features/session/InstanceList.tsx:isTunnelUnhealthy",{"id":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:isTunnelUnhealthy","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/session/InstanceList.tsx","symbolName":"isTunnelUnhealthy","symbolKind":"function","exported":false,"metadata":{"line":181,"column":28,"cyclomaticComplexity":4,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["file:/workspace/apps/web/src/components/features/session/SessionCard.test.tsx",{"id":"file:/workspace/apps/web/src/components/features/session/SessionCard.test.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx","metadata":{"lineCount":77,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["file:/workspace/apps/web/src/components/features/session/SessionCard.tsx",{"id":"file:/workspace/apps/web/src/components/features/session/SessionCard.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/session/SessionCard.tsx","metadata":{"lineCount":212,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["external:@/types/session",{"id":"external:@/types/session","kind":"external","language":"jsts","metadata":{"source":"@/types/session"}}],["/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:",{"id":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":60,"column":58,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["external:expect(screen.getByText(\"running\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"running\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"running\")).toBeInTheDocument"}}],["external:screen.getAllByText",{"id":"external:screen.getAllByText","kind":"external","language":"jsts","metadata":{"unresolvedName":"screen.getAllByText"}}],["external:expect(screen.getByText(\"VS Code\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"VS Code\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code\")).toBeInTheDocument"}}],["/workspace/apps/web/src/components/features/session/SessionCard.tsx:SessionCard",{"id":"/workspace/apps/web/src/components/features/session/SessionCard.tsx:SessionCard","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/session/SessionCard.tsx","symbolName":"SessionCard","symbolKind":"function","exported":true,"metadata":{"line":24,"column":56,"cyclomaticComplexity":15,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["file:/workspace/apps/web/src/components/features/session/SessionList.tsx",{"id":"file:/workspace/apps/web/src/components/features/session/SessionList.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/session/SessionList.tsx","metadata":{"lineCount":195,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/components/features/session/SessionList.tsx:SessionList",{"id":"/workspace/apps/web/src/components/features/session/SessionList.tsx:SessionList","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/session/SessionList.tsx","symbolName":"SessionList","symbolKind":"function","exported":true,"metadata":{"line":18,"column":56,"cyclomaticComplexity":16,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["external:sessions.filter",{"id":"external:sessions.filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"sessions.filter"}}],["symbol-name:setTunnelHealth",{"id":"symbol-name:setTunnelHealth","kind":"symbol","language":"jsts","symbolName":"setTunnelHealth","metadata":{"unresolvedName":"setTunnelHealth"}}],["symbol-name:onSessionChange",{"id":"symbol-name:onSessionChange","kind":"symbol","language":"jsts","symbolName":"onSessionChange","metadata":{"unresolvedName":"onSessionChange"}}],["symbol-name:setDeleteConfirmId",{"id":"symbol-name:setDeleteConfirmId","kind":"symbol","language":"jsts","symbolName":"setDeleteConfirmId","metadata":{"unresolvedName":"setDeleteConfirmId"}}],["symbol-name:setRecreatingId",{"id":"symbol-name:setRecreatingId","kind":"symbol","language":"jsts","symbolName":"setRecreatingId","metadata":{"unresolvedName":"setRecreatingId"}}],["external:window.open",{"id":"external:window.open","kind":"external","language":"jsts","metadata":{"unresolvedName":"window.open"}}],["symbol-name:handleOpen",{"id":"symbol-name:handleOpen","kind":"symbol","language":"jsts","symbolName":"handleOpen","metadata":{"unresolvedName":"handleOpen"}}],["/workspace/apps/web/src/components/features/session/SessionList.tsx:",{"id":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/session/SessionList.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":186,"column":22,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/components/features/session/SessionList.tsx:checkHealth",{"id":"/workspace/apps/web/src/components/features/session/SessionList.tsx:checkHealth","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/session/SessionList.tsx","symbolName":"checkHealth","symbolKind":"function","exported":false,"metadata":{"line":38,"column":23,"cyclomaticComplexity":4,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["file:/workspace/apps/web/src/components/features/session/index.ts",{"id":"file:/workspace/apps/web/src/components/features/session/index.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/session/index.ts","metadata":{"lineCount":4,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["file:/workspace/apps/web/src/components/features/settings/SettingsTabLayout.tsx",{"id":"file:/workspace/apps/web/src/components/features/settings/SettingsTabLayout.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/settings/SettingsTabLayout.tsx","metadata":{"lineCount":47,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/features/settings/SettingsTabLayout.module.css",{"id":"file:/workspace/apps/web/src/components/features/settings/SettingsTabLayout.module.css","kind":"file","language":"css","filePath":"/workspace/apps/web/src/components/features/settings/SettingsTabLayout.module.css"}],["/workspace/apps/web/src/components/features/settings/SettingsTabLayout.tsx:SettingsTabLayout",{"id":"/workspace/apps/web/src/components/features/settings/SettingsTabLayout.tsx:SettingsTabLayout","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/settings/SettingsTabLayout.tsx","symbolName":"SettingsTabLayout","symbolKind":"function","exported":true,"metadata":{"line":17,"column":68,"cyclomaticComplexity":2,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:tabs.map",{"id":"external:tabs.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"tabs.map"}}],["external:location.pathname.includes",{"id":"external:location.pathname.includes","kind":"external","language":"jsts","metadata":{"unresolvedName":"location.pathname.includes"}}],["external:xterm",{"id":"external:xterm","kind":"external","language":"jsts","metadata":{"source":"xterm"}}],["external:xterm-addon-fit",{"id":"external:xterm-addon-fit","kind":"external","language":"jsts","metadata":{"source":"xterm-addon-fit"}}],["external:xterm-addon-serialize",{"id":"external:xterm-addon-serialize","kind":"external","language":"jsts","metadata":{"source":"xterm-addon-serialize"}}],["external:xterm-addon-web-links",{"id":"external:xterm-addon-web-links","kind":"external","language":"jsts","metadata":{"source":"xterm-addon-web-links"}}],["external:xterm/css/xterm.css",{"id":"external:xterm/css/xterm.css","kind":"external","language":"jsts","metadata":{"source":"xterm/css/xterm.css"}}],["file:/workspace/apps/web/src/hooks/use-terminal-connection.ts",{"id":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-terminal-connection.ts","metadata":{"lineCount":440,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/types/terminal.ts",{"id":"file:/workspace/apps/web/src/types/terminal.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/types/terminal.ts","metadata":{"lineCount":83,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.module.css",{"id":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.module.css","kind":"file","language":"css","filePath":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.module.css"}],["external:document.documentElement.getAttribute",{"id":"external:document.documentElement.getAttribute","kind":"external","language":"jsts","metadata":{"unresolvedName":"document.documentElement.getAttribute"}}],["external:window.matchMedia",{"id":"external:window.matchMedia","kind":"external","language":"jsts","metadata":{"unresolvedName":"window.matchMedia"}}],["external:xtermRef.current?.write",{"id":"external:xtermRef.current?.write","kind":"external","language":"jsts","metadata":{"unresolvedName":"xtermRef.current?.write"}}],["external:serializeAddonRef.current?.serialize",{"id":"external:serializeAddonRef.current?.serialize","kind":"external","language":"jsts","metadata":{"unresolvedName":"serializeAddonRef.current?.serialize"}}],["symbol-name:setSessionEnded",{"id":"symbol-name:setSessionEnded","kind":"symbol","language":"jsts","symbolName":"setSessionEnded","metadata":{"unresolvedName":"setSessionEnded"}}],["symbol-name:useTerminalConnection",{"id":"symbol-name:useTerminalConnection","kind":"symbol","language":"jsts","symbolName":"useTerminalConnection","metadata":{"unresolvedName":"useTerminalConnection"}}],["external:term.loadAddon",{"id":"external:term.loadAddon","kind":"external","language":"jsts","metadata":{"unresolvedName":"term.loadAddon"}}],["external:term.open",{"id":"external:term.open","kind":"external","language":"jsts","metadata":{"unresolvedName":"term.open"}}],["external:fitAddon.fit",{"id":"external:fitAddon.fit","kind":"external","language":"jsts","metadata":{"unresolvedName":"fitAddon.fit"}}],["external:term.onData",{"id":"external:term.onData","kind":"external","language":"jsts","metadata":{"unresolvedName":"term.onData"}}],["symbol-name:sendInput",{"id":"symbol-name:sendInput","kind":"symbol","language":"jsts","symbolName":"sendInput","metadata":{"unresolvedName":"sendInput"}}],["external:Math.abs",{"id":"external:Math.abs","kind":"external","language":"jsts","metadata":{"unresolvedName":"Math.abs"}}],["symbol-name:sendResize",{"id":"symbol-name:sendResize","kind":"symbol","language":"jsts","symbolName":"sendResize","metadata":{"unresolvedName":"sendResize"}}],["external:resizeObserver.observe",{"id":"external:resizeObserver.observe","kind":"external","language":"jsts","metadata":{"unresolvedName":"resizeObserver.observe"}}],["external:disposable.dispose",{"id":"external:disposable.dispose","kind":"external","language":"jsts","metadata":{"unresolvedName":"disposable.dispose"}}],["external:resizeObserver.disconnect",{"id":"external:resizeObserver.disconnect","kind":"external","language":"jsts","metadata":{"unresolvedName":"resizeObserver.disconnect"}}],["external:term.dispose",{"id":"external:term.dispose","kind":"external","language":"jsts","metadata":{"unresolvedName":"term.dispose"}}],["symbol-name:getStatusText",{"id":"symbol-name:getStatusText","kind":"symbol","language":"jsts","symbolName":"getStatusText","metadata":{"unresolvedName":"getStatusText"}}],["symbol-name:reconnect",{"id":"symbol-name:reconnect","kind":"symbol","language":"jsts","symbolName":"reconnect","metadata":{"unresolvedName":"reconnect"}}],["file:/workspace/apps/web/src/components/features/tool-configs/ToolConfigForm.tsx",{"id":"file:/workspace/apps/web/src/components/features/tool-configs/ToolConfigForm.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-configs/ToolConfigForm.tsx","metadata":{"lineCount":130,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/components/features/tool-configs/ToolConfigForm.tsx:ToolConfigForm",{"id":"/workspace/apps/web/src/components/features/tool-configs/ToolConfigForm.tsx:ToolConfigForm","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-configs/ToolConfigForm.tsx","symbolName":"ToolConfigForm","symbolKind":"function","exported":true,"metadata":{"line":15,"column":31,"cyclomaticComplexity":13,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["symbol-name:setSaveStatus",{"id":"symbol-name:setSaveStatus","kind":"symbol","language":"jsts","symbolName":"setSaveStatus","metadata":{"unresolvedName":"setSaveStatus"}}],["symbol-name:setFormData",{"id":"symbol-name:setFormData","kind":"symbol","language":"jsts","symbolName":"setFormData","metadata":{"unresolvedName":"setFormData"}}],["/workspace/apps/web/src/components/features/tool-configs/ToolConfigForm.tsx:handleSubmit",{"id":"/workspace/apps/web/src/components/features/tool-configs/ToolConfigForm.tsx:handleSubmit","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-configs/ToolConfigForm.tsx","symbolName":"handleSubmit","symbolKind":"function","exported":false,"metadata":{"line":30,"column":23,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["file:/workspace/apps/web/src/components/features/tool-configs/ToolConfigList.tsx",{"id":"file:/workspace/apps/web/src/components/features/tool-configs/ToolConfigList.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-configs/ToolConfigList.tsx","metadata":{"lineCount":85,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/components/features/tool-configs/ToolConfigList.tsx:ToolConfigList",{"id":"/workspace/apps/web/src/components/features/tool-configs/ToolConfigList.tsx:ToolConfigList","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-configs/ToolConfigList.tsx","symbolName":"ToolConfigList","symbolKind":"function","exported":true,"metadata":{"line":10,"column":31,"cyclomaticComplexity":5,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["external:configs.map",{"id":"external:configs.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"configs.map"}}],["symbol-name:onEdit",{"id":"symbol-name:onEdit","kind":"symbol","language":"jsts","symbolName":"onEdit","metadata":{"unresolvedName":"onEdit"}}],["file:/workspace/apps/web/src/components/features/tool-configs/index.ts",{"id":"file:/workspace/apps/web/src/components/features/tool-configs/index.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-configs/index.ts","metadata":{"lineCount":3,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["file:/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx",{"id":"file:/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx","metadata":{"lineCount":206,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm",{"id":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx","symbolName":"ToolTypeForm","symbolKind":"function","exported":true,"metadata":{"line":18,"column":29,"cyclomaticComplexity":25,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:toolType?.default_port?.toString",{"id":"external:toolType?.default_port?.toString","kind":"external","language":"jsts","metadata":{"unresolvedName":"toolType?.default_port?.toString"}}],["external:toolType?.required_variables?.join",{"id":"external:toolType?.required_variables?.join","kind":"external","language":"jsts","metadata":{"unresolvedName":"toolType?.required_variables?.join"}}],["external:formDisplayName.trim",{"id":"external:formDisplayName.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"formDisplayName.trim"}}],["external:formTemplate.trim",{"id":"external:formTemplate.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"formTemplate.trim"}}],["external:formPort.trim",{"id":"external:formPort.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"formPort.trim"}}],["symbol-name:isNaN",{"id":"symbol-name:isNaN","kind":"symbol","language":"jsts","symbolName":"isNaN","metadata":{"unresolvedName":"isNaN"}}],["symbol-name:Number",{"id":"symbol-name:Number","kind":"symbol","language":"jsts","symbolName":"Number","metadata":{"unresolvedName":"Number"}}],["external:formVariables\n\t\t\t.split(\",\")\n\t\t\t.map((v) => v.trim())\n\t\t\t.filter",{"id":"external:formVariables\n\t\t\t.split(\",\")\n\t\t\t.map((v) => v.trim())\n\t\t\t.filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"formVariables\n\t\t\t.split(\",\")\n\t\t\t.map((v) => v.trim())\n\t\t\t.filter"}}],["external:formVariables\n\t\t\t.split(\",\")\n\t\t\t.map",{"id":"external:formVariables\n\t\t\t.split(\",\")\n\t\t\t.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"formVariables\n\t\t\t.split(\",\")\n\t\t\t.map"}}],["external:formVariables\n\t\t\t.split",{"id":"external:formVariables\n\t\t\t.split","kind":"external","language":"jsts","metadata":{"unresolvedName":"formVariables\n\t\t\t.split"}}],["external:v.trim",{"id":"external:v.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"v.trim"}}],["external:formDescription.trim",{"id":"external:formDescription.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"formDescription.trim"}}],["external:formCategory.trim",{"id":"external:formCategory.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"formCategory.trim"}}],["symbol-name:setFormInterfaces",{"id":"symbol-name:setFormInterfaces","kind":"symbol","language":"jsts","symbolName":"setFormInterfaces","metadata":{"unresolvedName":"setFormInterfaces"}}],["external:prev.includes",{"id":"external:prev.includes","kind":"external","language":"jsts","metadata":{"unresolvedName":"prev.includes"}}],["symbol-name:setFormDisplayName",{"id":"symbol-name:setFormDisplayName","kind":"symbol","language":"jsts","symbolName":"setFormDisplayName","metadata":{"unresolvedName":"setFormDisplayName"}}],["symbol-name:setFormDescription",{"id":"symbol-name:setFormDescription","kind":"symbol","language":"jsts","symbolName":"setFormDescription","metadata":{"unresolvedName":"setFormDescription"}}],["symbol-name:setFormCategory",{"id":"symbol-name:setFormCategory","kind":"symbol","language":"jsts","symbolName":"setFormCategory","metadata":{"unresolvedName":"setFormCategory"}}],["external:formInterfaces.includes",{"id":"external:formInterfaces.includes","kind":"external","language":"jsts","metadata":{"unresolvedName":"formInterfaces.includes"}}],["symbol-name:toggleInterface",{"id":"symbol-name:toggleInterface","kind":"symbol","language":"jsts","symbolName":"toggleInterface","metadata":{"unresolvedName":"toggleInterface"}}],["symbol-name:setFormPort",{"id":"symbol-name:setFormPort","kind":"symbol","language":"jsts","symbolName":"setFormPort","metadata":{"unresolvedName":"setFormPort"}}],["symbol-name:setFormTemplate",{"id":"symbol-name:setFormTemplate","kind":"symbol","language":"jsts","symbolName":"setFormTemplate","metadata":{"unresolvedName":"setFormTemplate"}}],["symbol-name:setFormVariables",{"id":"symbol-name:setFormVariables","kind":"symbol","language":"jsts","symbolName":"setFormVariables","metadata":{"unresolvedName":"setFormVariables"}}],["/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:handleSubmit",{"id":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:handleSubmit","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx","symbolName":"handleSubmit","symbolKind":"function","exported":false,"metadata":{"line":46,"column":23,"cyclomaticComplexity":12,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:toggleInterface",{"id":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:toggleInterface","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx","symbolName":"toggleInterface","symbolKind":"function","exported":false,"metadata":{"line":90,"column":26,"cyclomaticComplexity":2,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/features/tool-types/ToolTypeList.tsx",{"id":"file:/workspace/apps/web/src/components/features/tool-types/ToolTypeList.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-types/ToolTypeList.tsx","metadata":{"lineCount":87,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/tool-types/ToolTypeList.tsx:ToolTypeList",{"id":"/workspace/apps/web/src/components/features/tool-types/ToolTypeList.tsx:ToolTypeList","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-types/ToolTypeList.tsx","symbolName":"ToolTypeList","symbolKind":"function","exported":true,"metadata":{"line":12,"column":29,"cyclomaticComplexity":9,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:toolType.interfaces.join",{"id":"external:toolType.interfaces.join","kind":"external","language":"jsts","metadata":{"unresolvedName":"toolType.interfaces.join"}}],["file:/workspace/apps/web/src/components/features/tool-types/index.ts",{"id":"file:/workspace/apps/web/src/components/features/tool-types/index.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-types/index.ts","metadata":{"lineCount":3,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx",{"id":"file:/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx","metadata":{"lineCount":290,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab",{"id":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx","symbolName":"ConfigFoldersTab","symbolKind":"function","exported":true,"metadata":{"line":13,"column":33,"cyclomaticComplexity":23,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["symbol-name:setFolders",{"id":"symbol-name:setFolders","kind":"symbol","language":"jsts","symbolName":"setFolders","metadata":{"unresolvedName":"setFolders"}}],["symbol-name:loadFolders",{"id":"symbol-name:loadFolders","kind":"symbol","language":"jsts","symbolName":"loadFolders","metadata":{"unresolvedName":"loadFolders"}}],["symbol-name:setFolderForm",{"id":"symbol-name:setFolderForm","kind":"symbol","language":"jsts","symbolName":"setFolderForm","metadata":{"unresolvedName":"setFolderForm"}}],["symbol-name:setFolderError",{"id":"symbol-name:setFolderError","kind":"symbol","language":"jsts","symbolName":"setFolderError","metadata":{"unresolvedName":"setFolderError"}}],["symbol-name:setShowFolderForm",{"id":"symbol-name:setShowFolderForm","kind":"symbol","language":"jsts","symbolName":"setShowFolderForm","metadata":{"unresolvedName":"setShowFolderForm"}}],["symbol-name:setSelectedFolder",{"id":"symbol-name:setSelectedFolder","kind":"symbol","language":"jsts","symbolName":"setSelectedFolder","metadata":{"unresolvedName":"setSelectedFolder"}}],["external:JSON.stringify",{"id":"external:JSON.stringify","kind":"external","language":"jsts","metadata":{"unresolvedName":"JSON.stringify"}}],["external:folderForm.name.trim",{"id":"external:folderForm.name.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"folderForm.name.trim"}}],["external:folderForm.mount_path.trim",{"id":"external:folderForm.mount_path.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"folderForm.mount_path.trim"}}],["external:folderForm.files_json.trim",{"id":"external:folderForm.files_json.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"folderForm.files_json.trim"}}],["external:JSON.parse",{"id":"external:JSON.parse","kind":"external","language":"jsts","metadata":{"unresolvedName":"JSON.parse"}}],["external:folderForm.description.trim",{"id":"external:folderForm.description.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"folderForm.description.trim"}}],["symbol-name:alert",{"id":"symbol-name:alert","kind":"symbol","language":"jsts","symbolName":"alert","metadata":{"unresolvedName":"alert"}}],["external:folders.map",{"id":"external:folders.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"folders.map"}}],["external:Object.keys",{"id":"external:Object.keys","kind":"external","language":"jsts","metadata":{"unresolvedName":"Object.keys"}}],["symbol-name:openEditFolder",{"id":"symbol-name:openEditFolder","kind":"symbol","language":"jsts","symbolName":"openEditFolder","metadata":{"unresolvedName":"openEditFolder"}}],["symbol-name:handleDeleteFolder",{"id":"symbol-name:handleDeleteFolder","kind":"symbol","language":"jsts","symbolName":"handleDeleteFolder","metadata":{"unresolvedName":"handleDeleteFolder"}}],["/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:",{"id":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":42,"column":12,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:openCreateFolder",{"id":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:openCreateFolder","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx","symbolName":"openCreateFolder","symbolKind":"function","exported":false,"metadata":{"line":46,"column":27,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:openEditFolder",{"id":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:openEditFolder","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx","symbolName":"openEditFolder","symbolKind":"function","exported":false,"metadata":{"line":59,"column":25,"cyclomaticComplexity":2,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:handleFolderSubmit",{"id":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:handleFolderSubmit","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx","symbolName":"handleFolderSubmit","symbolKind":"function","exported":false,"metadata":{"line":72,"column":29,"cyclomaticComplexity":10,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:handleDeleteFolder",{"id":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:handleDeleteFolder","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx","symbolName":"handleDeleteFolder","symbolKind":"function","exported":false,"metadata":{"line":119,"column":29,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["file:/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx",{"id":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx","metadata":{"lineCount":461,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab",{"id":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx","symbolName":"ToolConfigsTab","symbolKind":"function","exported":true,"metadata":{"line":13,"column":31,"cyclomaticComplexity":40,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["external:Promise.all",{"id":"external:Promise.all","kind":"external","language":"jsts","metadata":{"unresolvedName":"Promise.all"}}],["symbol-name:listToolConfigs",{"id":"symbol-name:listToolConfigs","kind":"symbol","language":"jsts","symbolName":"listToolConfigs","metadata":{"unresolvedName":"listToolConfigs"}}],["symbol-name:setConfigs",{"id":"symbol-name:setConfigs","kind":"symbol","language":"jsts","symbolName":"setConfigs","metadata":{"unresolvedName":"setConfigs"}}],["symbol-name:setToolTypes",{"id":"symbol-name:setToolTypes","kind":"symbol","language":"jsts","symbolName":"setToolTypes","metadata":{"unresolvedName":"setToolTypes"}}],["symbol-name:loadData",{"id":"symbol-name:loadData","kind":"symbol","language":"jsts","symbolName":"loadData","metadata":{"unresolvedName":"loadData"}}],["symbol-name:setConfigForm",{"id":"symbol-name:setConfigForm","kind":"symbol","language":"jsts","symbolName":"setConfigForm","metadata":{"unresolvedName":"setConfigForm"}}],["symbol-name:setConfigError",{"id":"symbol-name:setConfigError","kind":"symbol","language":"jsts","symbolName":"setConfigError","metadata":{"unresolvedName":"setConfigError"}}],["symbol-name:setShowConfigForm",{"id":"symbol-name:setShowConfigForm","kind":"symbol","language":"jsts","symbolName":"setShowConfigForm","metadata":{"unresolvedName":"setShowConfigForm"}}],["symbol-name:setSelectedConfig",{"id":"symbol-name:setSelectedConfig","kind":"symbol","language":"jsts","symbolName":"setSelectedConfig","metadata":{"unresolvedName":"setSelectedConfig"}}],["external:config.port_override?.toString",{"id":"external:config.port_override?.toString","kind":"external","language":"jsts","metadata":{"unresolvedName":"config.port_override?.toString"}}],["external:configForm.key.trim",{"id":"external:configForm.key.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"configForm.key.trim"}}],["external:configForm.env_vars_json.trim",{"id":"external:configForm.env_vars_json.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"configForm.env_vars_json.trim"}}],["external:configForm.volumes_json.trim",{"id":"external:configForm.volumes_json.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"configForm.volumes_json.trim"}}],["external:configForm.start_command.trim",{"id":"external:configForm.start_command.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"configForm.start_command.trim"}}],["external:configForm.working_directory.trim",{"id":"external:configForm.working_directory.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"configForm.working_directory.trim"}}],["symbol-name:updateToolConfig",{"id":"symbol-name:updateToolConfig","kind":"symbol","language":"jsts","symbolName":"updateToolConfig","metadata":{"unresolvedName":"updateToolConfig"}}],["symbol-name:createToolConfig",{"id":"symbol-name:createToolConfig","kind":"symbol","language":"jsts","symbolName":"createToolConfig","metadata":{"unresolvedName":"createToolConfig"}}],["symbol-name:deleteToolConfig",{"id":"symbol-name:deleteToolConfig","kind":"symbol","language":"jsts","symbolName":"deleteToolConfig","metadata":{"unresolvedName":"deleteToolConfig"}}],["symbol-name:openEditConfig",{"id":"symbol-name:openEditConfig","kind":"symbol","language":"jsts","symbolName":"openEditConfig","metadata":{"unresolvedName":"openEditConfig"}}],["symbol-name:handleDeleteConfig",{"id":"symbol-name:handleDeleteConfig","kind":"symbol","language":"jsts","symbolName":"handleDeleteConfig","metadata":{"unresolvedName":"handleDeleteConfig"}}],["/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:",{"id":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":50,"column":12,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:openCreateConfig",{"id":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:openCreateConfig","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx","symbolName":"openCreateConfig","symbolKind":"function","exported":false,"metadata":{"line":54,"column":27,"cyclomaticComplexity":2,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:openEditConfig",{"id":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:openEditConfig","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx","symbolName":"openEditConfig","symbolKind":"function","exported":false,"metadata":{"line":72,"column":25,"cyclomaticComplexity":7,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:handleConfigSubmit",{"id":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:handleConfigSubmit","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx","symbolName":"handleConfigSubmit","symbolKind":"function","exported":false,"metadata":{"line":94,"column":29,"cyclomaticComplexity":16,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:handleDeleteConfig",{"id":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:handleDeleteConfig","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx","symbolName":"handleDeleteConfig","symbolKind":"function","exported":false,"metadata":{"line":165,"column":29,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx",{"id":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx","metadata":{"lineCount":61,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx",{"id":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","metadata":{"lineCount":544,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:",{"id":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":54,"column":17,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:expect(screen.getByText(/loading tool types/i)).toBeInTheDocument",{"id":"external:expect(screen.getByText(/loading tool types/i)).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(/loading tool types/i)).toBeInTheDocument"}}],["external:expect(screen.getByText(\"Tool Types\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"Tool Types\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"Tool Types\")).toBeInTheDocument"}}],["external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument"}}],["external:mockedList.mockRejectedValueOnce",{"id":"external:mockedList.mockRejectedValueOnce","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockedList.mockRejectedValueOnce"}}],["/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab",{"id":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","symbolName":"ToolTypesTab","symbolKind":"function","exported":true,"metadata":{"line":14,"column":29,"cyclomaticComplexity":56,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:loadToolTypes",{"id":"symbol-name:loadToolTypes","kind":"symbol","language":"jsts","symbolName":"loadToolTypes","metadata":{"unresolvedName":"loadToolTypes"}}],["symbol-name:setToolTypeForm",{"id":"symbol-name:setToolTypeForm","kind":"symbol","language":"jsts","symbolName":"setToolTypeForm","metadata":{"unresolvedName":"setToolTypeForm"}}],["symbol-name:setToolTypeError",{"id":"symbol-name:setToolTypeError","kind":"symbol","language":"jsts","symbolName":"setToolTypeError","metadata":{"unresolvedName":"setToolTypeError"}}],["symbol-name:setShowToolTypeForm",{"id":"symbol-name:setShowToolTypeForm","kind":"symbol","language":"jsts","symbolName":"setShowToolTypeForm","metadata":{"unresolvedName":"setShowToolTypeForm"}}],["external:toolType.default_port?.toString",{"id":"external:toolType.default_port?.toString","kind":"external","language":"jsts","metadata":{"unresolvedName":"toolType.default_port?.toString"}}],["external:toolType.readiness_probe?.timeout?.toString",{"id":"external:toolType.readiness_probe?.timeout?.toString","kind":"external","language":"jsts","metadata":{"unresolvedName":"toolType.readiness_probe?.timeout?.toString"}}],["external:toolType.readiness_probe?.interval?.toString",{"id":"external:toolType.readiness_probe?.interval?.toString","kind":"external","language":"jsts","metadata":{"unresolvedName":"toolType.readiness_probe?.interval?.toString"}}],["external:toolType.required_variables?.join",{"id":"external:toolType.required_variables?.join","kind":"external","language":"jsts","metadata":{"unresolvedName":"toolType.required_variables?.join"}}],["external:toolTypeForm.name.trim",{"id":"external:toolTypeForm.name.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"toolTypeForm.name.trim"}}],["external:toolTypeForm.display_name.trim",{"id":"external:toolTypeForm.display_name.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"toolTypeForm.display_name.trim"}}],["external:toolTypeForm.default_port.trim",{"id":"external:toolTypeForm.default_port.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"toolTypeForm.default_port.trim"}}],["external:template.trim",{"id":"external:template.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"template.trim"}}],["external:toolTypeForm.required_variables\n\t\t\t.split(\",\")\n\t\t\t.map",{"id":"external:toolTypeForm.required_variables\n\t\t\t.split(\",\")\n\t\t\t.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"toolTypeForm.required_variables\n\t\t\t.split(\",\")\n\t\t\t.map"}}],["external:toolTypeForm.required_variables\n\t\t\t.split",{"id":"external:toolTypeForm.required_variables\n\t\t\t.split","kind":"external","language":"jsts","metadata":{"unresolvedName":"toolTypeForm.required_variables\n\t\t\t.split"}}],["external:toolTypeForm.readiness_command.trim",{"id":"external:toolTypeForm.readiness_command.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"toolTypeForm.readiness_command.trim"}}],["symbol-name:parseInt",{"id":"symbol-name:parseInt","kind":"symbol","language":"jsts","symbolName":"parseInt","metadata":{"unresolvedName":"parseInt"}}],["external:toolTypeForm.description.trim",{"id":"external:toolTypeForm.description.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"toolTypeForm.description.trim"}}],["external:toolTypeForm.category.trim",{"id":"external:toolTypeForm.category.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"toolTypeForm.category.trim"}}],["external:[\"web\", \"terminal\"].map",{"id":"external:[\"web\", \"terminal\"].map","kind":"external","language":"jsts","metadata":{"unresolvedName":"[\"web\", \"terminal\"].map"}}],["external:toolTypeForm.interfaces.includes",{"id":"external:toolTypeForm.interfaces.includes","kind":"external","language":"jsts","metadata":{"unresolvedName":"toolTypeForm.interfaces.includes"}}],["external:toolTypeForm.interfaces.filter",{"id":"external:toolTypeForm.interfaces.filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"toolTypeForm.interfaces.filter"}}],["symbol-name:openEditToolType",{"id":"symbol-name:openEditToolType","kind":"symbol","language":"jsts","symbolName":"openEditToolType","metadata":{"unresolvedName":"openEditToolType"}}],["symbol-name:handleDeleteToolType",{"id":"symbol-name:handleDeleteToolType","kind":"symbol","language":"jsts","symbolName":"handleDeleteToolType","metadata":{"unresolvedName":"handleDeleteToolType"}}],["/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:",{"id":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":391,"column":19,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:openCreateToolType",{"id":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:openCreateToolType","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","symbolName":"openCreateToolType","symbolKind":"function","exported":false,"metadata":{"line":55,"column":29,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:openEditToolType",{"id":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:openEditToolType","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","symbolName":"openEditToolType","symbolKind":"function","exported":false,"metadata":{"line":76,"column":27,"cyclomaticComplexity":12,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleToolTypeSubmit",{"id":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleToolTypeSubmit","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","symbolName":"handleToolTypeSubmit","symbolKind":"function","exported":false,"metadata":{"line":97,"column":31,"cyclomaticComplexity":24,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleDeleteToolType",{"id":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleDeleteToolType","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","symbolName":"handleDeleteToolType","symbolKind":"function","exported":false,"metadata":{"line":196,"column":31,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/features/tool-workshop/index.ts",{"id":"file:/workspace/apps/web/src/components/features/tool-workshop/index.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-workshop/index.ts","metadata":{"lineCount":4,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/features/workspace/WorkspaceHeader.tsx",{"id":"file:/workspace/apps/web/src/components/features/workspace/WorkspaceHeader.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/workspace/WorkspaceHeader.tsx","metadata":{"lineCount":49,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/workspace/WorkspaceHeader.tsx:WorkspaceHeader",{"id":"/workspace/apps/web/src/components/features/workspace/WorkspaceHeader.tsx:WorkspaceHeader","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/workspace/WorkspaceHeader.tsx","symbolName":"WorkspaceHeader","symbolKind":"function","exported":true,"metadata":{"line":16,"column":32,"cyclomaticComplexity":3,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/git-mount-editor.tsx",{"id":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/git-mount-editor.tsx","metadata":{"lineCount":575,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/git-mount-editor.tsx:normalizeMount",{"id":"/workspace/apps/web/src/components/git-mount-editor.tsx:normalizeMount","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/git-mount-editor.tsx","symbolName":"normalizeMount","symbolKind":"function","exported":false,"metadata":{"line":11,"column":1,"cyclomaticComplexity":6,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/git-mount-editor.tsx:normalizeMounts",{"id":"/workspace/apps/web/src/components/git-mount-editor.tsx:normalizeMounts","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/git-mount-editor.tsx","symbolName":"normalizeMounts","symbolKind":"function","exported":false,"metadata":{"line":32,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:mounts.map",{"id":"external:mounts.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"mounts.map"}}],["/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountEditor",{"id":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountEditor","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/git-mount-editor.tsx","symbolName":"GitMountEditor","symbolKind":"function","exported":true,"metadata":{"line":36,"column":31,"cyclomaticComplexity":6,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:normalizeMounts",{"id":"symbol-name:normalizeMounts","kind":"symbol","language":"jsts","symbolName":"normalizeMounts","metadata":{"unresolvedName":"normalizeMounts"}}],["symbol-name:setNormalizedMounts",{"id":"symbol-name:setNormalizedMounts","kind":"symbol","language":"jsts","symbolName":"setNormalizedMounts","metadata":{"unresolvedName":"setNormalizedMounts"}}],["symbol-name:normalizeMount",{"id":"symbol-name:normalizeMount","kind":"symbol","language":"jsts","symbolName":"normalizeMount","metadata":{"unresolvedName":"normalizeMount"}}],["symbol-name:onChange",{"id":"symbol-name:onChange","kind":"symbol","language":"jsts","symbolName":"onChange","metadata":{"unresolvedName":"onChange"}}],["symbol-name:setIsAdding",{"id":"symbol-name:setIsAdding","kind":"symbol","language":"jsts","symbolName":"setIsAdding","metadata":{"unresolvedName":"setIsAdding"}}],["symbol-name:setEditingIndex",{"id":"symbol-name:setEditingIndex","kind":"symbol","language":"jsts","symbolName":"setEditingIndex","metadata":{"unresolvedName":"setEditingIndex"}}],["external:normalizedMounts.filter",{"id":"external:normalizedMounts.filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"normalizedMounts.filter"}}],["external:normalizedMounts.map",{"id":"external:normalizedMounts.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"normalizedMounts.map"}}],["symbol-name:handleUpdate",{"id":"symbol-name:handleUpdate","kind":"symbol","language":"jsts","symbolName":"handleUpdate","metadata":{"unresolvedName":"handleUpdate"}}],["external:mount.mappings?.map",{"id":"external:mount.mappings?.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"mount.mappings?.map"}}],["symbol-name:handleRemove",{"id":"symbol-name:handleRemove","kind":"symbol","language":"jsts","symbolName":"handleRemove","metadata":{"unresolvedName":"handleRemove"}}],["/workspace/apps/web/src/components/git-mount-editor.tsx:",{"id":"/workspace/apps/web/src/components/git-mount-editor.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/git-mount-editor.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":379,"column":20,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/git-mount-editor.tsx:handleAdd",{"id":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleAdd","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/git-mount-editor.tsx","symbolName":"handleAdd","symbolKind":"function","exported":false,"metadata":{"line":50,"column":20,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/git-mount-editor.tsx:handleUpdate",{"id":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleUpdate","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/git-mount-editor.tsx","symbolName":"handleUpdate","symbolKind":"function","exported":false,"metadata":{"line":57,"column":23,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/git-mount-editor.tsx:handleRemove",{"id":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleRemove","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/git-mount-editor.tsx","symbolName":"handleRemove","symbolKind":"function","exported":false,"metadata":{"line":65,"column":23,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountForm",{"id":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountForm","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/git-mount-editor.tsx","symbolName":"GitMountForm","symbolKind":"function","exported":false,"metadata":{"line":218,"column":22,"cyclomaticComplexity":46,"maxNestingDepth":3,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:remoteUrl.trim",{"id":"external:remoteUrl.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"remoteUrl.trim"}}],["symbol-name:setErrors",{"id":"symbol-name:setErrors","kind":"symbol","language":"jsts","symbolName":"setErrors","metadata":{"unresolvedName":"setErrors"}}],["symbol-name:setValidation",{"id":"symbol-name:setValidation","kind":"symbol","language":"jsts","symbolName":"setValidation","metadata":{"unresolvedName":"setValidation"}}],["symbol-name:validateGitUrl",{"id":"symbol-name:validateGitUrl","kind":"symbol","language":"jsts","symbolName":"validateGitUrl","metadata":{"unresolvedName":"validateGitUrl"}}],["symbol-name:setRemoteUrl",{"id":"symbol-name:setRemoteUrl","kind":"symbol","language":"jsts","symbolName":"setRemoteUrl","metadata":{"unresolvedName":"setRemoteUrl"}}],["external:remoteUrl.startsWith",{"id":"external:remoteUrl.startsWith","kind":"external","language":"jsts","metadata":{"unresolvedName":"remoteUrl.startsWith"}}],["external:mappings.forEach",{"id":"external:mappings.forEach","kind":"external","language":"jsts","metadata":{"unresolvedName":"mappings.forEach"}}],["external:m.target_path.trim",{"id":"external:m.target_path.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"m.target_path.trim"}}],["external:m.source_path.includes",{"id":"external:m.source_path.includes","kind":"external","language":"jsts","metadata":{"unresolvedName":"m.source_path.includes"}}],["external:m.target_path.includes",{"id":"external:m.target_path.includes","kind":"external","language":"jsts","metadata":{"unresolvedName":"m.target_path.includes"}}],["symbol-name:validate",{"id":"symbol-name:validate","kind":"symbol","language":"jsts","symbolName":"validate","metadata":{"unresolvedName":"validate"}}],["symbol-name:onSave",{"id":"symbol-name:onSave","kind":"symbol","language":"jsts","symbolName":"onSave","metadata":{"unresolvedName":"onSave"}}],["external:branch.trim",{"id":"external:branch.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"branch.trim"}}],["external:mappings.map",{"id":"external:mappings.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"mappings.map"}}],["external:m.source_path.trim",{"id":"external:m.source_path.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"m.source_path.trim"}}],["symbol-name:setMappings",{"id":"symbol-name:setMappings","kind":"symbol","language":"jsts","symbolName":"setMappings","metadata":{"unresolvedName":"setMappings"}}],["symbol-name:updateMapping",{"id":"symbol-name:updateMapping","kind":"symbol","language":"jsts","symbolName":"updateMapping","metadata":{"unresolvedName":"updateMapping"}}],["symbol-name:removeMapping",{"id":"symbol-name:removeMapping","kind":"symbol","language":"jsts","symbolName":"removeMapping","metadata":{"unresolvedName":"removeMapping"}}],["/workspace/apps/web/src/components/git-mount-editor.tsx:handleCheckUrl",{"id":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleCheckUrl","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/git-mount-editor.tsx","symbolName":"handleCheckUrl","symbolKind":"function","exported":false,"metadata":{"line":239,"column":25,"cyclomaticComplexity":13,"maxNestingDepth":3,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/git-mount-editor.tsx:applySuggestion",{"id":"/workspace/apps/web/src/components/git-mount-editor.tsx:applySuggestion","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/git-mount-editor.tsx","symbolName":"applySuggestion","symbolKind":"function","exported":false,"metadata":{"line":284,"column":26,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/git-mount-editor.tsx:validate",{"id":"/workspace/apps/web/src/components/git-mount-editor.tsx:validate","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/git-mount-editor.tsx","symbolName":"validate","symbolKind":"function","exported":false,"metadata":{"line":291,"column":19,"cyclomaticComplexity":9,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/git-mount-editor.tsx:handleSubmit",{"id":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleSubmit","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/git-mount-editor.tsx","symbolName":"handleSubmit","symbolKind":"function","exported":false,"metadata":{"line":322,"column":23,"cyclomaticComplexity":4,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/git-mount-editor.tsx:addMapping",{"id":"/workspace/apps/web/src/components/git-mount-editor.tsx:addMapping","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/git-mount-editor.tsx","symbolName":"addMapping","symbolKind":"function","exported":false,"metadata":{"line":334,"column":21,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/git-mount-editor.tsx:updateMapping",{"id":"/workspace/apps/web/src/components/git-mount-editor.tsx:updateMapping","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/git-mount-editor.tsx","symbolName":"updateMapping","symbolKind":"function","exported":false,"metadata":{"line":338,"column":24,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/git-mount-editor.tsx:removeMapping",{"id":"/workspace/apps/web/src/components/git-mount-editor.tsx:removeMapping","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/git-mount-editor.tsx","symbolName":"removeMapping","symbolKind":"function","exported":false,"metadata":{"line":357,"column":24,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/layout/AppShell.tsx",{"id":"file:/workspace/apps/web/src/components/layout/AppShell.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/layout/AppShell.tsx","metadata":{"lineCount":138,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/hooks/use-theme.ts",{"id":"file:/workspace/apps/web/src/hooks/use-theme.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-theme.ts","metadata":{"lineCount":24,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/state/sessions.tsx",{"id":"file:/workspace/apps/web/src/state/sessions.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/state/sessions.tsx","metadata":{"lineCount":42,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["file:/workspace/apps/web/src/utils/icons.ts",{"id":"file:/workspace/apps/web/src/utils/icons.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/utils/icons.ts","metadata":{"lineCount":2,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/layout/AppShell.module.css",{"id":"file:/workspace/apps/web/src/components/layout/AppShell.module.css","kind":"file","language":"css","filePath":"/workspace/apps/web/src/components/layout/AppShell.module.css"}],["/workspace/apps/web/src/components/layout/AppShell.tsx:SessionItem",{"id":"/workspace/apps/web/src/components/layout/AppShell.tsx:SessionItem","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/layout/AppShell.tsx","symbolName":"SessionItem","symbolKind":"function","exported":false,"metadata":{"line":22,"column":21,"cyclomaticComplexity":7,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/components/layout/AppShell.tsx:AppShell",{"id":"/workspace/apps/web/src/components/layout/AppShell.tsx:AppShell","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/layout/AppShell.tsx","symbolName":"AppShell","symbolKind":"function","exported":true,"metadata":{"line":44,"column":25,"cyclomaticComplexity":7,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:useTheme",{"id":"symbol-name:useTheme","kind":"symbol","language":"jsts","symbolName":"useTheme","metadata":{"unresolvedName":"useTheme"}}],["symbol-name:useSessions",{"id":"symbol-name:useSessions","kind":"symbol","language":"jsts","symbolName":"useSessions","metadata":{"unresolvedName":"useSessions"}}],["symbol-name:getUserSessions",{"id":"symbol-name:getUserSessions","kind":"symbol","language":"jsts","symbolName":"getUserSessions","metadata":{"unresolvedName":"getUserSessions"}}],["symbol-name:setAllSessions",{"id":"symbol-name:setAllSessions","kind":"symbol","language":"jsts","symbolName":"setAllSessions","metadata":{"unresolvedName":"setAllSessions"}}],["symbol-name:loadSessions",{"id":"symbol-name:loadSessions","kind":"symbol","language":"jsts","symbolName":"loadSessions","metadata":{"unresolvedName":"loadSessions"}}],["symbol-name:logout",{"id":"symbol-name:logout","kind":"symbol","language":"jsts","symbolName":"logout","metadata":{"unresolvedName":"logout"}}],["external:NAV_ITEMS.map",{"id":"external:NAV_ITEMS.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"NAV_ITEMS.map"}}],["external:ACTIVE_STATUSES.includes",{"id":"external:ACTIVE_STATUSES.includes","kind":"external","language":"jsts","metadata":{"unresolvedName":"ACTIVE_STATUSES.includes"}}],["external:sessions\n\t\t\t\t\t\t\t\t.filter",{"id":"external:sessions\n\t\t\t\t\t\t\t\t.filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"sessions\n\t\t\t\t\t\t\t\t.filter"}}],["/workspace/apps/web/src/components/layout/AppShell.tsx:",{"id":"/workspace/apps/web/src/components/layout/AppShell.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/layout/AppShell.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":92,"column":21,"cyclomaticComplexity":4,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/manifest-editor.tsx",{"id":"file:/workspace/apps/web/src/components/manifest-editor.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/manifest-editor.tsx","metadata":{"lineCount":859,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/utils/errors.ts",{"id":"file:/workspace/apps/web/src/utils/errors.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/utils/errors.ts","metadata":{"lineCount":10,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor",{"id":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/manifest-editor.tsx","symbolName":"ManifestEditor","symbolKind":"function","exported":true,"metadata":{"line":32,"column":31,"cyclomaticComplexity":45,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:setBaseImage",{"id":"symbol-name:setBaseImage","kind":"symbol","language":"jsts","symbolName":"setBaseImage","metadata":{"unresolvedName":"setBaseImage"}}],["symbol-name:setBaseDefinitionId",{"id":"symbol-name:setBaseDefinitionId","kind":"symbol","language":"jsts","symbolName":"setBaseDefinitionId","metadata":{"unresolvedName":"setBaseDefinitionId"}}],["symbol-name:setAptPackages",{"id":"symbol-name:setAptPackages","kind":"symbol","language":"jsts","symbolName":"setAptPackages","metadata":{"unresolvedName":"setAptPackages"}}],["external:((pkgs.apt as string[]) || []).map",{"id":"external:((pkgs.apt as string[]) || []).map","kind":"external","language":"jsts","metadata":{"unresolvedName":"((pkgs.apt as string[]) || []).map"}}],["symbol-name:setNpmPackages",{"id":"symbol-name:setNpmPackages","kind":"symbol","language":"jsts","symbolName":"setNpmPackages","metadata":{"unresolvedName":"setNpmPackages"}}],["external:((pkgs.npm_global as string[]) || []).map",{"id":"external:((pkgs.npm_global as string[]) || []).map","kind":"external","language":"jsts","metadata":{"unresolvedName":"((pkgs.npm_global as string[]) || []).map"}}],["symbol-name:setPipPackages",{"id":"symbol-name:setPipPackages","kind":"symbol","language":"jsts","symbolName":"setPipPackages","metadata":{"unresolvedName":"setPipPackages"}}],["external:((pkgs.pip as string[]) || []).map",{"id":"external:((pkgs.pip as string[]) || []).map","kind":"external","language":"jsts","metadata":{"unresolvedName":"((pkgs.pip as string[]) || []).map"}}],["symbol-name:setNodeVersion",{"id":"symbol-name:setNodeVersion","kind":"symbol","language":"jsts","symbolName":"setNodeVersion","metadata":{"unresolvedName":"setNodeVersion"}}],["symbol-name:setUserName",{"id":"symbol-name:setUserName","kind":"symbol","language":"jsts","symbolName":"setUserName","metadata":{"unresolvedName":"setUserName"}}],["symbol-name:setUserUid",{"id":"symbol-name:setUserUid","kind":"symbol","language":"jsts","symbolName":"setUserUid","metadata":{"unresolvedName":"setUserUid"}}],["symbol-name:setUserGid",{"id":"symbol-name:setUserGid","kind":"symbol","language":"jsts","symbolName":"setUserGid","metadata":{"unresolvedName":"setUserGid"}}],["symbol-name:setEnvVars",{"id":"symbol-name:setEnvVars","kind":"symbol","language":"jsts","symbolName":"setEnvVars","metadata":{"unresolvedName":"setEnvVars"}}],["external:Object.entries(env).map",{"id":"external:Object.entries(env).map","kind":"external","language":"jsts","metadata":{"unresolvedName":"Object.entries(env).map"}}],["external:Object.entries",{"id":"external:Object.entries","kind":"external","language":"jsts","metadata":{"unresolvedName":"Object.entries"}}],["symbol-name:setBuildScripts",{"id":"symbol-name:setBuildScripts","kind":"symbol","language":"jsts","symbolName":"setBuildScripts","metadata":{"unresolvedName":"setBuildScripts"}}],["symbol-name:setStartupScripts",{"id":"symbol-name:setStartupScripts","kind":"symbol","language":"jsts","symbolName":"setStartupScripts","metadata":{"unresolvedName":"setStartupScripts"}}],["symbol-name:setMounts",{"id":"symbol-name:setMounts","kind":"symbol","language":"jsts","symbolName":"setMounts","metadata":{"unresolvedName":"setMounts"}}],["symbol-name:setCommand",{"id":"symbol-name:setCommand","kind":"symbol","language":"jsts","symbolName":"setCommand","metadata":{"unresolvedName":"setCommand"}}],["symbol-name:setWorkingDir",{"id":"symbol-name:setWorkingDir","kind":"symbol","language":"jsts","symbolName":"setWorkingDir","metadata":{"unresolvedName":"setWorkingDir"}}],["symbol-name:setStdinOpen",{"id":"symbol-name:setStdinOpen","kind":"symbol","language":"jsts","symbolName":"setStdinOpen","metadata":{"unresolvedName":"setStdinOpen"}}],["symbol-name:setTty",{"id":"symbol-name:setTty","kind":"symbol","language":"jsts","symbolName":"setTty","metadata":{"unresolvedName":"setTty"}}],["external:aptPackages.map((p) => p.name).filter",{"id":"external:aptPackages.map((p) => p.name).filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"aptPackages.map((p) => p.name).filter"}}],["external:aptPackages.map",{"id":"external:aptPackages.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"aptPackages.map"}}],["external:npmPackages.map((p) => p.name).filter",{"id":"external:npmPackages.map((p) => p.name).filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"npmPackages.map((p) => p.name).filter"}}],["external:npmPackages.map",{"id":"external:npmPackages.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"npmPackages.map"}}],["external:pipPackages.map((p) => p.name).filter",{"id":"external:pipPackages.map((p) => p.name).filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"pipPackages.map((p) => p.name).filter"}}],["external:pipPackages.map",{"id":"external:pipPackages.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"pipPackages.map"}}],["external:envVars.forEach",{"id":"external:envVars.forEach","kind":"external","language":"jsts","metadata":{"unresolvedName":"envVars.forEach"}}],["external:buildScripts.filter",{"id":"external:buildScripts.filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"buildScripts.filter"}}],["external:startupScripts.filter",{"id":"external:startupScripts.filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"startupScripts.filter"}}],["external:mounts.filter",{"id":"external:mounts.filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"mounts.filter"}}],["external:command.filter",{"id":"external:command.filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"command.filter"}}],["symbol-name:buildManifest",{"id":"symbol-name:buildManifest","kind":"symbol","language":"jsts","symbolName":"buildManifest","metadata":{"unresolvedName":"buildManifest"}}],["external:onChangeRef.current",{"id":"external:onChangeRef.current","kind":"external","language":"jsts","metadata":{"unresolvedName":"onChangeRef.current"}}],["symbol-name:setPreviewError",{"id":"symbol-name:setPreviewError","kind":"symbol","language":"jsts","symbolName":"setPreviewError","metadata":{"unresolvedName":"setPreviewError"}}],["symbol-name:setPreviewLoading",{"id":"symbol-name:setPreviewLoading","kind":"symbol","language":"jsts","symbolName":"setPreviewLoading","metadata":{"unresolvedName":"setPreviewLoading"}}],["symbol-name:compileToolDefinition",{"id":"symbol-name:compileToolDefinition","kind":"symbol","language":"jsts","symbolName":"compileToolDefinition","metadata":{"unresolvedName":"compileToolDefinition"}}],["symbol-name:setPreview",{"id":"symbol-name:setPreview","kind":"symbol","language":"jsts","symbolName":"setPreview","metadata":{"unresolvedName":"setPreview"}}],["symbol-name:extractErrorMessage",{"id":"symbol-name:extractErrorMessage","kind":"symbol","language":"jsts","symbolName":"extractErrorMessage","metadata":{"unresolvedName":"extractErrorMessage"}}],["external:aptPackages.filter",{"id":"external:aptPackages.filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"aptPackages.filter"}}],["external:npmPackages.filter",{"id":"external:npmPackages.filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"npmPackages.filter"}}],["external:pipPackages.filter",{"id":"external:pipPackages.filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"pipPackages.filter"}}],["external:envVars.filter",{"id":"external:envVars.filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"envVars.filter"}}],["external:baseDefinitions.map",{"id":"external:baseDefinitions.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"baseDefinitions.map"}}],["symbol-name:updateAptPackage",{"id":"symbol-name:updateAptPackage","kind":"symbol","language":"jsts","symbolName":"updateAptPackage","metadata":{"unresolvedName":"updateAptPackage"}}],["symbol-name:removeAptPackage",{"id":"symbol-name:removeAptPackage","kind":"symbol","language":"jsts","symbolName":"removeAptPackage","metadata":{"unresolvedName":"removeAptPackage"}}],["symbol-name:updateNpmPackage",{"id":"symbol-name:updateNpmPackage","kind":"symbol","language":"jsts","symbolName":"updateNpmPackage","metadata":{"unresolvedName":"updateNpmPackage"}}],["symbol-name:removeNpmPackage",{"id":"symbol-name:removeNpmPackage","kind":"symbol","language":"jsts","symbolName":"removeNpmPackage","metadata":{"unresolvedName":"removeNpmPackage"}}],["symbol-name:updatePipPackage",{"id":"symbol-name:updatePipPackage","kind":"symbol","language":"jsts","symbolName":"updatePipPackage","metadata":{"unresolvedName":"updatePipPackage"}}],["symbol-name:removePipPackage",{"id":"symbol-name:removePipPackage","kind":"symbol","language":"jsts","symbolName":"removePipPackage","metadata":{"unresolvedName":"removePipPackage"}}],["external:envVars.map",{"id":"external:envVars.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"envVars.map"}}],["symbol-name:updateEnvVar",{"id":"symbol-name:updateEnvVar","kind":"symbol","language":"jsts","symbolName":"updateEnvVar","metadata":{"unresolvedName":"updateEnvVar"}}],["symbol-name:removeEnvVar",{"id":"symbol-name:removeEnvVar","kind":"symbol","language":"jsts","symbolName":"removeEnvVar","metadata":{"unresolvedName":"removeEnvVar"}}],["external:buildScripts.map",{"id":"external:buildScripts.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"buildScripts.map"}}],["symbol-name:updateBuildScript",{"id":"symbol-name:updateBuildScript","kind":"symbol","language":"jsts","symbolName":"updateBuildScript","metadata":{"unresolvedName":"updateBuildScript"}}],["symbol-name:removeBuildScript",{"id":"symbol-name:removeBuildScript","kind":"symbol","language":"jsts","symbolName":"removeBuildScript","metadata":{"unresolvedName":"removeBuildScript"}}],["external:startupScripts.map",{"id":"external:startupScripts.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"startupScripts.map"}}],["symbol-name:updateStartupScript",{"id":"symbol-name:updateStartupScript","kind":"symbol","language":"jsts","symbolName":"updateStartupScript","metadata":{"unresolvedName":"updateStartupScript"}}],["symbol-name:removeStartupScript",{"id":"symbol-name:removeStartupScript","kind":"symbol","language":"jsts","symbolName":"removeStartupScript","metadata":{"unresolvedName":"removeStartupScript"}}],["symbol-name:updateMount",{"id":"symbol-name:updateMount","kind":"symbol","language":"jsts","symbolName":"updateMount","metadata":{"unresolvedName":"updateMount"}}],["symbol-name:removeMount",{"id":"symbol-name:removeMount","kind":"symbol","language":"jsts","symbolName":"removeMount","metadata":{"unresolvedName":"removeMount"}}],["external:command.join",{"id":"external:command.join","kind":"external","language":"jsts","metadata":{"unresolvedName":"command.join"}}],["external:e.target.value.split",{"id":"external:e.target.value.split","kind":"external","language":"jsts","metadata":{"unresolvedName":"e.target.value.split"}}],["/workspace/apps/web/src/components/manifest-editor.tsx:",{"id":"/workspace/apps/web/src/components/manifest-editor.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/manifest-editor.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":315,"column":18,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/manifest-editor.tsx:handlePreview",{"id":"/workspace/apps/web/src/components/manifest-editor.tsx:handlePreview","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/manifest-editor.tsx","symbolName":"handlePreview","symbolKind":"function","exported":false,"metadata":{"line":188,"column":24,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/manifest-editor.tsx:updateAptPackage",{"id":"/workspace/apps/web/src/components/manifest-editor.tsx:updateAptPackage","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/manifest-editor.tsx","symbolName":"updateAptPackage","symbolKind":"function","exported":false,"metadata":{"line":212,"column":27,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/manifest-editor.tsx:updateNpmPackage",{"id":"/workspace/apps/web/src/components/manifest-editor.tsx:updateNpmPackage","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/manifest-editor.tsx","symbolName":"updateNpmPackage","symbolKind":"function","exported":false,"metadata":{"line":221,"column":27,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/manifest-editor.tsx:updatePipPackage",{"id":"/workspace/apps/web/src/components/manifest-editor.tsx:updatePipPackage","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/manifest-editor.tsx","symbolName":"updatePipPackage","symbolKind":"function","exported":false,"metadata":{"line":230,"column":27,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/manifest-editor.tsx:updateEnvVar",{"id":"/workspace/apps/web/src/components/manifest-editor.tsx:updateEnvVar","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/manifest-editor.tsx","symbolName":"updateEnvVar","symbolKind":"function","exported":false,"metadata":{"line":239,"column":23,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/manifest-editor.tsx:updateBuildScript",{"id":"/workspace/apps/web/src/components/manifest-editor.tsx:updateBuildScript","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/manifest-editor.tsx","symbolName":"updateBuildScript","symbolKind":"function","exported":false,"metadata":{"line":248,"column":28,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/manifest-editor.tsx:updateStartupScript",{"id":"/workspace/apps/web/src/components/manifest-editor.tsx:updateStartupScript","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/manifest-editor.tsx","symbolName":"updateStartupScript","symbolKind":"function","exported":false,"metadata":{"line":257,"column":30,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/manifest-editor.tsx:updateMount",{"id":"/workspace/apps/web/src/components/manifest-editor.tsx:updateMount","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/manifest-editor.tsx","symbolName":"updateMount","symbolKind":"function","exported":false,"metadata":{"line":280,"column":22,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/mobile-action-sheet.tsx",{"id":"file:/workspace/apps/web/src/components/mobile-action-sheet.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/mobile-action-sheet.tsx","metadata":{"lineCount":89,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/mobile-action-sheet.tsx:MobileActionSheet",{"id":"/workspace/apps/web/src/components/mobile-action-sheet.tsx:MobileActionSheet","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/mobile-action-sheet.tsx","symbolName":"MobileActionSheet","symbolKind":"function","exported":true,"metadata":{"line":20,"column":1,"cyclomaticComplexity":7,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:document.addEventListener",{"id":"external:document.addEventListener","kind":"external","language":"jsts","metadata":{"unresolvedName":"document.addEventListener"}}],["external:document.removeEventListener",{"id":"external:document.removeEventListener","kind":"external","language":"jsts","metadata":{"unresolvedName":"document.removeEventListener"}}],["external:actions.map",{"id":"external:actions.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"actions.map"}}],["external:action.onClick",{"id":"external:action.onClick","kind":"external","language":"jsts","metadata":{"unresolvedName":"action.onClick"}}],["/workspace/apps/web/src/components/mobile-action-sheet.tsx:",{"id":"/workspace/apps/web/src/components/mobile-action-sheet.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/mobile-action-sheet.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":67,"column":24,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/mobile-action-sheet.tsx:handleEscape",{"id":"/workspace/apps/web/src/components/mobile-action-sheet.tsx:handleEscape","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/mobile-action-sheet.tsx","symbolName":"handleEscape","symbolKind":"function","exported":false,"metadata":{"line":40,"column":26,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/mobile-detail-view.tsx",{"id":"file:/workspace/apps/web/src/components/mobile-detail-view.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/mobile-detail-view.tsx","metadata":{"lineCount":96,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/mobile-detail-view.tsx:MobileDetailView",{"id":"/workspace/apps/web/src/components/mobile-detail-view.tsx:MobileDetailView","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/mobile-detail-view.tsx","symbolName":"MobileDetailView","symbolKind":"function","exported":true,"metadata":{"line":18,"column":66,"cyclomaticComplexity":9,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:fields.map",{"id":"external:fields.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"fields.map"}}],["symbol-name:renderValue",{"id":"symbol-name:renderValue","kind":"symbol","language":"jsts","symbolName":"renderValue","metadata":{"unresolvedName":"renderValue"}}],["/workspace/apps/web/src/components/mobile-detail-view.tsx:renderValue",{"id":"/workspace/apps/web/src/components/mobile-detail-view.tsx:renderValue","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/mobile-detail-view.tsx","symbolName":"renderValue","symbolKind":"function","exported":false,"metadata":{"line":26,"column":23,"cyclomaticComplexity":8,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/mobile-edit-view.tsx",{"id":"file:/workspace/apps/web/src/components/mobile-edit-view.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/mobile-edit-view.tsx","metadata":{"lineCount":162,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/mobile-edit-view.tsx:MobileEditView",{"id":"/workspace/apps/web/src/components/mobile-edit-view.tsx:MobileEditView","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/mobile-edit-view.tsx","symbolName":"MobileEditView","symbolKind":"function","exported":true,"metadata":{"line":23,"column":62,"cyclomaticComplexity":18,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:fields?.forEach",{"id":"external:fields?.forEach","kind":"external","language":"jsts","metadata":{"unresolvedName":"fields?.forEach"}}],["external:fields?.map",{"id":"external:fields?.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"fields?.map"}}],["symbol-name:handleChange",{"id":"symbol-name:handleChange","kind":"symbol","language":"jsts","symbolName":"handleChange","metadata":{"unresolvedName":"handleChange"}}],["external:field.options?.map",{"id":"external:field.options?.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"field.options?.map"}}],["symbol-name:Boolean",{"id":"symbol-name:Boolean","kind":"symbol","language":"jsts","symbolName":"Boolean","metadata":{"unresolvedName":"Boolean"}}],["/workspace/apps/web/src/components/mobile-edit-view.tsx:",{"id":"/workspace/apps/web/src/components/mobile-edit-view.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/mobile-edit-view.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":34,"column":23,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/mobile-edit-view.tsx:handleChange",{"id":"/workspace/apps/web/src/components/mobile-edit-view.tsx:handleChange","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/mobile-edit-view.tsx","symbolName":"handleChange","symbolKind":"function","exported":false,"metadata":{"line":41,"column":24,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/mobile-edit-view.tsx:handleSubmit",{"id":"/workspace/apps/web/src/components/mobile-edit-view.tsx:handleSubmit","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/mobile-edit-view.tsx","symbolName":"handleSubmit","symbolKind":"function","exported":false,"metadata":{"line":45,"column":24,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/mobile-fab.tsx",{"id":"file:/workspace/apps/web/src/components/mobile-fab.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/mobile-fab.tsx","metadata":{"lineCount":23,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/mobile-fab.tsx:MobileFAB",{"id":"/workspace/apps/web/src/components/mobile-fab.tsx:MobileFAB","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/mobile-fab.tsx","symbolName":"MobileFAB","symbolKind":"function","exported":true,"metadata":{"line":8,"column":52,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/mobile-list-view.tsx",{"id":"file:/workspace/apps/web/src/components/mobile-list-view.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/mobile-list-view.tsx","metadata":{"lineCount":77,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/mobile-list-view.tsx:MobileListView",{"id":"/workspace/apps/web/src/components/mobile-list-view.tsx:MobileListView","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/mobile-list-view.tsx","symbolName":"MobileListView","symbolKind":"function","exported":true,"metadata":{"line":22,"column":62,"cyclomaticComplexity":5,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:onSearch",{"id":"symbol-name:onSearch","kind":"symbol","language":"jsts","symbolName":"onSearch","metadata":{"unresolvedName":"onSearch"}}],["external:items.map",{"id":"external:items.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"items.map"}}],["symbol-name:onItemClick",{"id":"symbol-name:onItemClick","kind":"symbol","language":"jsts","symbolName":"onItemClick","metadata":{"unresolvedName":"onItemClick"}}],["file:/workspace/apps/web/src/components/mobile-nav.tsx",{"id":"file:/workspace/apps/web/src/components/mobile-nav.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/mobile-nav.tsx","metadata":{"lineCount":88,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/tools-bottom-sheet.tsx",{"id":"file:/workspace/apps/web/src/components/tools-bottom-sheet.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/tools-bottom-sheet.tsx","metadata":{"lineCount":63,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/mobile-nav.tsx:MobileNav",{"id":"/workspace/apps/web/src/components/mobile-nav.tsx:MobileNav","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/mobile-nav.tsx","symbolName":"MobileNav","symbolKind":"function","exported":true,"metadata":{"line":26,"column":52,"cyclomaticComplexity":8,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:setToolsSheetOpen",{"id":"symbol-name:setToolsSheetOpen","kind":"symbol","language":"jsts","symbolName":"setToolsSheetOpen","metadata":{"unresolvedName":"setToolsSheetOpen"}}],["external:MOBILE_NAV_ITEMS.map",{"id":"external:MOBILE_NAV_ITEMS.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"MOBILE_NAV_ITEMS.map"}}],["symbol-name:handleNavClick",{"id":"symbol-name:handleNavClick","kind":"symbol","language":"jsts","symbolName":"handleNavClick","metadata":{"unresolvedName":"handleNavClick"}}],["/workspace/apps/web/src/components/mobile-nav.tsx:handleNavClick",{"id":"/workspace/apps/web/src/components/mobile-nav.tsx:handleNavClick","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/mobile-nav.tsx","symbolName":"handleNavClick","symbolKind":"function","exported":false,"metadata":{"line":34,"column":26,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/mobile-nav.tsx:",{"id":"/workspace/apps/web/src/components/mobile-nav.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/mobile-nav.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":43,"column":31,"cyclomaticComplexity":6,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/mobile-page-header.tsx",{"id":"file:/workspace/apps/web/src/components/mobile-page-header.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/mobile-page-header.tsx","metadata":{"lineCount":34,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/hooks/use-mobile-viewport.ts",{"id":"file:/workspace/apps/web/src/hooks/use-mobile-viewport.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-mobile-viewport.ts","metadata":{"lineCount":22,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/mobile-page-header.tsx:MobilePageHeader",{"id":"/workspace/apps/web/src/components/mobile-page-header.tsx:MobilePageHeader","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/mobile-page-header.tsx","symbolName":"MobilePageHeader","symbolKind":"function","exported":true,"metadata":{"line":11,"column":1,"cyclomaticComplexity":4,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:useMobileViewport",{"id":"symbol-name:useMobileViewport","kind":"symbol","language":"jsts","symbolName":"useMobileViewport","metadata":{"unresolvedName":"useMobileViewport"}}],["file:/workspace/apps/web/src/components/mobile-terminal-header.tsx",{"id":"file:/workspace/apps/web/src/components/mobile-terminal-header.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/mobile-terminal-header.tsx","metadata":{"lineCount":95,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/mobile-terminal-header.tsx:MobileTerminalHeader",{"id":"/workspace/apps/web/src/components/mobile-terminal-header.tsx:MobileTerminalHeader","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/mobile-terminal-header.tsx","symbolName":"MobileTerminalHeader","symbolKind":"function","exported":true,"metadata":{"line":14,"column":74,"cyclomaticComplexity":7,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:onFontSizeChange",{"id":"symbol-name:onFontSizeChange","kind":"symbol","language":"jsts","symbolName":"onFontSizeChange","metadata":{"unresolvedName":"onFontSizeChange"}}],["file:/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx",{"id":"file:/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx","metadata":{"lineCount":117,"featureKind":"library","trustBoundaries":[]}}],["external:@/components/features/terminal/TerminalComponent",{"id":"external:@/components/features/terminal/TerminalComponent","kind":"external","language":"jsts","metadata":{"source":"@/components/features/terminal/TerminalComponent"}}],["file:/workspace/apps/web/src/components/special-keys-strip.tsx",{"id":"file:/workspace/apps/web/src/components/special-keys-strip.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/special-keys-strip.tsx","metadata":{"lineCount":97,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/special-keys-panel.tsx",{"id":"file:/workspace/apps/web/src/components/special-keys-panel.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/special-keys-panel.tsx","metadata":{"lineCount":115,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/hooks/use-virtual-keyboard.ts",{"id":"file:/workspace/apps/web/src/hooks/use-virtual-keyboard.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts","metadata":{"lineCount":69,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/hooks/use-auto-hide.ts",{"id":"file:/workspace/apps/web/src/hooks/use-auto-hide.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-auto-hide.ts","metadata":{"lineCount":69,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/hooks/use-special-keys.ts",{"id":"file:/workspace/apps/web/src/hooks/use-special-keys.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-special-keys.ts","metadata":{"lineCount":141,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx:MobileTerminalWrapper",{"id":"/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx:MobileTerminalWrapper","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx","symbolName":"MobileTerminalWrapper","symbolKind":"function","exported":true,"metadata":{"line":19,"column":76,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:useVirtualKeyboard",{"id":"symbol-name:useVirtualKeyboard","kind":"symbol","language":"jsts","symbolName":"useVirtualKeyboard","metadata":{"unresolvedName":"useVirtualKeyboard"}}],["symbol-name:useAutoHide",{"id":"symbol-name:useAutoHide","kind":"symbol","language":"jsts","symbolName":"useAutoHide","metadata":{"unresolvedName":"useAutoHide"}}],["external:headerAutoHide.toggle",{"id":"external:headerAutoHide.toggle","kind":"external","language":"jsts","metadata":{"unresolvedName":"headerAutoHide.toggle"}}],["symbol-name:setTerminalRef",{"id":"symbol-name:setTerminalRef","kind":"symbol","language":"jsts","symbolName":"setTerminalRef","metadata":{"unresolvedName":"setTerminalRef"}}],["external:terminalRef?.sendData",{"id":"external:terminalRef?.sendData","kind":"external","language":"jsts","metadata":{"unresolvedName":"terminalRef?.sendData"}}],["external:terminalRef?.changeFontSize",{"id":"external:terminalRef?.changeFontSize","kind":"external","language":"jsts","metadata":{"unresolvedName":"terminalRef?.changeFontSize"}}],["symbol-name:setShowPanel",{"id":"symbol-name:setShowPanel","kind":"symbol","language":"jsts","symbolName":"setShowPanel","metadata":{"unresolvedName":"setShowPanel"}}],["external:terminalRef?.focusInput",{"id":"external:terminalRef?.focusInput","kind":"external","language":"jsts","metadata":{"unresolvedName":"terminalRef?.focusInput"}}],["/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx:",{"id":"/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":52,"column":5,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/notification-center.test.tsx",{"id":"file:/workspace/apps/web/src/components/notification-center.test.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/notification-center.test.tsx","metadata":{"lineCount":178,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/notification-center.tsx",{"id":"file:/workspace/apps/web/src/components/notification-center.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/notification-center.tsx","metadata":{"lineCount":135,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/state/notifications.tsx",{"id":"file:/workspace/apps/web/src/state/notifications.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/state/notifications.tsx","metadata":{"lineCount":308,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/notification-center.test.tsx:wrapper",{"id":"/workspace/apps/web/src/components/notification-center.test.tsx:wrapper","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/notification-center.test.tsx","symbolName":"wrapper","symbolKind":"function","exported":false,"metadata":{"line":36,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/notification-center.test.tsx:",{"id":"/workspace/apps/web/src/components/notification-center.test.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/notification-center.test.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":170,"column":43,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:vi.useFakeTimers",{"id":"external:vi.useFakeTimers","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.useFakeTimers"}}],["external:mockedGetNotifications.mockResolvedValue",{"id":"external:mockedGetNotifications.mockResolvedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockedGetNotifications.mockResolvedValue"}}],["external:mockedGetUnreadCount.mockResolvedValue",{"id":"external:mockedGetUnreadCount.mockResolvedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockedGetUnreadCount.mockResolvedValue"}}],["external:vi.useRealTimers",{"id":"external:vi.useRealTimers","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.useRealTimers"}}],["external:vi.advanceTimersByTimeAsync",{"id":"external:vi.advanceTimersByTimeAsync","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.advanceTimersByTimeAsync"}}],["external:expect(screen.getByText(\"3\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"3\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"3\")).toBeInTheDocument"}}],["external:expect(screen.queryByText(\"0\")).not.toBeInTheDocument",{"id":"external:expect(screen.queryByText(\"0\")).not.toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.queryByText(\"0\")).not.toBeInTheDocument"}}],["external:screen.queryByText",{"id":"external:screen.queryByText","kind":"external","language":"jsts","metadata":{"unresolvedName":"screen.queryByText"}}],["external:expect(screen.getByRole(\"dialog\")).toBeInTheDocument",{"id":"external:expect(screen.getByRole(\"dialog\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByRole(\"dialog\")).toBeInTheDocument"}}],["external:fireEvent.mouseDown",{"id":"external:fireEvent.mouseDown","kind":"external","language":"jsts","metadata":{"unresolvedName":"fireEvent.mouseDown"}}],["external:screen.getByTestId",{"id":"external:screen.getByTestId","kind":"external","language":"jsts","metadata":{"unresolvedName":"screen.getByTestId"}}],["external:expect(screen.queryByRole(\"dialog\")).not.toBeInTheDocument",{"id":"external:expect(screen.queryByRole(\"dialog\")).not.toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.queryByRole(\"dialog\")).not.toBeInTheDocument"}}],["external:screen.queryByRole",{"id":"external:screen.queryByRole","kind":"external","language":"jsts","metadata":{"unresolvedName":"screen.queryByRole"}}],["external:fireEvent.keyDown",{"id":"external:fireEvent.keyDown","kind":"external","language":"jsts","metadata":{"unresolvedName":"fireEvent.keyDown"}}],["external:expect(screen.getByText(\"No notifications\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"No notifications\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"No notifications\")).toBeInTheDocument"}}],["symbol-name:makeNotification",{"id":"symbol-name:makeNotification","kind":"symbol","language":"jsts","symbolName":"makeNotification","metadata":{"unresolvedName":"makeNotification"}}],["external:expect(screen.getByText(\"Notification 1\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"Notification 1\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"Notification 1\")).toBeInTheDocument"}}],["external:expect(screen.getByText(\"Notification 2\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"Notification 2\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"Notification 2\")).toBeInTheDocument"}}],["symbol-name:import",{"id":"symbol-name:import","kind":"symbol","language":"jsts","symbolName":"import","metadata":{"unresolvedName":"import"}}],["external:expect(vi.mocked(mockMarkAll)).toHaveBeenCalled",{"id":"external:expect(vi.mocked(mockMarkAll)).toHaveBeenCalled","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(vi.mocked(mockMarkAll)).toHaveBeenCalled"}}],["external:vi.mocked",{"id":"external:vi.mocked","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.mocked"}}],["external:expect(vi.mocked(mockClearAll)).toHaveBeenCalled",{"id":"external:expect(vi.mocked(mockClearAll)).toHaveBeenCalled","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(vi.mocked(mockClearAll)).toHaveBeenCalled"}}],["external:expect(mockedGetNotifications).toHaveBeenCalled",{"id":"external:expect(mockedGetNotifications).toHaveBeenCalled","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(mockedGetNotifications).toHaveBeenCalled"}}],["file:/workspace/apps/web/src/hooks/use-notifications.ts",{"id":"file:/workspace/apps/web/src/hooks/use-notifications.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-notifications.ts","metadata":{"lineCount":13,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/notification-item.tsx",{"id":"file:/workspace/apps/web/src/components/notification-item.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/notification-item.tsx","metadata":{"lineCount":65,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/notification-center.tsx:NotificationCenter",{"id":"/workspace/apps/web/src/components/notification-center.tsx:NotificationCenter","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/notification-center.tsx","symbolName":"NotificationCenter","symbolKind":"function","exported":true,"metadata":{"line":10,"column":1,"cyclomaticComplexity":12,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:useNotifications",{"id":"symbol-name:useNotifications","kind":"symbol","language":"jsts","symbolName":"useNotifications","metadata":{"unresolvedName":"useNotifications"}}],["external:dropdownRef.current.contains",{"id":"external:dropdownRef.current.contains","kind":"external","language":"jsts","metadata":{"unresolvedName":"dropdownRef.current.contains"}}],["symbol-name:setIsDropdownOpen",{"id":"symbol-name:setIsDropdownOpen","kind":"symbol","language":"jsts","symbolName":"setIsDropdownOpen","metadata":{"unresolvedName":"setIsDropdownOpen"}}],["symbol-name:refreshList",{"id":"symbol-name:refreshList","kind":"symbol","language":"jsts","symbolName":"refreshList","metadata":{"unresolvedName":"refreshList"}}],["external:notifications.map",{"id":"external:notifications.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"notifications.map"}}],["symbol-name:markAllRead",{"id":"symbol-name:markAllRead","kind":"symbol","language":"jsts","symbolName":"markAllRead","metadata":{"unresolvedName":"markAllRead"}}],["symbol-name:clearAll",{"id":"symbol-name:clearAll","kind":"symbol","language":"jsts","symbolName":"clearAll","metadata":{"unresolvedName":"clearAll"}}],["/workspace/apps/web/src/components/notification-center.tsx:",{"id":"/workspace/apps/web/src/components/notification-center.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/notification-center.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":122,"column":18,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/notification-center.tsx:handleMouseDown",{"id":"/workspace/apps/web/src/components/notification-center.tsx:handleMouseDown","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/notification-center.tsx","symbolName":"handleMouseDown","symbolKind":"function","exported":false,"metadata":{"line":30,"column":27,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/notification-center.tsx:handleKeyDown",{"id":"/workspace/apps/web/src/components/notification-center.tsx:handleKeyDown","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/notification-center.tsx","symbolName":"handleKeyDown","symbolKind":"function","exported":false,"metadata":{"line":39,"column":25,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/notification-item.test.tsx",{"id":"file:/workspace/apps/web/src/components/notification-item.test.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/notification-item.test.tsx","metadata":{"lineCount":105,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/notification-item.test.tsx:",{"id":"/workspace/apps/web/src/components/notification-item.test.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/notification-item.test.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":93,"column":31,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:expect(screen.getByText(\"Container started\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"Container started\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"Container started\")).toBeInTheDocument"}}],["external:expect(screen.getByText(/ago|just now/)).toBeInTheDocument",{"id":"external:expect(screen.getByText(/ago|just now/)).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(/ago|just now/)).toBeInTheDocument"}}],["external:expect(row.className).toContain",{"id":"external:expect(row.className).toContain","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(row.className).toContain"}}],["external:expect(onMarkRead).toHaveBeenCalledWith",{"id":"external:expect(onMarkRead).toHaveBeenCalledWith","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(onMarkRead).toHaveBeenCalledWith"}}],["external:expect(onDismiss).toHaveBeenCalledWith",{"id":"external:expect(onDismiss).toHaveBeenCalledWith","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(onDismiss).toHaveBeenCalledWith"}}],["external:expect(screen.getByRole(\"img\", { hidden: true })).toBeInTheDocument",{"id":"external:expect(screen.getByRole(\"img\", { hidden: true })).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByRole(\"img\", { hidden: true })).toBeInTheDocument"}}],["file:/workspace/apps/web/src/utils/time.ts",{"id":"file:/workspace/apps/web/src/utils/time.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/utils/time.ts","metadata":{"lineCount":30,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/notification-item.tsx:NotificationItem",{"id":"/workspace/apps/web/src/components/notification-item.tsx:NotificationItem","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/notification-item.tsx","symbolName":"NotificationItem","symbolKind":"function","exported":true,"metadata":{"line":20,"column":1,"cyclomaticComplexity":4,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:formatRelativeTime",{"id":"symbol-name:formatRelativeTime","kind":"symbol","language":"jsts","symbolName":"formatRelativeTime","metadata":{"unresolvedName":"formatRelativeTime"}}],["symbol-name:onMarkRead",{"id":"symbol-name:onMarkRead","kind":"symbol","language":"jsts","symbolName":"onMarkRead","metadata":{"unresolvedName":"onMarkRead"}}],["symbol-name:onDismiss",{"id":"symbol-name:onDismiss","kind":"symbol","language":"jsts","symbolName":"onDismiss","metadata":{"unresolvedName":"onDismiss"}}],["file:/workspace/apps/web/src/components/session-card.tsx",{"id":"file:/workspace/apps/web/src/components/session-card.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/session-card.tsx","metadata":{"lineCount":369,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/components/session-card.tsx:SessionCard",{"id":"/workspace/apps/web/src/components/session-card.tsx:SessionCard","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/session-card.tsx","symbolName":"SessionCard","symbolKind":"function","exported":true,"metadata":{"line":39,"column":1,"cyclomaticComplexity":40,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["external:session.tool_type_interfaces?.includes",{"id":"external:session.tool_type_interfaces?.includes","kind":"external","language":"jsts","metadata":{"unresolvedName":"session.tool_type_interfaces?.includes"}}],["symbol-name:setShowStopConfirm",{"id":"symbol-name:setShowStopConfirm","kind":"symbol","language":"jsts","symbolName":"setShowStopConfirm","metadata":{"unresolvedName":"setShowStopConfirm"}}],["symbol-name:setShowDeleteConfirm",{"id":"symbol-name:setShowDeleteConfirm","kind":"symbol","language":"jsts","symbolName":"setShowDeleteConfirm","metadata":{"unresolvedName":"setShowDeleteConfirm"}}],["external:new Date(session.created_at).toLocaleString",{"id":"external:new Date(session.created_at).toLocaleString","kind":"external","language":"jsts","metadata":{"unresolvedName":"new Date(session.created_at).toLocaleString"}}],["symbol-name:setShowActionSheet",{"id":"symbol-name:setShowActionSheet","kind":"symbol","language":"jsts","symbolName":"setShowActionSheet","metadata":{"unresolvedName":"setShowActionSheet"}}],["symbol-name:onStart",{"id":"symbol-name:onStart","kind":"symbol","language":"jsts","symbolName":"onStart","metadata":{"unresolvedName":"onStart"}}],["/workspace/apps/web/src/components/session-card.tsx:handleStop",{"id":"/workspace/apps/web/src/components/session-card.tsx:handleStop","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/session-card.tsx","symbolName":"handleStop","symbolKind":"function","exported":false,"metadata":{"line":71,"column":21,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/components/session-card.tsx:handleDelete",{"id":"/workspace/apps/web/src/components/session-card.tsx:handleDelete","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/session-card.tsx","symbolName":"handleDelete","symbolKind":"function","exported":false,"metadata":{"line":80,"column":23,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["file:/workspace/apps/web/src/components/session-list.tsx",{"id":"file:/workspace/apps/web/src/components/session-list.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/session-list.tsx","metadata":{"lineCount":126,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/components/session-list.tsx:SessionList",{"id":"/workspace/apps/web/src/components/session-list.tsx:SessionList","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/session-list.tsx","symbolName":"SessionList","symbolKind":"function","exported":true,"metadata":{"line":24,"column":1,"cyclomaticComplexity":9,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["external:activeStatuses.includes",{"id":"external:activeStatuses.includes","kind":"external","language":"jsts","metadata":{"unresolvedName":"activeStatuses.includes"}}],["external:sessions\n .filter((s) => recentStatuses.includes(s.status))\n .slice",{"id":"external:sessions\n .filter((s) => recentStatuses.includes(s.status))\n .slice","kind":"external","language":"jsts","metadata":{"unresolvedName":"sessions\n .filter((s) => recentStatuses.includes(s.status))\n .slice"}}],["external:sessions\n .filter",{"id":"external:sessions\n .filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"sessions\n .filter"}}],["external:recentStatuses.includes",{"id":"external:recentStatuses.includes","kind":"external","language":"jsts","metadata":{"unresolvedName":"recentStatuses.includes"}}],["external:activeSessions.map",{"id":"external:activeSessions.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"activeSessions.map"}}],["external:recentSessions.map",{"id":"external:recentSessions.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"recentSessions.map"}}],["/workspace/apps/web/src/components/special-keys-panel.tsx:SpecialKeysPanel",{"id":"/workspace/apps/web/src/components/special-keys-panel.tsx:SpecialKeysPanel","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/special-keys-panel.tsx","symbolName":"SpecialKeysPanel","symbolKind":"function","exported":true,"metadata":{"line":38,"column":66,"cyclomaticComplexity":4,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:getSequenceWithModifier",{"id":"symbol-name:getSequenceWithModifier","kind":"symbol","language":"jsts","symbolName":"getSequenceWithModifier","metadata":{"unresolvedName":"getSequenceWithModifier"}}],["symbol-name:onSend",{"id":"symbol-name:onSend","kind":"symbol","language":"jsts","symbolName":"onSend","metadata":{"unresolvedName":"onSend"}}],["symbol-name:onModifierChange",{"id":"symbol-name:onModifierChange","kind":"symbol","language":"jsts","symbolName":"onModifierChange","metadata":{"unresolvedName":"onModifierChange"}}],["symbol-name:requestAnimationFrame",{"id":"symbol-name:requestAnimationFrame","kind":"symbol","language":"jsts","symbolName":"requestAnimationFrame","metadata":{"unresolvedName":"requestAnimationFrame"}}],["symbol-name:onKeepFocus",{"id":"symbol-name:onKeepFocus","kind":"symbol","language":"jsts","symbolName":"onKeepFocus","metadata":{"unresolvedName":"onKeepFocus"}}],["external:EXPANDED_KEYS.map",{"id":"external:EXPANDED_KEYS.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"EXPANDED_KEYS.map"}}],["symbol-name:handlePointerDown",{"id":"symbol-name:handlePointerDown","kind":"symbol","language":"jsts","symbolName":"handlePointerDown","metadata":{"unresolvedName":"handlePointerDown"}}],["external:F_KEYS.map",{"id":"external:F_KEYS.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"F_KEYS.map"}}],["/workspace/apps/web/src/components/special-keys-panel.tsx:handlePointerDown",{"id":"/workspace/apps/web/src/components/special-keys-panel.tsx:handlePointerDown","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/special-keys-panel.tsx","symbolName":"handlePointerDown","symbolKind":"function","exported":false,"metadata":{"line":48,"column":29,"cyclomaticComplexity":3,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/special-keys-panel.tsx:",{"id":"/workspace/apps/web/src/components/special-keys-panel.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/special-keys-panel.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":70,"column":27,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/special-keys-panel.tsx:handleOverlayPointerDown",{"id":"/workspace/apps/web/src/components/special-keys-panel.tsx:handleOverlayPointerDown","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/special-keys-panel.tsx","symbolName":"handleOverlayPointerDown","symbolKind":"function","exported":false,"metadata":{"line":66,"column":36,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/special-keys-strip.tsx:SpecialKeysStrip",{"id":"/workspace/apps/web/src/components/special-keys-strip.tsx:SpecialKeysStrip","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/special-keys-strip.tsx","symbolName":"SpecialKeysStrip","symbolKind":"function","exported":true,"metadata":{"line":24,"column":66,"cyclomaticComplexity":11,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:onMoreClick",{"id":"symbol-name:onMoreClick","kind":"symbol","language":"jsts","symbolName":"onMoreClick","metadata":{"unresolvedName":"onMoreClick"}}],["external:PRIMARY_KEYS.map",{"id":"external:PRIMARY_KEYS.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"PRIMARY_KEYS.map"}}],["/workspace/apps/web/src/components/special-keys-strip.tsx:handlePointerDown",{"id":"/workspace/apps/web/src/components/special-keys-strip.tsx:handlePointerDown","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/special-keys-strip.tsx","symbolName":"handlePointerDown","symbolKind":"function","exported":false,"metadata":{"line":32,"column":29,"cyclomaticComplexity":6,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/special-keys-strip.tsx:",{"id":"/workspace/apps/web/src/components/special-keys-strip.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/special-keys-strip.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":61,"column":27,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/special-keys-strip.tsx:handleMorePointerDown",{"id":"/workspace/apps/web/src/components/special-keys-strip.tsx:handleMorePointerDown","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/special-keys-strip.tsx","symbolName":"handleMorePointerDown","symbolKind":"function","exported":false,"metadata":{"line":58,"column":33,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["file:/workspace/apps/web/src/components/start-tool-fab.tsx",{"id":"file:/workspace/apps/web/src/components/start-tool-fab.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/start-tool-fab.tsx","metadata":{"lineCount":119,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/tool-starter.tsx",{"id":"file:/workspace/apps/web/src/components/tool-starter.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/tool-starter.tsx","metadata":{"lineCount":300,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/start-tool-fab.tsx:StartToolFAB",{"id":"/workspace/apps/web/src/components/start-tool-fab.tsx:StartToolFAB","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/start-tool-fab.tsx","symbolName":"StartToolFAB","symbolKind":"function","exported":true,"metadata":{"line":9,"column":1,"cyclomaticComplexity":7,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:setOpen",{"id":"symbol-name:setOpen","kind":"symbol","language":"jsts","symbolName":"setOpen","metadata":{"unresolvedName":"setOpen"}}],["symbol-name:setWorkspacesLoading",{"id":"symbol-name:setWorkspacesLoading","kind":"symbol","language":"jsts","symbolName":"setWorkspacesLoading","metadata":{"unresolvedName":"setWorkspacesLoading"}}],["symbol-name:listAllWorkspaces",{"id":"symbol-name:listAllWorkspaces","kind":"symbol","language":"jsts","symbolName":"listAllWorkspaces","metadata":{"unresolvedName":"listAllWorkspaces"}}],["symbol-name:setWorkspaces",{"id":"symbol-name:setWorkspaces","kind":"symbol","language":"jsts","symbolName":"setWorkspaces","metadata":{"unresolvedName":"setWorkspaces"}}],["symbol-name:setSelectedWorkspace",{"id":"symbol-name:setSelectedWorkspace","kind":"symbol","language":"jsts","symbolName":"setSelectedWorkspace","metadata":{"unresolvedName":"setSelectedWorkspace"}}],["external:workspaces.find",{"id":"external:workspaces.find","kind":"external","language":"jsts","metadata":{"unresolvedName":"workspaces.find"}}],["external:workspaces.map",{"id":"external:workspaces.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"workspaces.map"}}],["/workspace/apps/web/src/components/start-tool-fab.tsx:handleOpen",{"id":"/workspace/apps/web/src/components/start-tool-fab.tsx:handleOpen","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/start-tool-fab.tsx","symbolName":"handleOpen","symbolKind":"function","exported":false,"metadata":{"line":17,"column":21,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/start-tool-fab.tsx:handleClose",{"id":"/workspace/apps/web/src/components/start-tool-fab.tsx:handleClose","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/start-tool-fab.tsx","symbolName":"handleClose","symbolKind":"function","exported":false,"metadata":{"line":30,"column":22,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/start-tool-fab.tsx:",{"id":"/workspace/apps/web/src/components/start-tool-fab.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/start-tool-fab.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":78,"column":20,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/start-tool-modal.tsx",{"id":"file:/workspace/apps/web/src/components/start-tool-modal.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/start-tool-modal.tsx","metadata":{"lineCount":115,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/hooks/use-async-data.ts",{"id":"file:/workspace/apps/web/src/hooks/use-async-data.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-async-data.ts","metadata":{"lineCount":43,"featureKind":"service","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/components/start-tool-modal.tsx:StartToolModal",{"id":"/workspace/apps/web/src/components/start-tool-modal.tsx:StartToolModal","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/start-tool-modal.tsx","symbolName":"StartToolModal","symbolKind":"function","exported":true,"metadata":{"line":15,"column":1,"cyclomaticComplexity":11,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:useAsyncData",{"id":"symbol-name:useAsyncData","kind":"symbol","language":"jsts","symbolName":"useAsyncData","metadata":{"unresolvedName":"useAsyncData"}}],["symbol-name:setSubmitting",{"id":"symbol-name:setSubmitting","kind":"symbol","language":"jsts","symbolName":"setSubmitting","metadata":{"unresolvedName":"setSubmitting"}}],["symbol-name:setToolTypeId",{"id":"symbol-name:setToolTypeId","kind":"symbol","language":"jsts","symbolName":"setToolTypeId","metadata":{"unresolvedName":"setToolTypeId"}}],["external:toolTypes?.map",{"id":"external:toolTypes?.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"toolTypes?.map"}}],["symbol-name:setConfigProfileId",{"id":"symbol-name:setConfigProfileId","kind":"symbol","language":"jsts","symbolName":"setConfigProfileId","metadata":{"unresolvedName":"setConfigProfileId"}}],["/workspace/apps/web/src/components/start-tool-modal.tsx:handleSubmit",{"id":"/workspace/apps/web/src/components/start-tool-modal.tsx:handleSubmit","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/start-tool-modal.tsx","symbolName":"handleSubmit","symbolKind":"function","exported":false,"metadata":{"line":31,"column":23,"cyclomaticComplexity":5,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/terminal-session-tabs.test.tsx",{"id":"file:/workspace/apps/web/src/components/terminal-session-tabs.test.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx","metadata":{"lineCount":162,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["file:/workspace/apps/web/src/components/terminal-session-tabs.tsx",{"id":"file:/workspace/apps/web/src/components/terminal-session-tabs.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/terminal-session-tabs.tsx","metadata":{"lineCount":168,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:",{"id":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":143,"column":46,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["external:expect(screen.getByText(\"Session 1\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"Session 1\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"Session 1\")).toBeInTheDocument"}}],["external:expect(screen.getByText(\"Session 2\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"Session 2\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"Session 2\")).toBeInTheDocument"}}],["external:expect(screen.getByText(\"Session 3\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"Session 3\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"Session 3\")).toBeInTheDocument"}}],["external:expect(screen.getByRole(\"button\", { name: /new session/i })).toBeInTheDocument",{"id":"external:expect(screen.getByRole(\"button\", { name: /new session/i })).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByRole(\"button\", { name: /new session/i })).toBeInTheDocument"}}],["external:expect(onSelect).toHaveBeenCalledWith",{"id":"external:expect(onSelect).toHaveBeenCalledWith","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(onSelect).toHaveBeenCalledWith"}}],["external:screen.getByLabelText",{"id":"external:screen.getByLabelText","kind":"external","language":"jsts","metadata":{"unresolvedName":"screen.getByLabelText"}}],["external:expect(screen.getByText(\"Close?\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"Close?\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"Close?\")).toBeInTheDocument"}}],["external:expect(onClose).toHaveBeenCalledWith",{"id":"external:expect(onClose).toHaveBeenCalledWith","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(onClose).toHaveBeenCalledWith"}}],["external:fireEvent.doubleClick",{"id":"external:fireEvent.doubleClick","kind":"external","language":"jsts","metadata":{"unresolvedName":"fireEvent.doubleClick"}}],["external:expect(input).toBeInTheDocument",{"id":"external:expect(input).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(input).toBeInTheDocument"}}],["external:expect(onRename).toHaveBeenCalledWith",{"id":"external:expect(onRename).toHaveBeenCalledWith","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(onRename).toHaveBeenCalledWith"}}],["external:expect(onRename).not.toHaveBeenCalled",{"id":"external:expect(onRename).not.toHaveBeenCalled","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(onRename).not.toHaveBeenCalled"}}],["external:Array.from",{"id":"external:Array.from","kind":"external","language":"jsts","metadata":{"unresolvedName":"Array.from"}}],["external:expect(newButton).toBeDisabled",{"id":"external:expect(newButton).toBeDisabled","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(newButton).toBeDisabled"}}],["external:screen.getAllByRole",{"id":"external:screen.getAllByRole","kind":"external","language":"jsts","metadata":{"unresolvedName":"screen.getAllByRole"}}],["external:expect(tabs).toHaveLength",{"id":"external:expect(tabs).toHaveLength","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(tabs).toHaveLength"}}],["external:expect(tabs[0].querySelector(\".connected\")).toBeInTheDocument",{"id":"external:expect(tabs[0].querySelector(\".connected\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(tabs[0].querySelector(\".connected\")).toBeInTheDocument"}}],["external:tabs[0].querySelector",{"id":"external:tabs[0].querySelector","kind":"external","language":"jsts","metadata":{"unresolvedName":"tabs[0].querySelector"}}],["external:expect(tabs[1].querySelector(\".connecting\")).toBeInTheDocument",{"id":"external:expect(tabs[1].querySelector(\".connecting\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(tabs[1].querySelector(\".connecting\")).toBeInTheDocument"}}],["external:tabs[1].querySelector",{"id":"external:tabs[1].querySelector","kind":"external","language":"jsts","metadata":{"unresolvedName":"tabs[1].querySelector"}}],["external:expect(tabs[2].querySelector(\".disconnected\")).toBeInTheDocument",{"id":"external:expect(tabs[2].querySelector(\".disconnected\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(tabs[2].querySelector(\".disconnected\")).toBeInTheDocument"}}],["external:tabs[2].querySelector",{"id":"external:tabs[2].querySelector","kind":"external","language":"jsts","metadata":{"unresolvedName":"tabs[2].querySelector"}}],["/workspace/apps/web/src/components/terminal-session-tabs.tsx:TerminalSessionTabs",{"id":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:TerminalSessionTabs","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/terminal-session-tabs.tsx","symbolName":"TerminalSessionTabs","symbolKind":"function","exported":true,"metadata":{"line":19,"column":72,"cyclomaticComplexity":13,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["symbol-name:setRenamingId",{"id":"symbol-name:setRenamingId","kind":"symbol","language":"jsts","symbolName":"setRenamingId","metadata":{"unresolvedName":"setRenamingId"}}],["symbol-name:setRenameValue",{"id":"symbol-name:setRenameValue","kind":"symbol","language":"jsts","symbolName":"setRenameValue","metadata":{"unresolvedName":"setRenameValue"}}],["external:renameInputRef.current?.focus",{"id":"external:renameInputRef.current?.focus","kind":"external","language":"jsts","metadata":{"unresolvedName":"renameInputRef.current?.focus"}}],["external:renameInputRef.current?.select",{"id":"external:renameInputRef.current?.select","kind":"external","language":"jsts","metadata":{"unresolvedName":"renameInputRef.current?.select"}}],["external:renameValue.trim",{"id":"external:renameValue.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"renameValue.trim"}}],["symbol-name:onRename",{"id":"symbol-name:onRename","kind":"symbol","language":"jsts","symbolName":"onRename","metadata":{"unresolvedName":"onRename"}}],["symbol-name:commitRename",{"id":"symbol-name:commitRename","kind":"symbol","language":"jsts","symbolName":"commitRename","metadata":{"unresolvedName":"commitRename"}}],["symbol-name:cancelRename",{"id":"symbol-name:cancelRename","kind":"symbol","language":"jsts","symbolName":"cancelRename","metadata":{"unresolvedName":"cancelRename"}}],["symbol-name:setConfirmCloseId",{"id":"symbol-name:setConfirmCloseId","kind":"symbol","language":"jsts","symbolName":"setConfirmCloseId","metadata":{"unresolvedName":"setConfirmCloseId"}}],["symbol-name:onSelect",{"id":"symbol-name:onSelect","kind":"symbol","language":"jsts","symbolName":"onSelect","metadata":{"unresolvedName":"onSelect"}}],["symbol-name:handleDoubleClick",{"id":"symbol-name:handleDoubleClick","kind":"symbol","language":"jsts","symbolName":"handleDoubleClick","metadata":{"unresolvedName":"handleDoubleClick"}}],["symbol-name:handleCloseClick",{"id":"symbol-name:handleCloseClick","kind":"symbol","language":"jsts","symbolName":"handleCloseClick","metadata":{"unresolvedName":"handleCloseClick"}}],["/workspace/apps/web/src/components/terminal-session-tabs.tsx:",{"id":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/terminal-session-tabs.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":131,"column":18,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["file:/workspace/apps/web/src/components/toast-rules.test.ts",{"id":"file:/workspace/apps/web/src/components/toast-rules.test.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/toast-rules.test.ts","metadata":{"lineCount":70,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/toast-rules.test.ts:makeEvent",{"id":"/workspace/apps/web/src/components/toast-rules.test.ts:makeEvent","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/toast-rules.test.ts","symbolName":"makeEvent","symbolKind":"function","exported":false,"metadata":{"line":5,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/toast-rules.test.ts:",{"id":"/workspace/apps/web/src/components/toast-rules.test.ts:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/toast-rules.test.ts","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":66,"column":41,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:expect(mapEventToCategory(makeEvent(\"instance.started\"))).toBe",{"id":"external:expect(mapEventToCategory(makeEvent(\"instance.started\"))).toBe","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(mapEventToCategory(makeEvent(\"instance.started\"))).toBe"}}],["external:expect(mapEventToCategory(makeEvent(\"instance.error\"))).toBe",{"id":"external:expect(mapEventToCategory(makeEvent(\"instance.error\"))).toBe","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(mapEventToCategory(makeEvent(\"instance.error\"))).toBe"}}],["external:expect(mapEventToCategory(makeEvent(\"health.error\"))).toBe",{"id":"external:expect(mapEventToCategory(makeEvent(\"health.error\"))).toBe","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(mapEventToCategory(makeEvent(\"health.error\"))).toBe"}}],["external:expect(mapEventToCategory(makeEvent(\"system.announcement\"))).toBe",{"id":"external:expect(mapEventToCategory(makeEvent(\"system.announcement\"))).toBe","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(mapEventToCategory(makeEvent(\"system.announcement\"))).toBe"}}],["external:expect(mapEventToSeverity(makeEvent(\"instance.error\"))).toBe",{"id":"external:expect(mapEventToSeverity(makeEvent(\"instance.error\"))).toBe","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(mapEventToSeverity(makeEvent(\"instance.error\"))).toBe"}}],["external:expect(mapEventToSeverity(makeEvent(\"health.error\"))).toBe",{"id":"external:expect(mapEventToSeverity(makeEvent(\"health.error\"))).toBe","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(mapEventToSeverity(makeEvent(\"health.error\"))).toBe"}}],["external:expect(mapEventToSeverity(makeEvent(\"instance.created\"))).toBe",{"id":"external:expect(mapEventToSeverity(makeEvent(\"instance.created\"))).toBe","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(mapEventToSeverity(makeEvent(\"instance.created\"))).toBe"}}],["external:expect(mapEventToSeverity(makeEvent(\"instance.started\"))).toBe",{"id":"external:expect(mapEventToSeverity(makeEvent(\"instance.started\"))).toBe","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(mapEventToSeverity(makeEvent(\"instance.started\"))).toBe"}}],["external:expect(mapEventToSeverity(makeEvent(\"instance.stopped\"))).toBe",{"id":"external:expect(mapEventToSeverity(makeEvent(\"instance.stopped\"))).toBe","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(mapEventToSeverity(makeEvent(\"instance.stopped\"))).toBe"}}],["external:expect(mapEventToSeverity(makeEvent(\"instance.restarted\"))).toBe",{"id":"external:expect(mapEventToSeverity(makeEvent(\"instance.restarted\"))).toBe","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(mapEventToSeverity(makeEvent(\"instance.restarted\"))).toBe"}}],["external:expect(mapEventToSeverity(makeEvent(\"instance.deleted\"))).toBe",{"id":"external:expect(mapEventToSeverity(makeEvent(\"instance.deleted\"))).toBe","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(mapEventToSeverity(makeEvent(\"instance.deleted\"))).toBe"}}],["external:expect(mapEventToSeverity(makeEvent(\"unknown.event\"))).toBe",{"id":"external:expect(mapEventToSeverity(makeEvent(\"unknown.event\"))).toBe","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(mapEventToSeverity(makeEvent(\"unknown.event\"))).toBe"}}],["file:/workspace/apps/web/src/state/toast.tsx",{"id":"file:/workspace/apps/web/src/state/toast.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/state/toast.tsx","metadata":{"lineCount":207,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/toast-rules.ts:makeDedupKey",{"id":"/workspace/apps/web/src/components/toast-rules.ts:makeDedupKey","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/toast-rules.ts","symbolName":"makeDedupKey","symbolKind":"function","exported":false,"metadata":{"line":7,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/toast-rules.ts:shouldShowToast",{"id":"/workspace/apps/web/src/components/toast-rules.ts:shouldShowToast","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/toast-rules.ts","symbolName":"shouldShowToast","symbolKind":"function","exported":false,"metadata":{"line":11,"column":1,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:makeDedupKey",{"id":"symbol-name:makeDedupKey","kind":"symbol","language":"jsts","symbolName":"makeDedupKey","metadata":{"unresolvedName":"makeDedupKey"}}],["external:lastToastTime.get",{"id":"external:lastToastTime.get","kind":"external","language":"jsts","metadata":{"unresolvedName":"lastToastTime.get"}}],["external:lastToastTime.set",{"id":"external:lastToastTime.set","kind":"external","language":"jsts","metadata":{"unresolvedName":"lastToastTime.set"}}],["/workspace/apps/web/src/components/toast-rules.ts:mapEventToCategory",{"id":"/workspace/apps/web/src/components/toast-rules.ts:mapEventToCategory","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/toast-rules.ts","symbolName":"mapEventToCategory","symbolKind":"function","exported":true,"metadata":{"line":22,"column":1,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:event.event.startsWith",{"id":"external:event.event.startsWith","kind":"external","language":"jsts","metadata":{"unresolvedName":"event.event.startsWith"}}],["/workspace/apps/web/src/components/toast-rules.ts:mapEventToSeverity",{"id":"/workspace/apps/web/src/components/toast-rules.ts:mapEventToSeverity","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/toast-rules.ts","symbolName":"mapEventToSeverity","symbolKind":"function","exported":true,"metadata":{"line":28,"column":1,"cyclomaticComplexity":10,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/toast-rules.ts:handleEventToast",{"id":"/workspace/apps/web/src/components/toast-rules.ts:handleEventToast","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/toast-rules.ts","symbolName":"handleEventToast","symbolKind":"function","exported":true,"metadata":{"line":48,"column":1,"cyclomaticComplexity":12,"maxNestingDepth":3,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:shouldShowToast",{"id":"symbol-name:shouldShowToast","kind":"symbol","language":"jsts","symbolName":"shouldShowToast","metadata":{"unresolvedName":"shouldShowToast"}}],["external:toast.info",{"id":"external:toast.info","kind":"external","language":"jsts","metadata":{"unresolvedName":"toast.info"}}],["external:toast.success",{"id":"external:toast.success","kind":"external","language":"jsts","metadata":{"unresolvedName":"toast.success"}}],["external:toast.warning",{"id":"external:toast.warning","kind":"external","language":"jsts","metadata":{"unresolvedName":"toast.warning"}}],["external:toast.error",{"id":"external:toast.error","kind":"external","language":"jsts","metadata":{"unresolvedName":"toast.error"}}],["/workspace/apps/web/src/components/toast-rules.ts:clearToastDedup",{"id":"/workspace/apps/web/src/components/toast-rules.ts:clearToastDedup","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/toast-rules.ts","symbolName":"clearToastDedup","symbolKind":"function","exported":true,"metadata":{"line":81,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:lastToastTime.clear",{"id":"external:lastToastTime.clear","kind":"external","language":"jsts","metadata":{"unresolvedName":"lastToastTime.clear"}}],["/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter",{"id":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/tool-starter.tsx","symbolName":"ToolStarter","symbolKind":"function","exported":true,"metadata":{"line":17,"column":1,"cyclomaticComplexity":36,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:setToolTypesError",{"id":"symbol-name:setToolTypesError","kind":"symbol","language":"jsts","symbolName":"setToolTypesError","metadata":{"unresolvedName":"setToolTypesError"}}],["symbol-name:setToolTypesLoading",{"id":"symbol-name:setToolTypesLoading","kind":"symbol","language":"jsts","symbolName":"setToolTypesLoading","metadata":{"unresolvedName":"setToolTypesLoading"}}],["symbol-name:load",{"id":"symbol-name:load","kind":"symbol","language":"jsts","symbolName":"load","metadata":{"unresolvedName":"load"}}],["symbol-name:setProfiles",{"id":"symbol-name:setProfiles","kind":"symbol","language":"jsts","symbolName":"setProfiles","metadata":{"unresolvedName":"setProfiles"}}],["symbol-name:setSelectedProfileId",{"id":"symbol-name:setSelectedProfileId","kind":"symbol","language":"jsts","symbolName":"setSelectedProfileId","metadata":{"unresolvedName":"setSelectedProfileId"}}],["symbol-name:setProfilesLoading",{"id":"symbol-name:setProfilesLoading","kind":"symbol","language":"jsts","symbolName":"setProfilesLoading","metadata":{"unresolvedName":"setProfilesLoading"}}],["external:data.find",{"id":"external:data.find","kind":"external","language":"jsts","metadata":{"unresolvedName":"data.find"}}],["symbol-name:setSshKeysLoading",{"id":"symbol-name:setSshKeysLoading","kind":"symbol","language":"jsts","symbolName":"setSshKeysLoading","metadata":{"unresolvedName":"setSshKeysLoading"}}],["symbol-name:setStarting",{"id":"symbol-name:setStarting","kind":"symbol","language":"jsts","symbolName":"setStarting","metadata":{"unresolvedName":"setStarting"}}],["symbol-name:onStarted",{"id":"symbol-name:onStarted","kind":"symbol","language":"jsts","symbolName":"onStarted","metadata":{"unresolvedName":"onStarted"}}],["symbol-name:setSelectedToolTypeId",{"id":"symbol-name:setSelectedToolTypeId","kind":"symbol","language":"jsts","symbolName":"setSelectedToolTypeId","metadata":{"unresolvedName":"setSelectedToolTypeId"}}],["external:profiles.map",{"id":"external:profiles.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"profiles.map"}}],["/workspace/apps/web/src/components/tool-starter.tsx:",{"id":"/workspace/apps/web/src/components/tool-starter.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/tool-starter.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":244,"column":20,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/tool-starter.tsx:load",{"id":"/workspace/apps/web/src/components/tool-starter.tsx:load","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/tool-starter.tsx","symbolName":"load","symbolKind":"function","exported":false,"metadata":{"line":89,"column":16,"cyclomaticComplexity":3,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/tools-bottom-sheet.tsx:ToolsBottomSheet",{"id":"/workspace/apps/web/src/components/tools-bottom-sheet.tsx:ToolsBottomSheet","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/tools-bottom-sheet.tsx","symbolName":"ToolsBottomSheet","symbolKind":"function","exported":true,"metadata":{"line":14,"column":66,"cyclomaticComplexity":4,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:TOOLS_ITEMS.map",{"id":"external:TOOLS_ITEMS.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"TOOLS_ITEMS.map"}}],["symbol-name:handleSelect",{"id":"symbol-name:handleSelect","kind":"symbol","language":"jsts","symbolName":"handleSelect","metadata":{"unresolvedName":"handleSelect"}}],["/workspace/apps/web/src/components/tools-bottom-sheet.tsx:handleSelect",{"id":"/workspace/apps/web/src/components/tools-bottom-sheet.tsx:handleSelect","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/tools-bottom-sheet.tsx","symbolName":"handleSelect","symbolKind":"function","exported":false,"metadata":{"line":23,"column":24,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:react-simple-code-editor",{"id":"external:react-simple-code-editor","kind":"external","language":"jsts","metadata":{"source":"react-simple-code-editor"}}],["/workspace/apps/web/src/components/ui/CodeEditor.tsx:CodeEditor",{"id":"/workspace/apps/web/src/components/ui/CodeEditor.tsx:CodeEditor","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/ui/CodeEditor.tsx","symbolName":"CodeEditor","symbolKind":"function","exported":true,"metadata":{"line":12,"column":54,"cyclomaticComplexity":2,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:input\n .split(\"\\n\")\n .map",{"id":"external:input\n .split(\"\\n\")\n .map","kind":"external","language":"jsts","metadata":{"unresolvedName":"input\n .split(\"\\n\")\n .map"}}],["external:input\n .split",{"id":"external:input\n .split","kind":"external","language":"jsts","metadata":{"unresolvedName":"input\n .split"}}],["/workspace/apps/web/src/components/ui/CodeEditor.tsx:",{"id":"/workspace/apps/web/src/components/ui/CodeEditor.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/ui/CodeEditor.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":18,"column":13,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/ui/CodeEditor.tsx:highlight",{"id":"/workspace/apps/web/src/components/ui/CodeEditor.tsx:highlight","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/ui/CodeEditor.tsx","symbolName":"highlight","symbolKind":"function","exported":false,"metadata":{"line":19,"column":23,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/ui/ConfirmDialog.tsx",{"id":"file:/workspace/apps/web/src/components/ui/ConfirmDialog.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/ui/ConfirmDialog.tsx","metadata":{"lineCount":41,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/ui/ConfirmDialog.tsx:ConfirmDialog",{"id":"/workspace/apps/web/src/components/ui/ConfirmDialog.tsx:ConfirmDialog","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/ui/ConfirmDialog.tsx","symbolName":"ConfirmDialog","symbolKind":"function","exported":true,"metadata":{"line":13,"column":60,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/ui/ErrorState.test.tsx",{"id":"file:/workspace/apps/web/src/components/ui/ErrorState.test.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx","metadata":{"lineCount":23,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/ui/ErrorState.tsx",{"id":"file:/workspace/apps/web/src/components/ui/ErrorState.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/ui/ErrorState.tsx","metadata":{"lineCount":18,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/ui/ErrorState.test.tsx:",{"id":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":16,"column":42,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:expect(screen.getByText(\"Failed to load\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"Failed to load\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"Failed to load\")).toBeInTheDocument"}}],["external:expect(screen.queryByText(\"Retry\")).not.toBeInTheDocument",{"id":"external:expect(screen.queryByText(\"Retry\")).not.toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.queryByText(\"Retry\")).not.toBeInTheDocument"}}],["external:expect(onRetry).toHaveBeenCalledTimes",{"id":"external:expect(onRetry).toHaveBeenCalledTimes","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(onRetry).toHaveBeenCalledTimes"}}],["external:@phosphor-icons/react",{"id":"external:@phosphor-icons/react","kind":"external","language":"jsts","metadata":{"source":"@phosphor-icons/react"}}],["/workspace/apps/web/src/components/ui/Icon.tsx:Icon",{"id":"/workspace/apps/web/src/components/ui/Icon.tsx:Icon","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/ui/Icon.tsx","symbolName":"Icon","symbolKind":"function","exported":true,"metadata":{"line":146,"column":42,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/ui/LoadingState.test.tsx",{"id":"file:/workspace/apps/web/src/components/ui/LoadingState.test.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/ui/LoadingState.test.tsx","metadata":{"lineCount":16,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/ui/LoadingState.tsx",{"id":"file:/workspace/apps/web/src/components/ui/LoadingState.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/ui/LoadingState.tsx","metadata":{"lineCount":10,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/ui/LoadingState.test.tsx:",{"id":"/workspace/apps/web/src/components/ui/LoadingState.test.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/ui/LoadingState.test.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":11,"column":31,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:expect(screen.getByText(\"Loading...\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"Loading...\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"Loading...\")).toBeInTheDocument"}}],["external:expect(screen.getByText(\"Loading sessions...\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"Loading sessions...\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"Loading sessions...\")).toBeInTheDocument"}}],["file:/workspace/apps/web/src/components/ui/StatusBadge.tsx",{"id":"file:/workspace/apps/web/src/components/ui/StatusBadge.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/ui/StatusBadge.tsx","metadata":{"lineCount":10,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/ui/index.ts",{"id":"file:/workspace/apps/web/src/components/ui/index.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/ui/index.ts","metadata":{"lineCount":6,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/workspace-card.tsx",{"id":"file:/workspace/apps/web/src/components/workspace-card.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/workspace-card.tsx","metadata":{"lineCount":78,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/workspace-instance-chips.tsx",{"id":"file:/workspace/apps/web/src/components/workspace-instance-chips.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/workspace-instance-chips.tsx","metadata":{"lineCount":58,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/workspace-card.tsx:WorkspaceCard",{"id":"/workspace/apps/web/src/components/workspace-card.tsx:WorkspaceCard","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/workspace-card.tsx","symbolName":"WorkspaceCard","symbolKind":"function","exported":true,"metadata":{"line":16,"column":1,"cyclomaticComplexity":4,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:onStartTool",{"id":"symbol-name:onStartTool","kind":"symbol","language":"jsts","symbolName":"onStartTool","metadata":{"unresolvedName":"onStartTool"}}],["symbol-name:onSync",{"id":"symbol-name:onSync","kind":"symbol","language":"jsts","symbolName":"onSync","metadata":{"unresolvedName":"onSync"}}],["file:/workspace/apps/web/src/components/workspace-create-form.tsx",{"id":"file:/workspace/apps/web/src/components/workspace-create-form.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/workspace-create-form.tsx","metadata":{"lineCount":322,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/hooks/use-git-repo.ts",{"id":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-git-repo.ts","metadata":{"lineCount":281,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm",{"id":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/workspace-create-form.tsx","symbolName":"WorkspaceCreateForm","symbolKind":"function","exported":true,"metadata":{"line":23,"column":1,"cyclomaticComplexity":51,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:useGitRepo",{"id":"symbol-name:useGitRepo","kind":"symbol","language":"jsts","symbolName":"useGitRepo","metadata":{"unresolvedName":"useGitRepo"}}],["external:git.branches.includes",{"id":"external:git.branches.includes","kind":"external","language":"jsts","metadata":{"unresolvedName":"git.branches.includes"}}],["symbol-name:setSelectedBranch",{"id":"symbol-name:setSelectedBranch","kind":"symbol","language":"jsts","symbolName":"setSelectedBranch","metadata":{"unresolvedName":"setSelectedBranch"}}],["symbol-name:setIsNewBranch",{"id":"symbol-name:setIsNewBranch","kind":"symbol","language":"jsts","symbolName":"setIsNewBranch","metadata":{"unresolvedName":"setIsNewBranch"}}],["symbol-name:listProjects",{"id":"symbol-name:listProjects","kind":"symbol","language":"jsts","symbolName":"listProjects","metadata":{"unresolvedName":"listProjects"}}],["symbol-name:setProjects",{"id":"symbol-name:setProjects","kind":"symbol","language":"jsts","symbolName":"setProjects","metadata":{"unresolvedName":"setProjects"}}],["symbol-name:setFetchingProjects",{"id":"symbol-name:setFetchingProjects","kind":"symbol","language":"jsts","symbolName":"setFetchingProjects","metadata":{"unresolvedName":"setFetchingProjects"}}],["symbol-name:loadProjects",{"id":"symbol-name:loadProjects","kind":"symbol","language":"jsts","symbolName":"loadProjects","metadata":{"unresolvedName":"loadProjects"}}],["symbol-name:setRepos",{"id":"symbol-name:setRepos","kind":"symbol","language":"jsts","symbolName":"setRepos","metadata":{"unresolvedName":"setRepos"}}],["external:name.trim",{"id":"external:name.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"name.trim"}}],["symbol-name:createWorkspaceTopLevel",{"id":"symbol-name:createWorkspaceTopLevel","kind":"symbol","language":"jsts","symbolName":"createWorkspaceTopLevel","metadata":{"unresolvedName":"createWorkspaceTopLevel"}}],["external:repos.map",{"id":"external:repos.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"repos.map"}}],["symbol-name:setName",{"id":"symbol-name:setName","kind":"symbol","language":"jsts","symbolName":"setName","metadata":{"unresolvedName":"setName"}}],["symbol-name:handleBranchChange",{"id":"symbol-name:handleBranchChange","kind":"symbol","language":"jsts","symbolName":"handleBranchChange","metadata":{"unresolvedName":"handleBranchChange"}}],["external:git.branches.map",{"id":"external:git.branches.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"git.branches.map"}}],["/workspace/apps/web/src/components/workspace-create-form.tsx:",{"id":"/workspace/apps/web/src/components/workspace-create-form.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/workspace-create-form.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":204,"column":18,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/workspace-create-form.tsx:loadRepos",{"id":"/workspace/apps/web/src/components/workspace-create-form.tsx:loadRepos","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/workspace-create-form.tsx","symbolName":"loadRepos","symbolKind":"function","exported":false,"metadata":{"line":94,"column":21,"cyclomaticComplexity":4,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/components/workspace-create-form.tsx:handleBranchChange",{"id":"/workspace/apps/web/src/components/workspace-create-form.tsx:handleBranchChange","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/workspace-create-form.tsx","symbolName":"handleBranchChange","symbolKind":"function","exported":false,"metadata":{"line":108,"column":29,"cyclomaticComplexity":3,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/workspace-create-form.tsx:handleSubmit",{"id":"/workspace/apps/web/src/components/workspace-create-form.tsx:handleSubmit","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/workspace-create-form.tsx","symbolName":"handleSubmit","symbolKind":"function","exported":false,"metadata":{"line":123,"column":23,"cyclomaticComplexity":7,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/workspace-instance-chips.tsx:WorkspaceInstanceChips",{"id":"/workspace/apps/web/src/components/workspace-instance-chips.tsx:WorkspaceInstanceChips","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/workspace-instance-chips.tsx","symbolName":"WorkspaceInstanceChips","symbolKind":"function","exported":true,"metadata":{"line":11,"column":1,"cyclomaticComplexity":6,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:listWorkspaceInstances",{"id":"symbol-name:listWorkspaceInstances","kind":"symbol","language":"jsts","symbolName":"listWorkspaceInstances","metadata":{"unresolvedName":"listWorkspaceInstances"}}],["/workspace/apps/web/src/components/workspace-instance-chips.tsx:",{"id":"/workspace/apps/web/src/components/workspace-instance-chips.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/workspace-instance-chips.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":17,"column":12,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/workspace-instance-chips.tsx:load",{"id":"/workspace/apps/web/src/components/workspace-instance-chips.tsx:load","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/workspace-instance-chips.tsx","symbolName":"load","symbolKind":"function","exported":false,"metadata":{"line":18,"column":16,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/hooks/use-async-data.ts:useAsyncData",{"id":"/workspace/apps/web/src/hooks/use-async-data.ts:useAsyncData","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-async-data.ts","symbolName":"useAsyncData","symbolKind":"function","exported":true,"metadata":{"line":12,"column":1,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["network","external-api","serialization"]}}],["symbol-name:fetcher",{"id":"symbol-name:fetcher","kind":"symbol","language":"jsts","symbolName":"fetcher","metadata":{"unresolvedName":"fetcher"}}],["symbol-name:setData",{"id":"symbol-name:setData","kind":"symbol","language":"jsts","symbolName":"setData","metadata":{"unresolvedName":"setData"}}],["/workspace/apps/web/src/hooks/use-async-data.ts:",{"id":"/workspace/apps/web/src/hooks/use-async-data.ts:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-async-data.ts","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":37,"column":13,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/hooks/use-auto-hide.ts:useAutoHide",{"id":"/workspace/apps/web/src/hooks/use-auto-hide.ts:useAutoHide","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-auto-hide.ts","symbolName":"useAutoHide","symbolKind":"function","exported":true,"metadata":{"line":8,"column":1,"cyclomaticComplexity":9,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:setIsVisible",{"id":"symbol-name:setIsVisible","kind":"symbol","language":"jsts","symbolName":"setIsVisible","metadata":{"unresolvedName":"setIsVisible"}}],["symbol-name:hide",{"id":"symbol-name:hide","kind":"symbol","language":"jsts","symbolName":"hide","metadata":{"unresolvedName":"hide"}}],["symbol-name:show",{"id":"symbol-name:show","kind":"symbol","language":"jsts","symbolName":"show","metadata":{"unresolvedName":"show"}}],["/workspace/apps/web/src/hooks/use-auto-hide.ts:",{"id":"/workspace/apps/web/src/hooks/use-auto-hide.ts:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-auto-hide.ts","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":55,"column":12,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/hooks/use-dashboard-actions.ts",{"id":"file:/workspace/apps/web/src/hooks/use-dashboard-actions.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts","metadata":{"lineCount":122,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/hooks/use-dashboard-actions.ts:useDashboardActions",{"id":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:useDashboardActions","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts","symbolName":"useDashboardActions","symbolKind":"function","exported":true,"metadata":{"line":13,"column":1,"cyclomaticComplexity":5,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:setSaveState",{"id":"symbol-name:setSaveState","kind":"symbol","language":"jsts","symbolName":"setSaveState","metadata":{"unresolvedName":"setSaveState"}}],["external:session.tool_type_interfaces.includes",{"id":"external:session.tool_type_interfaces.includes","kind":"external","language":"jsts","metadata":{"unresolvedName":"session.tool_type_interfaces.includes"}}],["symbol-name:setActionBusy",{"id":"symbol-name:setActionBusy","kind":"symbol","language":"jsts","symbolName":"setActionBusy","metadata":{"unresolvedName":"setActionBusy"}}],["/workspace/apps/web/src/hooks/use-dashboard-actions.ts:",{"id":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":96,"column":3,"cyclomaticComplexity":1,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/hooks/use-events.test.ts",{"id":"file:/workspace/apps/web/src/hooks/use-events.test.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-events.test.ts","metadata":{"lineCount":190,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/hooks/use-events.ts",{"id":"file:/workspace/apps/web/src/hooks/use-events.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-events.ts","metadata":{"lineCount":109,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/hooks/use-events.test.ts:",{"id":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-events.test.ts","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":185,"column":17,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:mockedCreateEventSource.mockReturnValue",{"id":"external:mockedCreateEventSource.mockReturnValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockedCreateEventSource.mockReturnValue"}}],["external:mockedProbeEventStreamStatus.mockResolvedValue",{"id":"external:mockedProbeEventStreamStatus.mockResolvedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockedProbeEventStreamStatus.mockResolvedValue"}}],["symbol-name:renderHook",{"id":"symbol-name:renderHook","kind":"symbol","language":"jsts","symbolName":"renderHook","metadata":{"unresolvedName":"renderHook"}}],["symbol-name:useEvents",{"id":"symbol-name:useEvents","kind":"symbol","language":"jsts","symbolName":"useEvents","metadata":{"unresolvedName":"useEvents"}}],["external:mockEs.onopen",{"id":"external:mockEs.onopen","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockEs.onopen"}}],["external:mockEs.onmessage",{"id":"external:mockEs.onmessage","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockEs.onmessage"}}],["external:expect(result.current.events).toHaveLength",{"id":"external:expect(result.current.events).toHaveLength","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(result.current.events).toHaveLength"}}],["external:expect(result.current.events[0].instance_id).toBe",{"id":"external:expect(result.current.events[0].instance_id).toBe","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(result.current.events[0].instance_id).toBe"}}],["external:expect(result.current.connected).toBe",{"id":"external:expect(result.current.connected).toBe","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(result.current.connected).toBe"}}],["external:mockEs.onerror",{"id":"external:mockEs.onerror","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockEs.onerror"}}],["external:expect(mockEs.close).toHaveBeenCalled",{"id":"external:expect(mockEs.close).toHaveBeenCalled","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(mockEs.close).toHaveBeenCalled"}}],["external:expect(mockedCreateEventSource).toHaveBeenCalledTimes",{"id":"external:expect(mockedCreateEventSource).toHaveBeenCalledTimes","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(mockedCreateEventSource).toHaveBeenCalledTimes"}}],["external:vi.advanceTimersByTime",{"id":"external:vi.advanceTimersByTime","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.advanceTimersByTime"}}],["external:secondEs.onerror",{"id":"external:secondEs.onerror","kind":"external","language":"jsts","metadata":{"unresolvedName":"secondEs.onerror"}}],["external:currentEs.onerror",{"id":"external:currentEs.onerror","kind":"external","language":"jsts","metadata":{"unresolvedName":"currentEs.onerror"}}],["external:expect(mockedCreateEventSource.mock.calls.length).toBeGreaterThan",{"id":"external:expect(mockedCreateEventSource.mock.calls.length).toBeGreaterThan","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(mockedCreateEventSource.mock.calls.length).toBeGreaterThan"}}],["external:expect(window.location.assign).toHaveBeenCalled",{"id":"external:expect(window.location.assign).toHaveBeenCalled","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(window.location.assign).toHaveBeenCalled"}}],["symbol-name:unmount",{"id":"symbol-name:unmount","kind":"symbol","language":"jsts","symbolName":"unmount","metadata":{"unresolvedName":"unmount"}}],["external:expect(result.current.reconnectCount).toBeGreaterThan",{"id":"external:expect(result.current.reconnectCount).toBeGreaterThan","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(result.current.reconnectCount).toBeGreaterThan"}}],["/workspace/apps/web/src/hooks/use-events.ts:useEvents",{"id":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-events.ts","symbolName":"useEvents","symbolKind":"function","exported":true,"metadata":{"line":15,"column":1,"cyclomaticComplexity":13,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:createEventSource",{"id":"symbol-name:createEventSource","kind":"symbol","language":"jsts","symbolName":"createEventSource","metadata":{"unresolvedName":"createEventSource"}}],["symbol-name:setConnected",{"id":"symbol-name:setConnected","kind":"symbol","language":"jsts","symbolName":"setConnected","metadata":{"unresolvedName":"setConnected"}}],["symbol-name:setReconnectCount",{"id":"symbol-name:setReconnectCount","kind":"symbol","language":"jsts","symbolName":"setReconnectCount","metadata":{"unresolvedName":"setReconnectCount"}}],["symbol-name:setEvents",{"id":"symbol-name:setEvents","kind":"symbol","language":"jsts","symbolName":"setEvents","metadata":{"unresolvedName":"setEvents"}}],["external:es.close",{"id":"external:es.close","kind":"external","language":"jsts","metadata":{"unresolvedName":"es.close"}}],["external:Math.min",{"id":"external:Math.min","kind":"external","language":"jsts","metadata":{"unresolvedName":"Math.min"}}],["external:Math.pow",{"id":"external:Math.pow","kind":"external","language":"jsts","metadata":{"unresolvedName":"Math.pow"}}],["external:Math.random",{"id":"external:Math.random","kind":"external","language":"jsts","metadata":{"unresolvedName":"Math.random"}}],["symbol-name:probeEventStreamStatus",{"id":"symbol-name:probeEventStreamStatus","kind":"symbol","language":"jsts","symbolName":"probeEventStreamStatus","metadata":{"unresolvedName":"probeEventStreamStatus"}}],["symbol-name:connect",{"id":"symbol-name:connect","kind":"symbol","language":"jsts","symbolName":"connect","metadata":{"unresolvedName":"connect"}}],["external:esRef.current.close",{"id":"external:esRef.current.close","kind":"external","language":"jsts","metadata":{"unresolvedName":"esRef.current.close"}}],["/workspace/apps/web/src/hooks/use-events.ts:",{"id":"/workspace/apps/web/src/hooks/use-events.ts:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-events.ts","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":94,"column":10,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo",{"id":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-git-repo.ts","symbolName":"useGitRepo","symbolKind":"function","exported":true,"metadata":{"line":84,"column":1,"cyclomaticComplexity":35,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["symbol-name:async",{"id":"symbol-name:async","kind":"symbol","language":"jsts","symbolName":"async","metadata":{"unresolvedName":"async"}}],["symbol-name:fn",{"id":"symbol-name:fn","kind":"symbol","language":"jsts","symbolName":"fn","metadata":{"unresolvedName":"fn"}}],["symbol-name:extractError",{"id":"symbol-name:extractError","kind":"symbol","language":"jsts","symbolName":"extractError","metadata":{"unresolvedName":"extractError"}}],["symbol-name:withLoading",{"id":"symbol-name:withLoading","kind":"symbol","language":"jsts","symbolName":"withLoading","metadata":{"unresolvedName":"withLoading"}}],["external:data.branches.map",{"id":"external:data.branches.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"data.branches.map"}}],["symbol-name:setDefaultBranch",{"id":"symbol-name:setDefaultBranch","kind":"symbol","language":"jsts","symbolName":"setDefaultBranch","metadata":{"unresolvedName":"setDefaultBranch"}}],["symbol-name:getRepositoryHistory",{"id":"symbol-name:getRepositoryHistory","kind":"symbol","language":"jsts","symbolName":"getRepositoryHistory","metadata":{"unresolvedName":"getRepositoryHistory"}}],["symbol-name:setHistory",{"id":"symbol-name:setHistory","kind":"symbol","language":"jsts","symbolName":"setHistory","metadata":{"unresolvedName":"setHistory"}}],["symbol-name:getCommitDetail",{"id":"symbol-name:getCommitDetail","kind":"symbol","language":"jsts","symbolName":"getCommitDetail","metadata":{"unresolvedName":"getCommitDetail"}}],["symbol-name:setCommitDetail",{"id":"symbol-name:setCommitDetail","kind":"symbol","language":"jsts","symbolName":"setCommitDetail","metadata":{"unresolvedName":"setCommitDetail"}}],["symbol-name:refreshStatus",{"id":"symbol-name:refreshStatus","kind":"symbol","language":"jsts","symbolName":"refreshStatus","metadata":{"unresolvedName":"refreshStatus"}}],["symbol-name:refreshBranches",{"id":"symbol-name:refreshBranches","kind":"symbol","language":"jsts","symbolName":"refreshBranches","metadata":{"unresolvedName":"refreshBranches"}}],["symbol-name:deleteBranch",{"id":"symbol-name:deleteBranch","kind":"symbol","language":"jsts","symbolName":"deleteBranch","metadata":{"unresolvedName":"deleteBranch"}}],["symbol-name:refreshHistory",{"id":"symbol-name:refreshHistory","kind":"symbol","language":"jsts","symbolName":"refreshHistory","metadata":{"unresolvedName":"refreshHistory"}}],["/workspace/apps/web/src/hooks/use-git-repo.ts:extractError",{"id":"/workspace/apps/web/src/hooks/use-git-repo.ts:extractError","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-git-repo.ts","symbolName":"extractError","symbolKind":"function","exported":false,"metadata":{"line":96,"column":23,"cyclomaticComplexity":6,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/hooks/use-git-repo.ts:",{"id":"/workspace/apps/web/src/hooks/use-git-repo.ts:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-git-repo.ts","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":249,"column":12,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["file:/workspace/apps/web/src/hooks/use-instance-actions.ts",{"id":"file:/workspace/apps/web/src/hooks/use-instance-actions.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-instance-actions.ts","metadata":{"lineCount":188,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/hooks/use-instance-actions.ts:useInstanceActions",{"id":"/workspace/apps/web/src/hooks/use-instance-actions.ts:useInstanceActions","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-instance-actions.ts","symbolName":"useInstanceActions","symbolKind":"function","exported":true,"metadata":{"line":27,"column":1,"cyclomaticComplexity":16,"maxNestingDepth":3,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:setLoadingSessionId",{"id":"symbol-name:setLoadingSessionId","kind":"symbol","language":"jsts","symbolName":"setLoadingSessionId","metadata":{"unresolvedName":"setLoadingSessionId"}}],["symbol-name:setDirtyDeleteSession",{"id":"symbol-name:setDirtyDeleteSession","kind":"symbol","language":"jsts","symbolName":"setDirtyDeleteSession","metadata":{"unresolvedName":"setDirtyDeleteSession"}}],["symbol-name:setDirtyDeleteFiles",{"id":"symbol-name:setDirtyDeleteFiles","kind":"symbol","language":"jsts","symbolName":"setDirtyDeleteFiles","metadata":{"unresolvedName":"setDirtyDeleteFiles"}}],["/workspace/apps/web/src/hooks/use-instance-actions.ts:",{"id":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-instance-actions.ts","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":170,"column":39,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/hooks/use-mobile-viewport.ts:useMobileViewport",{"id":"/workspace/apps/web/src/hooks/use-mobile-viewport.ts:useMobileViewport","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-mobile-viewport.ts","symbolName":"useMobileViewport","symbolKind":"function","exported":true,"metadata":{"line":5,"column":1,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:setIsMobile",{"id":"symbol-name:setIsMobile","kind":"symbol","language":"jsts","symbolName":"setIsMobile","metadata":{"unresolvedName":"setIsMobile"}}],["/workspace/apps/web/src/hooks/use-mobile-viewport.ts:",{"id":"/workspace/apps/web/src/hooks/use-mobile-viewport.ts:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-mobile-viewport.ts","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":11,"column":13,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/hooks/use-mobile-viewport.ts:handleResize",{"id":"/workspace/apps/web/src/hooks/use-mobile-viewport.ts:handleResize","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-mobile-viewport.ts","symbolName":"handleResize","symbolKind":"function","exported":false,"metadata":{"line":12,"column":26,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/hooks/use-notifications.test.tsx",{"id":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-notifications.test.tsx","metadata":{"lineCount":338,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/hooks/use-notifications.test.tsx:wrapper",{"id":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:wrapper","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-notifications.test.tsx","symbolName":"wrapper","symbolKind":"function","exported":false,"metadata":{"line":29,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/hooks/use-notifications.test.tsx:",{"id":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-notifications.test.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":329,"column":13,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:mockedMarkNotificationRead.mockResolvedValue",{"id":"external:mockedMarkNotificationRead.mockResolvedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockedMarkNotificationRead.mockResolvedValue"}}],["external:mockedMarkAllNotificationsRead.mockResolvedValue",{"id":"external:mockedMarkAllNotificationsRead.mockResolvedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockedMarkAllNotificationsRead.mockResolvedValue"}}],["external:mockedDismissNotification.mockResolvedValue",{"id":"external:mockedDismissNotification.mockResolvedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockedDismissNotification.mockResolvedValue"}}],["external:expect(result.current.notifications).toHaveLength",{"id":"external:expect(result.current.notifications).toHaveLength","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(result.current.notifications).toHaveLength"}}],["external:expect(result.current.unreadCount).toBe",{"id":"external:expect(result.current.unreadCount).toBe","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(result.current.unreadCount).toBe"}}],["external:mockedMarkNotificationRead.mockReturnValue",{"id":"external:mockedMarkNotificationRead.mockReturnValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockedMarkNotificationRead.mockReturnValue"}}],["external:result.current.markRead",{"id":"external:result.current.markRead","kind":"external","language":"jsts","metadata":{"unresolvedName":"result.current.markRead"}}],["external:result.current.notifications.find",{"id":"external:result.current.notifications.find","kind":"external","language":"jsts","metadata":{"unresolvedName":"result.current.notifications.find"}}],["external:expect(n?.read_at).not.toBeNull",{"id":"external:expect(n?.read_at).not.toBeNull","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(n?.read_at).not.toBeNull"}}],["symbol-name:resolveApi",{"id":"symbol-name:resolveApi","kind":"symbol","language":"jsts","symbolName":"resolveApi","metadata":{"unresolvedName":"resolveApi"}}],["external:mockedMarkNotificationRead.mockRejectedValue",{"id":"external:mockedMarkNotificationRead.mockRejectedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockedMarkNotificationRead.mockRejectedValue"}}],["external:expect(n?.read_at).toBeNull",{"id":"external:expect(n?.read_at).toBeNull","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(n?.read_at).toBeNull"}}],["external:expect(result.current.error).toBeInstanceOf",{"id":"external:expect(result.current.error).toBeInstanceOf","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(result.current.error).toBeInstanceOf"}}],["external:mockedDismissNotification.mockReturnValue",{"id":"external:mockedDismissNotification.mockReturnValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockedDismissNotification.mockReturnValue"}}],["external:result.current.dismiss",{"id":"external:result.current.dismiss","kind":"external","language":"jsts","metadata":{"unresolvedName":"result.current.dismiss"}}],["external:mockedDismissNotification.mockRejectedValue",{"id":"external:mockedDismissNotification.mockRejectedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockedDismissNotification.mockRejectedValue"}}],["external:result.current.refreshList",{"id":"external:result.current.refreshList","kind":"external","language":"jsts","metadata":{"unresolvedName":"result.current.refreshList"}}],["external:expect(mockedGetNotifications).toHaveBeenCalledTimes",{"id":"external:expect(mockedGetNotifications).toHaveBeenCalledTimes","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(mockedGetNotifications).toHaveBeenCalledTimes"}}],["external:mockedGetUnreadCount.mockRejectedValue",{"id":"external:mockedGetUnreadCount.mockRejectedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockedGetUnreadCount.mockRejectedValue"}}],["external:expect(mockedGetUnreadCount.mock.calls.length).toBe",{"id":"external:expect(mockedGetUnreadCount.mock.calls.length).toBe","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(mockedGetUnreadCount.mock.calls.length).toBe"}}],["external:Object.defineProperty",{"id":"external:Object.defineProperty","kind":"external","language":"jsts","metadata":{"unresolvedName":"Object.defineProperty"}}],["external:document.dispatchEvent",{"id":"external:document.dispatchEvent","kind":"external","language":"jsts","metadata":{"unresolvedName":"document.dispatchEvent"}}],["external:expect(mockedGetUnreadCount.mock.calls.length).toBeGreaterThan",{"id":"external:expect(mockedGetUnreadCount.mock.calls.length).toBeGreaterThan","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(mockedGetUnreadCount.mock.calls.length).toBeGreaterThan"}}],["/workspace/apps/web/src/hooks/use-notifications.ts:useNotifications",{"id":"/workspace/apps/web/src/hooks/use-notifications.ts:useNotifications","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-notifications.ts","symbolName":"useNotifications","symbolKind":"function","exported":true,"metadata":{"line":4,"column":1,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:useContext",{"id":"symbol-name:useContext","kind":"symbol","language":"jsts","symbolName":"useContext","metadata":{"unresolvedName":"useContext"}}],["/workspace/apps/web/src/hooks/use-special-keys.ts:getSequenceWithModifier",{"id":"/workspace/apps/web/src/hooks/use-special-keys.ts:getSequenceWithModifier","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-special-keys.ts","symbolName":"getSequenceWithModifier","symbolKind":"function","exported":true,"metadata":{"line":106,"column":1,"cyclomaticComplexity":7,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:char.toLowerCase",{"id":"external:char.toLowerCase","kind":"external","language":"jsts","metadata":{"unresolvedName":"char.toLowerCase"}}],["/workspace/apps/web/src/hooks/use-special-keys.ts:applyModifierToChar",{"id":"/workspace/apps/web/src/hooks/use-special-keys.ts:applyModifierToChar","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-special-keys.ts","symbolName":"applyModifierToChar","symbolKind":"function","exported":true,"metadata":{"line":130,"column":1,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/hooks/use-start-tool.ts",{"id":"file:/workspace/apps/web/src/hooks/use-start-tool.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-start-tool.ts","metadata":{"lineCount":65,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/hooks/use-start-tool.ts:useStartTool",{"id":"/workspace/apps/web/src/hooks/use-start-tool.ts:useStartTool","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-start-tool.ts","symbolName":"useStartTool","symbolKind":"function","exported":true,"metadata":{"line":19,"column":1,"cyclomaticComplexity":4,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/hooks/use-start-tool.ts:",{"id":"/workspace/apps/web/src/hooks/use-start-tool.ts:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-start-tool.ts","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":24,"column":3,"cyclomaticComplexity":4,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts",{"id":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","metadata":{"lineCount":340,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:send",{"id":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:send","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","symbolName":"send","symbolKind":"function","exported":false,"metadata":{"line":20,"column":2,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:this.sent.push",{"id":"external:this.sent.push","kind":"external","language":"jsts","metadata":{"unresolvedName":"this.sent.push"}}],["/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:close",{"id":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:close","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","symbolName":"close","symbolKind":"function","exported":false,"metadata":{"line":24,"column":2,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:this.onclose",{"id":"external:this.onclose","kind":"external","language":"jsts","metadata":{"unresolvedName":"this.onclose"}}],["/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:simulateOpen",{"id":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:simulateOpen","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","symbolName":"simulateOpen","symbolKind":"function","exported":false,"metadata":{"line":31,"column":2,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:this.onopen",{"id":"external:this.onopen","kind":"external","language":"jsts","metadata":{"unresolvedName":"this.onopen"}}],["/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:simulateMessage",{"id":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:simulateMessage","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","symbolName":"simulateMessage","symbolKind":"function","exported":false,"metadata":{"line":36,"column":2,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:this.onmessage",{"id":"external:this.onmessage","kind":"external","language":"jsts","metadata":{"unresolvedName":"this.onmessage"}}],["/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:simulateError",{"id":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:simulateError","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","symbolName":"simulateError","symbolKind":"function","exported":false,"metadata":{"line":42,"column":2,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:this.onerror",{"id":"external:this.onerror","kind":"external","language":"jsts","metadata":{"unresolvedName":"this.onerror"}}],["/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:",{"id":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":331,"column":7,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:vi.stubGlobal",{"id":"external:vi.stubGlobal","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.stubGlobal"}}],["external:vi.unstubAllGlobals",{"id":"external:vi.unstubAllGlobals","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.unstubAllGlobals"}}],["external:expect(result.current.state.status).toBe",{"id":"external:expect(result.current.state.status).toBe","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(result.current.state.status).toBe"}}],["external:expect(MockWebSocket.instances).toHaveLength",{"id":"external:expect(MockWebSocket.instances).toHaveLength","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(MockWebSocket.instances).toHaveLength"}}],["external:MockWebSocket.instances[0].simulateOpen",{"id":"external:MockWebSocket.instances[0].simulateOpen","kind":"external","language":"jsts","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateOpen"}}],["external:MockWebSocket.instances[0].sent.filter",{"id":"external:MockWebSocket.instances[0].sent.filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"MockWebSocket.instances[0].sent.filter"}}],["external:s.includes",{"id":"external:s.includes","kind":"external","language":"jsts","metadata":{"unresolvedName":"s.includes"}}],["external:expect(pings.length).toBeGreaterThanOrEqual",{"id":"external:expect(pings.length).toBeGreaterThanOrEqual","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(pings.length).toBeGreaterThanOrEqual"}}],["external:MockWebSocket.instances[0].simulateMessage",{"id":"external:MockWebSocket.instances[0].simulateMessage","kind":"external","language":"jsts","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateMessage"}}],["external:expect(result.current.state.latency).not.toBeNull",{"id":"external:expect(result.current.state.latency).not.toBeNull","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(result.current.state.latency).not.toBeNull"}}],["external:expect(result.current.state.latency).toBeGreaterThanOrEqual",{"id":"external:expect(result.current.state.latency).toBeGreaterThanOrEqual","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(result.current.state.latency).toBeGreaterThanOrEqual"}}],["external:MockWebSocket.instances[0].close",{"id":"external:MockWebSocket.instances[0].close","kind":"external","language":"jsts","metadata":{"unresolvedName":"MockWebSocket.instances[0].close"}}],["external:expect(result.current.state.attempt).toBe",{"id":"external:expect(result.current.state.attempt).toBe","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(result.current.state.attempt).toBe"}}],["external:ws.close",{"id":"external:ws.close","kind":"external","language":"jsts","metadata":{"unresolvedName":"ws.close"}}],["external:expect(result.current.state.error).toContain",{"id":"external:expect(result.current.state.error).toContain","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(result.current.state.error).toContain"}}],["external:result.current.sendResize",{"id":"external:result.current.sendResize","kind":"external","language":"jsts","metadata":{"unresolvedName":"result.current.sendResize"}}],["external:expect(resizes.length).toBeGreaterThanOrEqual",{"id":"external:expect(resizes.length).toBeGreaterThanOrEqual","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(resizes.length).toBeGreaterThanOrEqual"}}],["external:expect(resizes.length).toBe",{"id":"external:expect(resizes.length).toBe","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(resizes.length).toBe"}}],["external:result.current.sendInput",{"id":"external:result.current.sendInput","kind":"external","language":"jsts","metadata":{"unresolvedName":"result.current.sendInput"}}],["external:expect(MockWebSocket.instances[0].sent).toContain",{"id":"external:expect(MockWebSocket.instances[0].sent).toContain","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(MockWebSocket.instances[0].sent).toContain"}}],["external:result.current.reconnect",{"id":"external:result.current.reconnect","kind":"external","language":"jsts","metadata":{"unresolvedName":"result.current.reconnect"}}],["external:expect(onData).toHaveBeenCalledWith",{"id":"external:expect(onData).toHaveBeenCalledWith","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(onData).toHaveBeenCalledWith"}}],["external:expect.any",{"id":"external:expect.any","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect.any"}}],["external:expect(onControl).toHaveBeenCalledWith",{"id":"external:expect(onControl).toHaveBeenCalledWith","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(onControl).toHaveBeenCalledWith"}}],["external:expect(serializeFn).toHaveBeenCalled",{"id":"external:expect(serializeFn).toHaveBeenCalled","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(serializeFn).toHaveBeenCalled"}}],["external:expect(onRestoreScrollback).toHaveBeenCalledWith",{"id":"external:expect(onRestoreScrollback).toHaveBeenCalledWith","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(onRestoreScrollback).toHaveBeenCalledWith"}}],["file:/workspace/apps/web/src/utils/terminal-protocol.ts",{"id":"file:/workspace/apps/web/src/utils/terminal-protocol.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/utils/terminal-protocol.ts","metadata":{"lineCount":77,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection",{"id":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-terminal-connection.ts","symbolName":"useTerminalConnection","symbolKind":"function","exported":true,"metadata":{"line":35,"column":1,"cyclomaticComplexity":63,"maxNestingDepth":3,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:setState",{"id":"symbol-name:setState","kind":"symbol","language":"jsts","symbolName":"setState","metadata":{"unresolvedName":"setState"}}],["symbol-name:updater",{"id":"symbol-name:updater","kind":"symbol","language":"jsts","symbolName":"updater","metadata":{"unresolvedName":"updater"}}],["symbol-name:onLocalEcho",{"id":"symbol-name:onLocalEcho","kind":"symbol","language":"jsts","symbolName":"onLocalEcho","metadata":{"unresolvedName":"onLocalEcho"}}],["external:pendingEchoRef.current.slice",{"id":"external:pendingEchoRef.current.slice","kind":"external","language":"jsts","metadata":{"unresolvedName":"pendingEchoRef.current.slice"}}],["external:data.slice",{"id":"external:data.slice","kind":"external","language":"jsts","metadata":{"unresolvedName":"data.slice"}}],["external:new TextDecoder().decode",{"id":"external:new TextDecoder().decode","kind":"external","language":"jsts","metadata":{"unresolvedName":"new TextDecoder().decode"}}],["symbol-name:onData",{"id":"symbol-name:onData","kind":"symbol","language":"jsts","symbolName":"onData","metadata":{"unresolvedName":"onData"}}],["symbol-name:deduplicateServerData",{"id":"symbol-name:deduplicateServerData","kind":"symbol","language":"jsts","symbolName":"deduplicateServerData","metadata":{"unresolvedName":"deduplicateServerData"}}],["symbol-name:flushPendingEcho",{"id":"symbol-name:flushPendingEcho","kind":"symbol","language":"jsts","symbolName":"flushPendingEcho","metadata":{"unresolvedName":"flushPendingEcho"}}],["symbol-name:onControl",{"id":"symbol-name:onControl","kind":"symbol","language":"jsts","symbolName":"onControl","metadata":{"unresolvedName":"onControl"}}],["symbol-name:setStableState",{"id":"symbol-name:setStableState","kind":"symbol","language":"jsts","symbolName":"setStableState","metadata":{"unresolvedName":"setStableState"}}],["symbol-name:clearTimers",{"id":"symbol-name:clearTimers","kind":"symbol","language":"jsts","symbolName":"clearTimers","metadata":{"unresolvedName":"clearTimers"}}],["external:wsRef.current?.close",{"id":"external:wsRef.current?.close","kind":"external","language":"jsts","metadata":{"unresolvedName":"wsRef.current?.close"}}],["external:ws.send",{"id":"external:ws.send","kind":"external","language":"jsts","metadata":{"unresolvedName":"ws.send"}}],["symbol-name:encodeControlMessage",{"id":"symbol-name:encodeControlMessage","kind":"symbol","language":"jsts","symbolName":"encodeControlMessage","metadata":{"unresolvedName":"encodeControlMessage"}}],["symbol-name:serializeFn",{"id":"symbol-name:serializeFn","kind":"symbol","language":"jsts","symbolName":"serializeFn","metadata":{"unresolvedName":"serializeFn"}}],["external:sessionStorage.setItem",{"id":"external:sessionStorage.setItem","kind":"external","language":"jsts","metadata":{"unresolvedName":"sessionStorage.setItem"}}],["external:sessionStorage.getItem",{"id":"external:sessionStorage.getItem","kind":"external","language":"jsts","metadata":{"unresolvedName":"sessionStorage.getItem"}}],["symbol-name:onRestoreScrollback",{"id":"symbol-name:onRestoreScrollback","kind":"symbol","language":"jsts","symbolName":"onRestoreScrollback","metadata":{"unresolvedName":"onRestoreScrollback"}}],["external:sessionStorage.removeItem",{"id":"external:sessionStorage.removeItem","kind":"external","language":"jsts","metadata":{"unresolvedName":"sessionStorage.removeItem"}}],["external:apiUrl.replace(/^https?:\\/\\//, \"\").replace",{"id":"external:apiUrl.replace(/^https?:\\/\\//, \"\").replace","kind":"external","language":"jsts","metadata":{"unresolvedName":"apiUrl.replace(/^https?:\\/\\//, \"\").replace"}}],["external:apiUrl.replace",{"id":"external:apiUrl.replace","kind":"external","language":"jsts","metadata":{"unresolvedName":"apiUrl.replace"}}],["symbol-name:restoreScrollback",{"id":"symbol-name:restoreScrollback","kind":"symbol","language":"jsts","symbolName":"restoreScrollback","metadata":{"unresolvedName":"restoreScrollback"}}],["symbol-name:schedulePing",{"id":"symbol-name:schedulePing","kind":"symbol","language":"jsts","symbolName":"schedulePing","metadata":{"unresolvedName":"schedulePing"}}],["symbol-name:isControlMessage",{"id":"symbol-name:isControlMessage","kind":"symbol","language":"jsts","symbolName":"isControlMessage","metadata":{"unresolvedName":"isControlMessage"}}],["symbol-name:decodeControlMessage",{"id":"symbol-name:decodeControlMessage","kind":"symbol","language":"jsts","symbolName":"decodeControlMessage","metadata":{"unresolvedName":"decodeControlMessage"}}],["symbol-name:handleControlMessage",{"id":"symbol-name:handleControlMessage","kind":"symbol","language":"jsts","symbolName":"handleControlMessage","metadata":{"unresolvedName":"handleControlMessage"}}],["symbol-name:handleBinaryMessage",{"id":"symbol-name:handleBinaryMessage","kind":"symbol","language":"jsts","symbolName":"handleBinaryMessage","metadata":{"unresolvedName":"handleBinaryMessage"}}],["external:event.data.arrayBuffer().then",{"id":"external:event.data.arrayBuffer().then","kind":"external","language":"jsts","metadata":{"unresolvedName":"event.data.arrayBuffer().then"}}],["external:event.data.arrayBuffer",{"id":"external:event.data.arrayBuffer","kind":"external","language":"jsts","metadata":{"unresolvedName":"event.data.arrayBuffer"}}],["symbol-name:serializeScrollback",{"id":"symbol-name:serializeScrollback","kind":"symbol","language":"jsts","symbolName":"serializeScrollback","metadata":{"unresolvedName":"serializeScrollback"}}],["external:Math.round",{"id":"external:Math.round","kind":"external","language":"jsts","metadata":{"unresolvedName":"Math.round"}}],["external:data.charCodeAt",{"id":"external:data.charCodeAt","kind":"external","language":"jsts","metadata":{"unresolvedName":"data.charCodeAt"}}],["external:wsRef.current.close",{"id":"external:wsRef.current.close","kind":"external","language":"jsts","metadata":{"unresolvedName":"wsRef.current.close"}}],["/workspace/apps/web/src/hooks/use-terminal-connection.ts:",{"id":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-terminal-connection.ts","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":422,"column":12,"cyclomaticComplexity":4,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/hooks/use-terminal-connection.ts:handler",{"id":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:handler","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-terminal-connection.ts","symbolName":"handler","symbolKind":"function","exported":false,"metadata":{"line":423,"column":19,"cyclomaticComplexity":4,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/hooks/use-terminal-sessions.ts",{"id":"file:/workspace/apps/web/src/hooks/use-terminal-sessions.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts","metadata":{"lineCount":146,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/hooks/use-terminal-sessions.ts:useTerminalSessions",{"id":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:useTerminalSessions","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts","symbolName":"useTerminalSessions","symbolKind":"function","exported":true,"metadata":{"line":23,"column":1,"cyclomaticComplexity":17,"maxNestingDepth":3,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["symbol-name:listTerminalSessions",{"id":"symbol-name:listTerminalSessions","kind":"symbol","language":"jsts","symbolName":"listTerminalSessions","metadata":{"unresolvedName":"listTerminalSessions"}}],["symbol-name:setSessions",{"id":"symbol-name:setSessions","kind":"symbol","language":"jsts","symbolName":"setSessions","metadata":{"unresolvedName":"setSessions"}}],["symbol-name:setActiveSessionId",{"id":"symbol-name:setActiveSessionId","kind":"symbol","language":"jsts","symbolName":"setActiveSessionId","metadata":{"unresolvedName":"setActiveSessionId"}}],["symbol-name:createTerminalSession",{"id":"symbol-name:createTerminalSession","kind":"symbol","language":"jsts","symbolName":"createTerminalSession","metadata":{"unresolvedName":"createTerminalSession"}}],["symbol-name:closeTerminalSession",{"id":"symbol-name:closeTerminalSession","kind":"symbol","language":"jsts","symbolName":"closeTerminalSession","metadata":{"unresolvedName":"closeTerminalSession"}}],["symbol-name:renameTerminalSession",{"id":"symbol-name:renameTerminalSession","kind":"symbol","language":"jsts","symbolName":"renameTerminalSession","metadata":{"unresolvedName":"renameTerminalSession"}}],["external:prev.map",{"id":"external:prev.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"prev.map"}}],["symbol-name:resetTerminalSession",{"id":"symbol-name:resetTerminalSession","kind":"symbol","language":"jsts","symbolName":"resetTerminalSession","metadata":{"unresolvedName":"resetTerminalSession"}}],["/workspace/apps/web/src/hooks/use-terminal-sessions.ts:",{"id":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":130,"column":12,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/hooks/use-theme.ts:useTheme",{"id":"/workspace/apps/web/src/hooks/use-theme.ts:useTheme","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-theme.ts","symbolName":"useTheme","symbolKind":"function","exported":true,"metadata":{"line":5,"column":1,"cyclomaticComplexity":4,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:document.documentElement.removeAttribute",{"id":"external:document.documentElement.removeAttribute","kind":"external","language":"jsts","metadata":{"unresolvedName":"document.documentElement.removeAttribute"}}],["external:document.documentElement.setAttribute",{"id":"external:document.documentElement.setAttribute","kind":"external","language":"jsts","metadata":{"unresolvedName":"document.documentElement.setAttribute"}}],["/workspace/apps/web/src/hooks/use-theme.ts:",{"id":"/workspace/apps/web/src/hooks/use-theme.ts:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-theme.ts","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":7,"column":11,"cyclomaticComplexity":4,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:useVirtualKeyboard",{"id":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:useVirtualKeyboard","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts","symbolName":"useVirtualKeyboard","symbolKind":"function","exported":true,"metadata":{"line":9,"column":1,"cyclomaticComplexity":7,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:visualViewport.addEventListener",{"id":"external:visualViewport.addEventListener","kind":"external","language":"jsts","metadata":{"unresolvedName":"visualViewport.addEventListener"}}],["symbol-name:updateKeyboardState",{"id":"symbol-name:updateKeyboardState","kind":"symbol","language":"jsts","symbolName":"updateKeyboardState","metadata":{"unresolvedName":"updateKeyboardState"}}],["external:visualViewport.removeEventListener",{"id":"external:visualViewport.removeEventListener","kind":"external","language":"jsts","metadata":{"unresolvedName":"visualViewport.removeEventListener"}}],["/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:",{"id":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":57,"column":12,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/hooks/use-workspace-actions.ts",{"id":"file:/workspace/apps/web/src/hooks/use-workspace-actions.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-workspace-actions.ts","metadata":{"lineCount":147,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/hooks/use-workspace-actions.ts:useWorkspaceActions",{"id":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:useWorkspaceActions","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-workspace-actions.ts","symbolName":"useWorkspaceActions","symbolKind":"function","exported":true,"metadata":{"line":50,"column":1,"cyclomaticComplexity":10,"maxNestingDepth":3,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:createWorkspace",{"id":"symbol-name:createWorkspace","kind":"symbol","language":"jsts","symbolName":"createWorkspace","metadata":{"unresolvedName":"createWorkspace"}}],["symbol-name:setLoadingId",{"id":"symbol-name:setLoadingId","kind":"symbol","language":"jsts","symbolName":"setLoadingId","metadata":{"unresolvedName":"setLoadingId"}}],["symbol-name:deleteWorkspace",{"id":"symbol-name:deleteWorkspace","kind":"symbol","language":"jsts","symbolName":"deleteWorkspace","metadata":{"unresolvedName":"deleteWorkspace"}}],["external:instances.map((i) => `- ${i.name}`).join",{"id":"external:instances.map((i) => `- ${i.name}`).join","kind":"external","language":"jsts","metadata":{"unresolvedName":"instances.map((i) => `- ${i.name}`).join"}}],["symbol-name:syncWorkspace",{"id":"symbol-name:syncWorkspace","kind":"symbol","language":"jsts","symbolName":"syncWorkspace","metadata":{"unresolvedName":"syncWorkspace"}}],["symbol-name:updateWorkspace",{"id":"symbol-name:updateWorkspace","kind":"symbol","language":"jsts","symbolName":"updateWorkspace","metadata":{"unresolvedName":"updateWorkspace"}}],["/workspace/apps/web/src/hooks/use-workspace-actions.ts:",{"id":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-workspace-actions.ts","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":128,"column":3,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":true,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/hooks/use-workspace-files.ts",{"id":"file:/workspace/apps/web/src/hooks/use-workspace-files.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-workspace-files.ts","metadata":{"lineCount":69,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/hooks/use-workspace-files.ts:useWorkspaceFiles",{"id":"/workspace/apps/web/src/hooks/use-workspace-files.ts:useWorkspaceFiles","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-workspace-files.ts","symbolName":"useWorkspaceFiles","symbolKind":"function","exported":true,"metadata":{"line":21,"column":1,"cyclomaticComplexity":5,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:listWorkspaceFiles",{"id":"symbol-name:listWorkspaceFiles","kind":"symbol","language":"jsts","symbolName":"listWorkspaceFiles","metadata":{"unresolvedName":"listWorkspaceFiles"}}],["symbol-name:getWorkspaceFileContent",{"id":"symbol-name:getWorkspaceFileContent","kind":"symbol","language":"jsts","symbolName":"getWorkspaceFileContent","metadata":{"unresolvedName":"getWorkspaceFileContent"}}],["symbol-name:saveWorkspaceFile",{"id":"symbol-name:saveWorkspaceFile","kind":"symbol","language":"jsts","symbolName":"saveWorkspaceFile","metadata":{"unresolvedName":"saveWorkspaceFile"}}],["symbol-name:refresh",{"id":"symbol-name:refresh","kind":"symbol","language":"jsts","symbolName":"refresh","metadata":{"unresolvedName":"refresh"}}],["/workspace/apps/web/src/hooks/use-workspace-files.ts:",{"id":"/workspace/apps/web/src/hooks/use-workspace-files.ts:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-workspace-files.ts","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":63,"column":12,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/hooks/use-workspace-git.ts",{"id":"file:/workspace/apps/web/src/hooks/use-workspace-git.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-workspace-git.ts","metadata":{"lineCount":110,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/hooks/use-workspace-git.ts:useWorkspaceGit",{"id":"/workspace/apps/web/src/hooks/use-workspace-git.ts:useWorkspaceGit","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-workspace-git.ts","symbolName":"useWorkspaceGit","symbolKind":"function","exported":true,"metadata":{"line":32,"column":1,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:getGitStatus",{"id":"symbol-name:getGitStatus","kind":"symbol","language":"jsts","symbolName":"getGitStatus","metadata":{"unresolvedName":"getGitStatus"}}],["symbol-name:getGitBranches",{"id":"symbol-name:getGitBranches","kind":"symbol","language":"jsts","symbolName":"getGitBranches","metadata":{"unresolvedName":"getGitBranches"}}],["symbol-name:getGitHistory",{"id":"symbol-name:getGitHistory","kind":"symbol","language":"jsts","symbolName":"getGitHistory","metadata":{"unresolvedName":"getGitHistory"}}],["symbol-name:setCurrentBranch",{"id":"symbol-name:setCurrentBranch","kind":"symbol","language":"jsts","symbolName":"setCurrentBranch","metadata":{"unresolvedName":"setCurrentBranch"}}],["symbol-name:gitCommit",{"id":"symbol-name:gitCommit","kind":"symbol","language":"jsts","symbolName":"gitCommit","metadata":{"unresolvedName":"gitCommit"}}],["symbol-name:gitPush",{"id":"symbol-name:gitPush","kind":"symbol","language":"jsts","symbolName":"gitPush","metadata":{"unresolvedName":"gitPush"}}],["symbol-name:gitPull",{"id":"symbol-name:gitPull","kind":"symbol","language":"jsts","symbolName":"gitPull","metadata":{"unresolvedName":"gitPull"}}],["symbol-name:gitFetch",{"id":"symbol-name:gitFetch","kind":"symbol","language":"jsts","symbolName":"gitFetch","metadata":{"unresolvedName":"gitFetch"}}],["symbol-name:gitCheckout",{"id":"symbol-name:gitCheckout","kind":"symbol","language":"jsts","symbolName":"gitCheckout","metadata":{"unresolvedName":"gitCheckout"}}],["/workspace/apps/web/src/hooks/use-workspace-git.ts:",{"id":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-workspace-git.ts","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":91,"column":12,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/hooks/use-workspace-instances.ts",{"id":"file:/workspace/apps/web/src/hooks/use-workspace-instances.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-workspace-instances.ts","metadata":{"lineCount":66,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/hooks/use-workspace-instances.ts:useWorkspaceInstances",{"id":"/workspace/apps/web/src/hooks/use-workspace-instances.ts:useWorkspaceInstances","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-workspace-instances.ts","symbolName":"useWorkspaceInstances","symbolKind":"function","exported":true,"metadata":{"line":22,"column":1,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:createWorkspaceInstance",{"id":"symbol-name:createWorkspaceInstance","kind":"symbol","language":"jsts","symbolName":"createWorkspaceInstance","metadata":{"unresolvedName":"createWorkspaceInstance"}}],["/workspace/apps/web/src/hooks/use-workspace-instances.ts:",{"id":"/workspace/apps/web/src/hooks/use-workspace-instances.ts:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-workspace-instances.ts","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":60,"column":12,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/hooks/use-workspaces.ts",{"id":"file:/workspace/apps/web/src/hooks/use-workspaces.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-workspaces.ts","metadata":{"lineCount":46,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/hooks/use-workspaces.ts:useWorkspaces",{"id":"/workspace/apps/web/src/hooks/use-workspaces.ts:useWorkspaces","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-workspaces.ts","symbolName":"useWorkspaces","symbolKind":"function","exported":true,"metadata":{"line":14,"column":1,"cyclomaticComplexity":5,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:listWorkspaces",{"id":"symbol-name:listWorkspaces","kind":"symbol","language":"jsts","symbolName":"listWorkspaces","metadata":{"unresolvedName":"listWorkspaces"}}],["/workspace/apps/web/src/hooks/use-workspaces.ts:",{"id":"/workspace/apps/web/src/hooks/use-workspaces.ts:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/hooks/use-workspaces.ts","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":40,"column":12,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/main.tsx",{"id":"file:/workspace/apps/web/src/main.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/main.tsx","metadata":{"lineCount":31,"featureKind":"library","trustBoundaries":[]}}],["external:react-dom/client",{"id":"external:react-dom/client","kind":"external","language":"jsts","metadata":{"source":"react-dom/client"}}],["file:/workspace/apps/web/src/router.tsx",{"id":"file:/workspace/apps/web/src/router.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/router.tsx","metadata":{"lineCount":82,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/styles/tokens.css",{"id":"file:/workspace/apps/web/src/styles/tokens.css","kind":"file","language":"css","filePath":"/workspace/apps/web/src/styles/tokens.css"}],["file:/workspace/apps/web/src/styles/global.css",{"id":"file:/workspace/apps/web/src/styles/global.css","kind":"file","language":"css","filePath":"/workspace/apps/web/src/styles/global.css"}],["file:/workspace/apps/web/src/styles/utilities.css",{"id":"file:/workspace/apps/web/src/styles/utilities.css","kind":"file","language":"css","filePath":"/workspace/apps/web/src/styles/utilities.css"}],["file:/workspace/apps/web/src/styles/syntax-highlight.css",{"id":"file:/workspace/apps/web/src/styles/syntax-highlight.css","kind":"file","language":"css","filePath":"/workspace/apps/web/src/styles/syntax-highlight.css"}],["file:/workspace/apps/web/src/styles/pages/sessions.css",{"id":"file:/workspace/apps/web/src/styles/pages/sessions.css","kind":"file","language":"css","filePath":"/workspace/apps/web/src/styles/pages/sessions.css"}],["file:/workspace/apps/web/src/styles/pages/repo-workspace.css",{"id":"file:/workspace/apps/web/src/styles/pages/repo-workspace.css","kind":"file","language":"css","filePath":"/workspace/apps/web/src/styles/pages/repo-workspace.css"}],["file:/workspace/apps/web/src/styles/pages/dashboard.css",{"id":"file:/workspace/apps/web/src/styles/pages/dashboard.css","kind":"file","language":"css","filePath":"/workspace/apps/web/src/styles/pages/dashboard.css"}],["file:/workspace/apps/web/src/styles/pages/projects.css",{"id":"file:/workspace/apps/web/src/styles/pages/projects.css","kind":"file","language":"css","filePath":"/workspace/apps/web/src/styles/pages/projects.css"}],["file:/workspace/apps/web/src/styles/pages/git-history.css",{"id":"file:/workspace/apps/web/src/styles/pages/git-history.css","kind":"file","language":"css","filePath":"/workspace/apps/web/src/styles/pages/git-history.css"}],["file:/workspace/apps/web/src/styles/pages/ssh-keys.css",{"id":"file:/workspace/apps/web/src/styles/pages/ssh-keys.css","kind":"file","language":"css","filePath":"/workspace/apps/web/src/styles/pages/ssh-keys.css"}],["file:/workspace/apps/web/src/styles/pages/settings.css",{"id":"file:/workspace/apps/web/src/styles/pages/settings.css","kind":"file","language":"css","filePath":"/workspace/apps/web/src/styles/pages/settings.css"}],["file:/workspace/apps/web/src/pages/DashboardPage.test.tsx",{"id":"file:/workspace/apps/web/src/pages/DashboardPage.test.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/pages/DashboardPage.test.tsx","metadata":{"lineCount":82,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/pages/DashboardPage.tsx",{"id":"file:/workspace/apps/web/src/pages/DashboardPage.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/pages/DashboardPage.tsx","metadata":{"lineCount":194,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/DashboardPage.test.tsx:",{"id":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/DashboardPage.test.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":75,"column":19,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:mockDashboard.mockReset",{"id":"external:mockDashboard.mockReset","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockDashboard.mockReset"}}],["external:mockSessions.mockReset",{"id":"external:mockSessions.mockReset","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockSessions.mockReset"}}],["external:mockProjects.mockReset",{"id":"external:mockProjects.mockReset","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockProjects.mockReset"}}],["external:mockRepos.mockReset",{"id":"external:mockRepos.mockReset","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockRepos.mockReset"}}],["external:mockDashboard.mockResolvedValue",{"id":"external:mockDashboard.mockResolvedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockDashboard.mockResolvedValue"}}],["external:mockSessions.mockResolvedValue",{"id":"external:mockSessions.mockResolvedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockSessions.mockResolvedValue"}}],["external:mockProjects.mockResolvedValue",{"id":"external:mockProjects.mockResolvedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockProjects.mockResolvedValue"}}],["external:mockRepos.mockResolvedValue",{"id":"external:mockRepos.mockResolvedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockRepos.mockResolvedValue"}}],["external:expect(screen.getByText(\"Loading overview...\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"Loading overview...\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"Loading overview...\")).toBeInTheDocument"}}],["external:expect(screen.getAllByText(\"Open sessions\").length).toBeGreaterThan",{"id":"external:expect(screen.getAllByText(\"Open sessions\").length).toBeGreaterThan","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getAllByText(\"Open sessions\").length).toBeGreaterThan"}}],["external:expect(screen.getByText(\"Available projects\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"Available projects\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"Available projects\")).toBeInTheDocument"}}],["external:mockDashboard.mockRejectedValueOnce",{"id":"external:mockDashboard.mockRejectedValueOnce","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockDashboard.mockRejectedValueOnce"}}],["external:mockSessions.mockRejectedValueOnce",{"id":"external:mockSessions.mockRejectedValueOnce","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockSessions.mockRejectedValueOnce"}}],["external:mockProjects.mockRejectedValueOnce",{"id":"external:mockProjects.mockRejectedValueOnce","kind":"external","language":"jsts","metadata":{"unresolvedName":"mockProjects.mockRejectedValueOnce"}}],["file:/workspace/apps/web/src/components/features/dashboard",{"id":"file:/workspace/apps/web/src/components/features/dashboard","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/dashboard"}],["file:/workspace/apps/web/src/components/ui",{"id":"file:/workspace/apps/web/src/components/ui","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/ui"}],["/workspace/apps/web/src/pages/DashboardPage.tsx:HomePage",{"id":"/workspace/apps/web/src/pages/DashboardPage.tsx:HomePage","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/DashboardPage.tsx","symbolName":"HomePage","symbolKind":"function","exported":true,"metadata":{"line":26,"column":25,"cyclomaticComplexity":8,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:Array.isArray",{"id":"external:Array.isArray","kind":"external","language":"jsts","metadata":{"unresolvedName":"Array.isArray"}}],["symbol-name:getDashboardSummary",{"id":"symbol-name:getDashboardSummary","kind":"symbol","language":"jsts","symbolName":"getDashboardSummary","metadata":{"unresolvedName":"getDashboardSummary"}}],["symbol-name:setSummary",{"id":"symbol-name:setSummary","kind":"symbol","language":"jsts","symbolName":"setSummary","metadata":{"unresolvedName":"setSummary"}}],["symbol-name:loadHome",{"id":"symbol-name:loadHome","kind":"symbol","language":"jsts","symbolName":"loadHome","metadata":{"unresolvedName":"loadHome"}}],["symbol-name:useMemo",{"id":"symbol-name:useMemo","kind":"symbol","language":"jsts","symbolName":"useMemo","metadata":{"unresolvedName":"useMemo"}}],["external:safeSessions.filter",{"id":"external:safeSessions.filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"safeSessions.filter"}}],["external:[\"running\", \"building\", \"pending\"].includes",{"id":"external:[\"running\", \"building\", \"pending\"].includes","kind":"external","language":"jsts","metadata":{"unresolvedName":"[\"running\", \"building\", \"pending\"].includes"}}],["external:safeSessions\n\t\t\t\t.filter",{"id":"external:safeSessions\n\t\t\t\t.filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"safeSessions\n\t\t\t\t.filter"}}],["external:[\"stopped\", \"error\"].includes",{"id":"external:[\"stopped\", \"error\"].includes","kind":"external","language":"jsts","metadata":{"unresolvedName":"[\"stopped\", \"error\"].includes"}}],["symbol-name:useDashboardActions",{"id":"symbol-name:useDashboardActions","kind":"symbol","language":"jsts","symbolName":"useDashboardActions","metadata":{"unresolvedName":"useDashboardActions"}}],["/workspace/apps/web/src/pages/DashboardPage.tsx:",{"id":"/workspace/apps/web/src/pages/DashboardPage.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/DashboardPage.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":60,"column":32,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/pages/GitHistoryPage.tsx",{"id":"file:/workspace/apps/web/src/pages/GitHistoryPage.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/pages/GitHistoryPage.tsx","metadata":{"lineCount":234,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/GitHistoryPage.tsx:GitHistoryPage",{"id":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:GitHistoryPage","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/GitHistoryPage.tsx","symbolName":"GitHistoryPage","symbolKind":"function","exported":true,"metadata":{"line":7,"column":31,"cyclomaticComplexity":25,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:setCommits",{"id":"symbol-name:setCommits","kind":"symbol","language":"jsts","symbolName":"setCommits","metadata":{"unresolvedName":"setCommits"}}],["symbol-name:loadHistory",{"id":"symbol-name:loadHistory","kind":"symbol","language":"jsts","symbolName":"loadHistory","metadata":{"unresolvedName":"loadHistory"}}],["symbol-name:setSelectedCommit",{"id":"symbol-name:setSelectedCommit","kind":"symbol","language":"jsts","symbolName":"setSelectedCommit","metadata":{"unresolvedName":"setSelectedCommit"}}],["symbol-name:setDetailStatus",{"id":"symbol-name:setDetailStatus","kind":"symbol","language":"jsts","symbolName":"setDetailStatus","metadata":{"unresolvedName":"setDetailStatus"}}],["external:new Date(dateStr).toLocaleString",{"id":"external:new Date(dateStr).toLocaleString","kind":"external","language":"jsts","metadata":{"unresolvedName":"new Date(dateStr).toLocaleString"}}],["external:commits.map",{"id":"external:commits.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"commits.map"}}],["symbol-name:handleCommitClick",{"id":"symbol-name:handleCommitClick","kind":"symbol","language":"jsts","symbolName":"handleCommitClick","metadata":{"unresolvedName":"handleCommitClick"}}],["external:commit.refs.map",{"id":"external:commit.refs.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"commit.refs.map"}}],["external:commit.message.split",{"id":"external:commit.message.split","kind":"external","language":"jsts","metadata":{"unresolvedName":"commit.message.split"}}],["symbol-name:formatDate",{"id":"symbol-name:formatDate","kind":"symbol","language":"jsts","symbolName":"formatDate","metadata":{"unresolvedName":"formatDate"}}],["external:commitDetail.parents.map",{"id":"external:commitDetail.parents.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"commitDetail.parents.map"}}],["external:parent.substring",{"id":"external:parent.substring","kind":"external","language":"jsts","metadata":{"unresolvedName":"parent.substring"}}],["/workspace/apps/web/src/pages/GitHistoryPage.tsx:",{"id":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/GitHistoryPage.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":121,"column":26,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/GitHistoryPage.tsx:handleCommitClick",{"id":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:handleCommitClick","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/GitHistoryPage.tsx","symbolName":"handleCommitClick","symbolKind":"function","exported":false,"metadata":{"line":38,"column":29,"cyclomaticComplexity":4,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/GitHistoryPage.tsx:handleCloseDetail",{"id":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:handleCloseDetail","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/GitHistoryPage.tsx","symbolName":"handleCloseDetail","symbolKind":"function","exported":false,"metadata":{"line":51,"column":29,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/GitHistoryPage.tsx:formatDate",{"id":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:formatDate","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/GitHistoryPage.tsx","symbolName":"formatDate","symbolKind":"function","exported":false,"metadata":{"line":57,"column":22,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/pages/GitRepositoriesPage.tsx",{"id":"file:/workspace/apps/web/src/pages/GitRepositoriesPage.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx","metadata":{"lineCount":153,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:GitRepositoriesPage",{"id":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:GitRepositoriesPage","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx","symbolName":"GitRepositoriesPage","symbolKind":"function","exported":true,"metadata":{"line":11,"column":36,"cyclomaticComplexity":15,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:",{"id":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":32,"column":12,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:handleDelete",{"id":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:handleDelete","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx","symbolName":"handleDelete","symbolKind":"function","exported":false,"metadata":{"line":36,"column":23,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["file:/workspace/apps/web/src/pages/PlaceholderPage.tsx",{"id":"file:/workspace/apps/web/src/pages/PlaceholderPage.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/pages/PlaceholderPage.tsx","metadata":{"lineCount":38,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/PlaceholderPage.tsx:PlaceholderPage",{"id":"/workspace/apps/web/src/pages/PlaceholderPage.tsx:PlaceholderPage","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/PlaceholderPage.tsx","symbolName":"PlaceholderPage","symbolKind":"function","exported":true,"metadata":{"line":1,"column":32,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/PlaceholderPage.tsx:NotFoundPage",{"id":"/workspace/apps/web/src/pages/PlaceholderPage.tsx:NotFoundPage","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/PlaceholderPage.tsx","symbolName":"NotFoundPage","symbolKind":"function","exported":true,"metadata":{"line":10,"column":29,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/PlaceholderPage.tsx:LoginRedirectPage",{"id":"/workspace/apps/web/src/pages/PlaceholderPage.tsx:LoginRedirectPage","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/PlaceholderPage.tsx","symbolName":"LoginRedirectPage","symbolKind":"function","exported":true,"metadata":{"line":23,"column":34,"cyclomaticComplexity":2,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["external:new URLSearchParams(window.location.search).get",{"id":"external:new URLSearchParams(window.location.search).get","kind":"external","language":"jsts","metadata":{"unresolvedName":"new URLSearchParams(window.location.search).get"}}],["file:/workspace/apps/web/src/pages/ProfilePage.tsx",{"id":"file:/workspace/apps/web/src/pages/ProfilePage.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/pages/ProfilePage.tsx","metadata":{"lineCount":195,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/ProfilePage.tsx:ProfilePage",{"id":"/workspace/apps/web/src/pages/ProfilePage.tsx:ProfilePage","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/ProfilePage.tsx","symbolName":"ProfilePage","symbolKind":"function","exported":true,"metadata":{"line":10,"column":28,"cyclomaticComplexity":19,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:getProfile",{"id":"symbol-name:getProfile","kind":"symbol","language":"jsts","symbolName":"getProfile","metadata":{"unresolvedName":"getProfile"}}],["symbol-name:setProfile",{"id":"symbol-name:setProfile","kind":"symbol","language":"jsts","symbolName":"setProfile","metadata":{"unresolvedName":"setProfile"}}],["symbol-name:setEmail",{"id":"symbol-name:setEmail","kind":"symbol","language":"jsts","symbolName":"setEmail","metadata":{"unresolvedName":"setEmail"}}],["symbol-name:loadProfile",{"id":"symbol-name:loadProfile","kind":"symbol","language":"jsts","symbolName":"loadProfile","metadata":{"unresolvedName":"loadProfile"}}],["external:email.includes",{"id":"external:email.includes","kind":"external","language":"jsts","metadata":{"unresolvedName":"email.includes"}}],["symbol-name:updateProfile",{"id":"symbol-name:updateProfile","kind":"symbol","language":"jsts","symbolName":"updateProfile","metadata":{"unresolvedName":"updateProfile"}}],["external:email.trim",{"id":"external:email.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"email.trim"}}],["symbol-name:refreshSession",{"id":"symbol-name:refreshSession","kind":"symbol","language":"jsts","symbolName":"refreshSession","metadata":{"unresolvedName":"refreshSession"}}],["external:file.type.startsWith",{"id":"external:file.type.startsWith","kind":"external","language":"jsts","metadata":{"unresolvedName":"file.type.startsWith"}}],["symbol-name:uploadAvatar",{"id":"symbol-name:uploadAvatar","kind":"symbol","language":"jsts","symbolName":"uploadAvatar","metadata":{"unresolvedName":"uploadAvatar"}}],["external:profile.name.charAt(0).toUpperCase",{"id":"external:profile.name.charAt(0).toUpperCase","kind":"external","language":"jsts","metadata":{"unresolvedName":"profile.name.charAt(0).toUpperCase"}}],["external:profile.name.charAt",{"id":"external:profile.name.charAt","kind":"external","language":"jsts","metadata":{"unresolvedName":"profile.name.charAt"}}],["external:fileInputRef.current?.click",{"id":"external:fileInputRef.current?.click","kind":"external","language":"jsts","metadata":{"unresolvedName":"fileInputRef.current?.click"}}],["/workspace/apps/web/src/pages/ProfilePage.tsx:",{"id":"/workspace/apps/web/src/pages/ProfilePage.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/ProfilePage.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":62,"column":5,"cyclomaticComplexity":5,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/pages/ProjectSettingsPage.tsx",{"id":"file:/workspace/apps/web/src/pages/ProjectSettingsPage.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx","metadata":{"lineCount":149,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:GeneralSettings",{"id":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:GeneralSettings","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx","symbolName":"GeneralSettings","symbolKind":"function","exported":false,"metadata":{"line":17,"column":6,"cyclomaticComplexity":7,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:setDescription",{"id":"symbol-name:setDescription","kind":"symbol","language":"jsts","symbolName":"setDescription","metadata":{"unresolvedName":"setDescription"}}],["/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:handleSave",{"id":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:handleSave","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx","symbolName":"handleSave","symbolKind":"function","exported":false,"metadata":{"line":24,"column":22,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:handleDelete",{"id":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:handleDelete","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx","symbolName":"handleDelete","symbolKind":"function","exported":false,"metadata":{"line":38,"column":24,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:ProjectSettingsPage",{"id":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:ProjectSettingsPage","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx","symbolName":"ProjectSettingsPage","symbolKind":"function","exported":true,"metadata":{"line":100,"column":46,"cyclomaticComplexity":5,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:setProject",{"id":"symbol-name:setProject","kind":"symbol","language":"jsts","symbolName":"setProject","metadata":{"unresolvedName":"setProject"}}],["symbol-name:fetchProject",{"id":"symbol-name:fetchProject","kind":"symbol","language":"jsts","symbolName":"fetchProject","metadata":{"unresolvedName":"fetchProject"}}],["/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:",{"id":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":106,"column":13,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:fetchProject",{"id":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:fetchProject","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx","symbolName":"fetchProject","symbolKind":"function","exported":false,"metadata":{"line":107,"column":26,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx",{"id":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx","metadata":{"lineCount":206,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/pages/ProjectsPage.tsx",{"id":"file:/workspace/apps/web/src/pages/ProjectsPage.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/pages/ProjectsPage.tsx","metadata":{"lineCount":207,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/ProjectsPage.test.tsx:",{"id":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":200,"column":17,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:vi.spyOn(projectsApi, \"listProjects\").mockImplementation",{"id":"external:vi.spyOn(projectsApi, \"listProjects\").mockImplementation","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.spyOn(projectsApi, \"listProjects\").mockImplementation"}}],["symbol-name:renderWithRouter",{"id":"symbol-name:renderWithRouter","kind":"symbol","language":"jsts","symbolName":"renderWithRouter","metadata":{"unresolvedName":"renderWithRouter"}}],["external:expect(screen.getByText(/loading projects/i)).toBeInTheDocument",{"id":"external:expect(screen.getByText(/loading projects/i)).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(/loading projects/i)).toBeInTheDocument"}}],["external:vi.spyOn(projectsApi, \"listProjects\").mockResolvedValue",{"id":"external:vi.spyOn(projectsApi, \"listProjects\").mockResolvedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.spyOn(projectsApi, \"listProjects\").mockResolvedValue"}}],["external:expect(screen.getByText(\"Alpha Project\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"Alpha Project\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"Alpha Project\")).toBeInTheDocument"}}],["external:expect(screen.getByText(\"Beta Project\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"Beta Project\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"Beta Project\")).toBeInTheDocument"}}],["external:expect(screen.getByText(\"First project\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"First project\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"First project\")).toBeInTheDocument"}}],["external:expect(screen.getByText(/no projects yet/i)).toBeInTheDocument",{"id":"external:expect(screen.getByText(/no projects yet/i)).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(/no projects yet/i)).toBeInTheDocument"}}],["external:vi.spyOn(projectsApi, \"listProjects\").mockRejectedValue",{"id":"external:vi.spyOn(projectsApi, \"listProjects\").mockRejectedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.spyOn(projectsApi, \"listProjects\").mockRejectedValue"}}],["external:expect(screen.getByText(/failed to load projects/i)).toBeInTheDocument",{"id":"external:expect(screen.getByText(/failed to load projects/i)).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(/failed to load projects/i)).toBeInTheDocument"}}],["external:expect(screen.getByRole(\"button\", { name: /retry/i })).toBeInTheDocument",{"id":"external:expect(screen.getByRole(\"button\", { name: /retry/i })).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByRole(\"button\", { name: /retry/i })).toBeInTheDocument"}}],["external:vi\n\t\t\t.spyOn(projectsApi, \"listProjects\")\n\t\t\t.mockResolvedValue",{"id":"external:vi\n\t\t\t.spyOn(projectsApi, \"listProjects\")\n\t\t\t.mockResolvedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi\n\t\t\t.spyOn(projectsApi, \"listProjects\")\n\t\t\t.mockResolvedValue"}}],["external:vi\n\t\t\t.spyOn",{"id":"external:vi\n\t\t\t.spyOn","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi\n\t\t\t.spyOn"}}],["external:vi\n\t\t\t.spyOn(projectsApi, \"createProject\")\n\t\t\t.mockResolvedValue",{"id":"external:vi\n\t\t\t.spyOn(projectsApi, \"createProject\")\n\t\t\t.mockResolvedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi\n\t\t\t.spyOn(projectsApi, \"createProject\")\n\t\t\t.mockResolvedValue"}}],["external:expect(screen.getByText(/project name is required/i)).toBeInTheDocument",{"id":"external:expect(screen.getByText(/project name is required/i)).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(/project name is required/i)).toBeInTheDocument"}}],["external:screen\n\t\t\t.getByText(\"Alpha Project\")\n\t\t\t.closest",{"id":"external:screen\n\t\t\t.getByText(\"Alpha Project\")\n\t\t\t.closest","kind":"external","language":"jsts","metadata":{"unresolvedName":"screen\n\t\t\t.getByText(\"Alpha Project\")\n\t\t\t.closest"}}],["external:screen\n\t\t\t.getByText",{"id":"external:screen\n\t\t\t.getByText","kind":"external","language":"jsts","metadata":{"unresolvedName":"screen\n\t\t\t.getByText"}}],["external:within(alphaCard).getByRole",{"id":"external:within(alphaCard).getByRole","kind":"external","language":"jsts","metadata":{"unresolvedName":"within(alphaCard).getByRole"}}],["symbol-name:within",{"id":"symbol-name:within","kind":"symbol","language":"jsts","symbolName":"within","metadata":{"unresolvedName":"within"}}],["external:expect(settingsLink).toBeInTheDocument",{"id":"external:expect(settingsLink).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(settingsLink).toBeInTheDocument"}}],["external:expect(settingsLink).toHaveAttribute",{"id":"external:expect(settingsLink).toHaveAttribute","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(settingsLink).toHaveAttribute"}}],["external:alphaCard.querySelector",{"id":"external:alphaCard.querySelector","kind":"external","language":"jsts","metadata":{"unresolvedName":"alphaCard.querySelector"}}],["external:expect(workspaceLink).toBeInTheDocument",{"id":"external:expect(workspaceLink).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(workspaceLink).toBeInTheDocument"}}],["external:expect(workspaceLink).toHaveAttribute",{"id":"external:expect(workspaceLink).toHaveAttribute","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(workspaceLink).toHaveAttribute"}}],["external:actions.querySelectorAll",{"id":"external:actions.querySelectorAll","kind":"external","language":"jsts","metadata":{"unresolvedName":"actions.querySelectorAll"}}],["external:expect(lastAction).toBe",{"id":"external:expect(lastAction).toBe","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(lastAction).toBe"}}],["external:vi\n\t\t\t.spyOn(projectsApi, \"deleteProject\")\n\t\t\t.mockResolvedValue",{"id":"external:vi\n\t\t\t.spyOn(projectsApi, \"deleteProject\")\n\t\t\t.mockResolvedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi\n\t\t\t.spyOn(projectsApi, \"deleteProject\")\n\t\t\t.mockResolvedValue"}}],["external:expect(within(alphaCard).getByText(/are you sure/i)).toBeInTheDocument",{"id":"external:expect(within(alphaCard).getByText(/are you sure/i)).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(within(alphaCard).getByText(/are you sure/i)).toBeInTheDocument"}}],["external:within(alphaCard).getByText",{"id":"external:within(alphaCard).getByText","kind":"external","language":"jsts","metadata":{"unresolvedName":"within(alphaCard).getByText"}}],["external:expect(deleteMock).toHaveBeenCalledWith",{"id":"external:expect(deleteMock).toHaveBeenCalledWith","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(deleteMock).toHaveBeenCalledWith"}}],["/workspace/apps/web/src/pages/ProjectsPage.tsx:ProjectsPage",{"id":"/workspace/apps/web/src/pages/ProjectsPage.tsx:ProjectsPage","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/ProjectsPage.tsx","symbolName":"ProjectsPage","symbolKind":"function","exported":true,"metadata":{"line":16,"column":29,"cyclomaticComplexity":16,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:createProject",{"id":"symbol-name:createProject","kind":"symbol","language":"jsts","symbolName":"createProject","metadata":{"unresolvedName":"createProject"}}],["symbol-name:closeCreate",{"id":"symbol-name:closeCreate","kind":"symbol","language":"jsts","symbolName":"closeCreate","metadata":{"unresolvedName":"closeCreate"}}],["symbol-name:deleteProject",{"id":"symbol-name:deleteProject","kind":"symbol","language":"jsts","symbolName":"deleteProject","metadata":{"unresolvedName":"deleteProject"}}],["/workspace/apps/web/src/pages/ProjectsPage.tsx:",{"id":"/workspace/apps/web/src/pages/ProjectsPage.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/ProjectsPage.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":37,"column":13,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/ProjectsPage.tsx:openCreate",{"id":"/workspace/apps/web/src/pages/ProjectsPage.tsx:openCreate","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/ProjectsPage.tsx","symbolName":"openCreate","symbolKind":"function","exported":false,"metadata":{"line":41,"column":22,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/ProjectsPage.tsx:closeCreate",{"id":"/workspace/apps/web/src/pages/ProjectsPage.tsx:closeCreate","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/ProjectsPage.tsx","symbolName":"closeCreate","symbolKind":"function","exported":false,"metadata":{"line":48,"column":23,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/ProjectsPage.tsx:handleCreate",{"id":"/workspace/apps/web/src/pages/ProjectsPage.tsx:handleCreate","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/ProjectsPage.tsx","symbolName":"handleCreate","symbolKind":"function","exported":false,"metadata":{"line":53,"column":24,"cyclomaticComplexity":4,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/ProjectsPage.tsx:handleDelete",{"id":"/workspace/apps/web/src/pages/ProjectsPage.tsx:handleDelete","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/ProjectsPage.tsx","symbolName":"handleDelete","symbolKind":"function","exported":false,"metadata":{"line":75,"column":24,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx",{"id":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","metadata":{"lineCount":210,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["file:/workspace/apps/web/src/components/features/git",{"id":"file:/workspace/apps/web/src/components/features/git","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/git"}],["/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace",{"id":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","symbolName":"RepoWorkspace","symbolKind":"function","exported":true,"metadata":{"line":27,"column":30,"cyclomaticComplexity":25,"maxNestingDepth":3,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["symbol-name:setSelectedRepoId",{"id":"symbol-name:setSelectedRepoId","kind":"symbol","language":"jsts","symbolName":"setSelectedRepoId","metadata":{"unresolvedName":"setSelectedRepoId"}}],["external:response.data.branches.map",{"id":"external:response.data.branches.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"response.data.branches.map"}}],["symbol-name:setGitStatus",{"id":"symbol-name:setGitStatus","kind":"symbol","language":"jsts","symbolName":"setGitStatus","metadata":{"unresolvedName":"setGitStatus"}}],["symbol-name:loadProject",{"id":"symbol-name:loadProject","kind":"symbol","language":"jsts","symbolName":"loadProject","metadata":{"unresolvedName":"loadProject"}}],["symbol-name:loadGitStatus",{"id":"symbol-name:loadGitStatus","kind":"symbol","language":"jsts","symbolName":"loadGitStatus","metadata":{"unresolvedName":"loadGitStatus"}}],["/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:",{"id":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":193,"column":19,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:handleRepoChange",{"id":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:handleRepoChange","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","symbolName":"handleRepoChange","symbolKind":"function","exported":false,"metadata":{"line":122,"column":27,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["file:/workspace/apps/web/src/pages/SessionsPage.tsx",{"id":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/pages/SessionsPage.tsx","metadata":{"lineCount":215,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["file:/workspace/apps/web/src/components/features/session",{"id":"file:/workspace/apps/web/src/components/features/session","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/session"}],["/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage",{"id":"/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/SessionsPage.tsx","symbolName":"SessionsPage","symbolKind":"function","exported":true,"metadata":{"line":17,"column":29,"cyclomaticComplexity":19,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["symbol-name:setLastSessionId",{"id":"symbol-name:setLastSessionId","kind":"symbol","language":"jsts","symbolName":"setLastSessionId","metadata":{"unresolvedName":"setLastSessionId"}}],["external:sessions\n\t\t\t\t.filter((s) => [\"stopped\", \"error\"].includes(s.status))\n\t\t\t\t.slice",{"id":"external:sessions\n\t\t\t\t.filter((s) => [\"stopped\", \"error\"].includes(s.status))\n\t\t\t\t.slice","kind":"external","language":"jsts","metadata":{"unresolvedName":"sessions\n\t\t\t\t.filter((s) => [\"stopped\", \"error\"].includes(s.status))\n\t\t\t\t.slice"}}],["external:sessions\n\t\t\t\t.filter",{"id":"external:sessions\n\t\t\t\t.filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"sessions\n\t\t\t\t.filter"}}],["external:sessions.find",{"id":"external:sessions.find","kind":"external","language":"jsts","metadata":{"unresolvedName":"sessions.find"}}],["/workspace/apps/web/src/pages/SessionsPage.tsx:",{"id":"/workspace/apps/web/src/pages/SessionsPage.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/SessionsPage.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":98,"column":39,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/pages/SessionsPage.tsx:loadProjects",{"id":"/workspace/apps/web/src/pages/SessionsPage.tsx:loadProjects","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/SessionsPage.tsx","symbolName":"loadProjects","symbolKind":"function","exported":false,"metadata":{"line":45,"column":24,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/pages/SessionsPage.tsx:loadToolTypes",{"id":"/workspace/apps/web/src/pages/SessionsPage.tsx:loadToolTypes","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/SessionsPage.tsx","symbolName":"loadToolTypes","symbolKind":"function","exported":false,"metadata":{"line":56,"column":25,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["file:/workspace/apps/web/src/pages/SettingsPage.tsx",{"id":"file:/workspace/apps/web/src/pages/SettingsPage.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/pages/SettingsPage.tsx","metadata":{"lineCount":167,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/SettingsPage.tsx:SettingsPage",{"id":"/workspace/apps/web/src/pages/SettingsPage.tsx:SettingsPage","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/SettingsPage.tsx","symbolName":"SettingsPage","symbolKind":"function","exported":true,"metadata":{"line":29,"column":29,"cyclomaticComplexity":10,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:loadConfig",{"id":"symbol-name:loadConfig","kind":"symbol","language":"jsts","symbolName":"loadConfig","metadata":{"unresolvedName":"loadConfig"}}],["external:window.setTimeout",{"id":"external:window.setTimeout","kind":"external","language":"jsts","metadata":{"unresolvedName":"window.setTimeout"}}],["external:location.pathname.split(\"/\").filter",{"id":"external:location.pathname.split(\"/\").filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"location.pathname.split(\"/\").filter"}}],["external:location.pathname.split",{"id":"external:location.pathname.split","kind":"external","language":"jsts","metadata":{"unresolvedName":"location.pathname.split"}}],["external:location.pathname.endsWith",{"id":"external:location.pathname.endsWith","kind":"external","language":"jsts","metadata":{"unresolvedName":"location.pathname.endsWith"}}],["external:TABS.map",{"id":"external:TABS.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"TABS.map"}}],["/workspace/apps/web/src/pages/SettingsPage.tsx:",{"id":"/workspace/apps/web/src/pages/SettingsPage.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/SettingsPage.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":51,"column":13,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/SettingsPage.tsx:handleChange",{"id":"/workspace/apps/web/src/pages/SettingsPage.tsx:handleChange","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/SettingsPage.tsx","symbolName":"handleChange","symbolKind":"function","exported":false,"metadata":{"line":55,"column":24,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/SettingsPage.tsx:handleSave",{"id":"/workspace/apps/web/src/pages/SettingsPage.tsx:handleSave","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/SettingsPage.tsx","symbolName":"handleSave","symbolKind":"function","exported":false,"metadata":{"line":60,"column":22,"cyclomaticComplexity":3,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/SettingsPage.tsx:GeneralSettingsTab",{"id":"/workspace/apps/web/src/pages/SettingsPage.tsx:GeneralSettingsTab","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/SettingsPage.tsx","symbolName":"GeneralSettingsTab","symbolKind":"function","exported":true,"metadata":{"line":131,"column":35,"cyclomaticComplexity":10,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:useOutletContext",{"id":"symbol-name:useOutletContext","kind":"symbol","language":"jsts","symbolName":"useOutletContext","metadata":{"unresolvedName":"useOutletContext"}}],["external:THEME_OPTIONS.map",{"id":"external:THEME_OPTIONS.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"THEME_OPTIONS.map"}}],["file:/workspace/apps/web/src/pages/SshKeysPage.tsx",{"id":"file:/workspace/apps/web/src/pages/SshKeysPage.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/pages/SshKeysPage.tsx","metadata":{"lineCount":143,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/SshKeysPage.tsx:SSHKeysPage",{"id":"/workspace/apps/web/src/pages/SshKeysPage.tsx:SSHKeysPage","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/SshKeysPage.tsx","symbolName":"SSHKeysPage","symbolKind":"function","exported":true,"metadata":{"line":6,"column":28,"cyclomaticComplexity":10,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:setKeys",{"id":"symbol-name:setKeys","kind":"symbol","language":"jsts","symbolName":"setKeys","metadata":{"unresolvedName":"setKeys"}}],["external:newKeyName.trim",{"id":"external:newKeyName.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"newKeyName.trim"}}],["symbol-name:setGenerating",{"id":"symbol-name:setGenerating","kind":"symbol","language":"jsts","symbolName":"setGenerating","metadata":{"unresolvedName":"setGenerating"}}],["symbol-name:createSSHKey",{"id":"symbol-name:createSSHKey","kind":"symbol","language":"jsts","symbolName":"createSSHKey","metadata":{"unresolvedName":"createSSHKey"}}],["symbol-name:setNewKeyName",{"id":"symbol-name:setNewKeyName","kind":"symbol","language":"jsts","symbolName":"setNewKeyName","metadata":{"unresolvedName":"setNewKeyName"}}],["symbol-name:deleteSSHKey",{"id":"symbol-name:deleteSSHKey","kind":"symbol","language":"jsts","symbolName":"deleteSSHKey","metadata":{"unresolvedName":"deleteSSHKey"}}],["external:keys.map",{"id":"external:keys.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"keys.map"}}],["external:new Date(key.created_at).toLocaleDateString",{"id":"external:new Date(key.created_at).toLocaleDateString","kind":"external","language":"jsts","metadata":{"unresolvedName":"new Date(key.created_at).toLocaleDateString"}}],["external:key.public_key.substring",{"id":"external:key.public_key.substring","kind":"external","language":"jsts","metadata":{"unresolvedName":"key.public_key.substring"}}],["symbol-name:copyToClipboard",{"id":"symbol-name:copyToClipboard","kind":"symbol","language":"jsts","symbolName":"copyToClipboard","metadata":{"unresolvedName":"copyToClipboard"}}],["/workspace/apps/web/src/pages/SshKeysPage.tsx:",{"id":"/workspace/apps/web/src/pages/SshKeysPage.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/SshKeysPage.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":14,"column":13,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/SshKeysPage.tsx:loadKeys",{"id":"/workspace/apps/web/src/pages/SshKeysPage.tsx:loadKeys","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/SshKeysPage.tsx","symbolName":"loadKeys","symbolKind":"function","exported":false,"metadata":{"line":18,"column":3,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/SshKeysPage.tsx:handleGenerate",{"id":"/workspace/apps/web/src/pages/SshKeysPage.tsx:handleGenerate","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/SshKeysPage.tsx","symbolName":"handleGenerate","symbolKind":"function","exported":false,"metadata":{"line":31,"column":3,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/SshKeysPage.tsx:handleDelete",{"id":"/workspace/apps/web/src/pages/SshKeysPage.tsx:handleDelete","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/SshKeysPage.tsx","symbolName":"handleDelete","symbolKind":"function","exported":false,"metadata":{"line":47,"column":3,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/SshKeysPage.tsx:copyToClipboard",{"id":"/workspace/apps/web/src/pages/SshKeysPage.tsx:copyToClipboard","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/SshKeysPage.tsx","symbolName":"copyToClipboard","symbolKind":"function","exported":false,"metadata":{"line":58,"column":3,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/pages/TerminalPage.tsx",{"id":"file:/workspace/apps/web/src/pages/TerminalPage.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/pages/TerminalPage.tsx","metadata":{"lineCount":39,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/TerminalPage.tsx:TerminalPage",{"id":"/workspace/apps/web/src/pages/TerminalPage.tsx:TerminalPage","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/TerminalPage.tsx","symbolName":"TerminalPage","symbolKind":"function","exported":true,"metadata":{"line":6,"column":39,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/pages/ToolConfigsPage.tsx",{"id":"file:/workspace/apps/web/src/pages/ToolConfigsPage.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx","metadata":{"lineCount":206,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["file:/workspace/apps/web/src/components/features/tool-configs",{"id":"file:/workspace/apps/web/src/components/features/tool-configs","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-configs"}],["/workspace/apps/web/src/pages/ToolConfigsPage.tsx:ToolConfigsPage",{"id":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:ToolConfigsPage","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx","symbolName":"ToolConfigsPage","symbolKind":"function","exported":true,"metadata":{"line":21,"column":32,"cyclomaticComplexity":12,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["symbol-name:setShowForm",{"id":"symbol-name:setShowForm","kind":"symbol","language":"jsts","symbolName":"setShowForm","metadata":{"unresolvedName":"setShowForm"}}],["symbol-name:setEditingConfig",{"id":"symbol-name:setEditingConfig","kind":"symbol","language":"jsts","symbolName":"setEditingConfig","metadata":{"unresolvedName":"setEditingConfig"}}],["external:toolTypes.find",{"id":"external:toolTypes.find","kind":"external","language":"jsts","metadata":{"unresolvedName":"toolTypes.find"}}],["external:configs.filter",{"id":"external:configs.filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"configs.filter"}}],["external:selectedTool.interfaces?.join",{"id":"external:selectedTool.interfaces?.join","kind":"external","language":"jsts","metadata":{"unresolvedName":"selectedTool.interfaces?.join"}}],["/workspace/apps/web/src/pages/ToolConfigsPage.tsx:",{"id":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":197,"column":16,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/ToolConfigsPage.tsx:handleSubmit",{"id":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:handleSubmit","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx","symbolName":"handleSubmit","symbolKind":"function","exported":false,"metadata":{"line":51,"column":23,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/ToolConfigsPage.tsx:handleEdit",{"id":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:handleEdit","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx","symbolName":"handleEdit","symbolKind":"function","exported":false,"metadata":{"line":75,"column":21,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/ToolConfigsPage.tsx:handleDelete",{"id":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:handleDelete","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx","symbolName":"handleDelete","symbolKind":"function","exported":false,"metadata":{"line":81,"column":23,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["file:/workspace/apps/web/src/pages/ToolTypesPage.tsx",{"id":"file:/workspace/apps/web/src/pages/ToolTypesPage.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/pages/ToolTypesPage.tsx","metadata":{"lineCount":144,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/components/features/tool-types",{"id":"file:/workspace/apps/web/src/components/features/tool-types","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-types"}],["/workspace/apps/web/src/pages/ToolTypesPage.tsx:ToolTypesPage",{"id":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:ToolTypesPage","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/ToolTypesPage.tsx","symbolName":"ToolTypesPage","symbolKind":"function","exported":true,"metadata":{"line":20,"column":30,"cyclomaticComplexity":8,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:setEditingToolType",{"id":"symbol-name:setEditingToolType","kind":"symbol","language":"jsts","symbolName":"setEditingToolType","metadata":{"unresolvedName":"setEditingToolType"}}],["symbol-name:setDialogMode",{"id":"symbol-name:setDialogMode","kind":"symbol","language":"jsts","symbolName":"setDialogMode","metadata":{"unresolvedName":"setDialogMode"}}],["symbol-name:closeDialog",{"id":"symbol-name:closeDialog","kind":"symbol","language":"jsts","symbolName":"closeDialog","metadata":{"unresolvedName":"closeDialog"}}],["/workspace/apps/web/src/pages/ToolTypesPage.tsx:",{"id":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/ToolTypesPage.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":42,"column":12,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/ToolTypesPage.tsx:openCreate",{"id":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:openCreate","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/ToolTypesPage.tsx","symbolName":"openCreate","symbolKind":"function","exported":false,"metadata":{"line":46,"column":21,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/ToolTypesPage.tsx:openEdit",{"id":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:openEdit","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/ToolTypesPage.tsx","symbolName":"openEdit","symbolKind":"function","exported":false,"metadata":{"line":51,"column":19,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/ToolTypesPage.tsx:closeDialog",{"id":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:closeDialog","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/ToolTypesPage.tsx","symbolName":"closeDialog","symbolKind":"function","exported":false,"metadata":{"line":56,"column":22,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/ToolTypesPage.tsx:handleSubmit",{"id":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:handleSubmit","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/ToolTypesPage.tsx","symbolName":"handleSubmit","symbolKind":"function","exported":false,"metadata":{"line":61,"column":23,"cyclomaticComplexity":3,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/ToolTypesPage.tsx:handleDelete",{"id":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:handleDelete","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/ToolTypesPage.tsx","symbolName":"handleDelete","symbolKind":"function","exported":false,"metadata":{"line":73,"column":23,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx",{"id":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","metadata":{"lineCount":528,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/pages/ToolWorkshopPage.tsx",{"id":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/pages/ToolWorkshopPage.tsx","metadata":{"lineCount":78,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:",{"id":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":520,"column":23,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:vi.spyOn(toolTypesApi, \"listToolTypes\").mockImplementation",{"id":"external:vi.spyOn(toolTypesApi, \"listToolTypes\").mockImplementation","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"listToolTypes\").mockImplementation"}}],["external:vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockImplementation",{"id":"external:vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockImplementation","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockImplementation"}}],["external:vi.spyOn(configFoldersApi, \"listConfigFolders\").mockImplementation",{"id":"external:vi.spyOn(configFoldersApi, \"listConfigFolders\").mockImplementation","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.spyOn(configFoldersApi, \"listConfigFolders\").mockImplementation"}}],["external:expect(screen.getByText(/loading/i)).toBeInTheDocument",{"id":"external:expect(screen.getByText(/loading/i)).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(/loading/i)).toBeInTheDocument"}}],["external:vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue",{"id":"external:vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue"}}],["external:vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue",{"id":"external:vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue"}}],["external:vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue",{"id":"external:vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue"}}],["external:expect(screen.getByText(\"Custom Tool\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"Custom Tool\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"Custom Tool\")).toBeInTheDocument"}}],["external:expect(screen.getByText(\"OPENAI_API_KEY\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"OPENAI_API_KEY\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"OPENAI_API_KEY\")).toBeInTheDocument"}}],["external:expect(screen.getByText(\"advanced-config\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"advanced-config\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"advanced-config\")).toBeInTheDocument"}}],["external:expect(screen.getByText(\"my-dotfiles\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"my-dotfiles\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"my-dotfiles\")).toBeInTheDocument"}}],["external:expect(screen.getByText(\"project-configs\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"project-configs\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"project-configs\")).toBeInTheDocument"}}],["external:expect(screen.getByLabelText(\"Name *\")).toBeInTheDocument",{"id":"external:expect(screen.getByLabelText(\"Name *\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByLabelText(\"Name *\")).toBeInTheDocument"}}],["external:expect(screen.getByLabelText(\"Display Name *\")).toBeInTheDocument",{"id":"external:expect(screen.getByLabelText(\"Display Name *\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByLabelText(\"Display Name *\")).toBeInTheDocument"}}],["external:vi.spyOn(toolTypesApi, \"createToolType\").mockResolvedValue",{"id":"external:vi.spyOn(toolTypesApi, \"createToolType\").mockResolvedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"createToolType\").mockResolvedValue"}}],["external:expect(screen.getByText(/readiness probe command/i)).toBeInTheDocument",{"id":"external:expect(screen.getByText(/readiness probe command/i)).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(/readiness probe command/i)).toBeInTheDocument"}}],["external:expect(screen.getByText(/timeout/i)).toBeInTheDocument",{"id":"external:expect(screen.getByText(/timeout/i)).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(/timeout/i)).toBeInTheDocument"}}],["external:expect(screen.getByText(/interval/i)).toBeInTheDocument",{"id":"external:expect(screen.getByText(/interval/i)).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(/interval/i)).toBeInTheDocument"}}],["external:expect(screen.getByLabelText(/key/i)).toBeInTheDocument",{"id":"external:expect(screen.getByLabelText(/key/i)).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByLabelText(/key/i)).toBeInTheDocument"}}],["external:expect(screen.getByLabelText(/value/i)).toBeInTheDocument",{"id":"external:expect(screen.getByLabelText(/value/i)).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByLabelText(/value/i)).toBeInTheDocument"}}],["external:vi.spyOn(toolConfigsApi, \"createToolConfig\").mockResolvedValue",{"id":"external:vi.spyOn(toolConfigsApi, \"createToolConfig\").mockResolvedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.spyOn(toolConfigsApi, \"createToolConfig\").mockResolvedValue"}}],["external:expect(configsListMock).toHaveBeenCalledTimes",{"id":"external:expect(configsListMock).toHaveBeenCalledTimes","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(configsListMock).toHaveBeenCalledTimes"}}],["external:expect(screen.getByLabelText(\"Mount Path *\")).toBeInTheDocument",{"id":"external:expect(screen.getByLabelText(\"Mount Path *\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByLabelText(\"Mount Path *\")).toBeInTheDocument"}}],["external:vi.spyOn(configFoldersApi, \"createConfigFolder\").mockResolvedValue",{"id":"external:vi.spyOn(configFoldersApi, \"createConfigFolder\").mockResolvedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.spyOn(configFoldersApi, \"createConfigFolder\").mockResolvedValue"}}],["external:expect(foldersListMock).toHaveBeenCalledTimes",{"id":"external:expect(foldersListMock).toHaveBeenCalledTimes","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(foldersListMock).toHaveBeenCalledTimes"}}],["external:expect(screen.getByText(\"Active\")).toBeInTheDocument",{"id":"external:expect(screen.getByText(\"Active\")).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(\"Active\")).toBeInTheDocument"}}],["external:vi.spyOn(toolTypesApi, \"listToolTypes\").mockRejectedValue",{"id":"external:vi.spyOn(toolTypesApi, \"listToolTypes\").mockRejectedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"listToolTypes\").mockRejectedValue"}}],["external:vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockRejectedValue",{"id":"external:vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockRejectedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockRejectedValue"}}],["external:vi.spyOn(configFoldersApi, \"listConfigFolders\").mockRejectedValue",{"id":"external:vi.spyOn(configFoldersApi, \"listConfigFolders\").mockRejectedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.spyOn(configFoldersApi, \"listConfigFolders\").mockRejectedValue"}}],["external:expect(screen.getByText(/failed to load/i)).toBeInTheDocument",{"id":"external:expect(screen.getByText(/failed to load/i)).toBeInTheDocument","kind":"external","language":"jsts","metadata":{"unresolvedName":"expect(screen.getByText(/failed to load/i)).toBeInTheDocument"}}],["external:vi.spyOn(toolTypesApi, \"listToolTypes\")\n .mockRejectedValueOnce",{"id":"external:vi.spyOn(toolTypesApi, \"listToolTypes\")\n .mockRejectedValueOnce","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"listToolTypes\")\n .mockRejectedValueOnce"}}],["external:vi.spyOn(toolConfigsApi, \"listToolConfigs\")\n .mockRejectedValueOnce",{"id":"external:vi.spyOn(toolConfigsApi, \"listToolConfigs\")\n .mockRejectedValueOnce","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.spyOn(toolConfigsApi, \"listToolConfigs\")\n .mockRejectedValueOnce"}}],["external:vi.spyOn(configFoldersApi, \"listConfigFolders\")\n .mockRejectedValueOnce",{"id":"external:vi.spyOn(configFoldersApi, \"listConfigFolders\")\n .mockRejectedValueOnce","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.spyOn(configFoldersApi, \"listConfigFolders\")\n .mockRejectedValueOnce"}}],["external:vi.spyOn(toolTypesApi, \"deleteToolType\").mockResolvedValue",{"id":"external:vi.spyOn(toolTypesApi, \"deleteToolType\").mockResolvedValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"deleteToolType\").mockResolvedValue"}}],["external:screen.getByText(\"Custom Tool\").closest",{"id":"external:screen.getByText(\"Custom Tool\").closest","kind":"external","language":"jsts","metadata":{"unresolvedName":"screen.getByText(\"Custom Tool\").closest"}}],["external:within(customToolCard as HTMLElement).queryByRole",{"id":"external:within(customToolCard as HTMLElement).queryByRole","kind":"external","language":"jsts","metadata":{"unresolvedName":"within(customToolCard as HTMLElement).queryByRole"}}],["external:vi.spyOn(window, \"confirm\").mockReturnValue",{"id":"external:vi.spyOn(window, \"confirm\").mockReturnValue","kind":"external","language":"jsts","metadata":{"unresolvedName":"vi.spyOn(window, \"confirm\").mockReturnValue"}}],["file:/workspace/apps/web/src/components/features/tool-workshop",{"id":"file:/workspace/apps/web/src/components/features/tool-workshop","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/tool-workshop"}],["/workspace/apps/web/src/pages/ToolWorkshopPage.tsx:ToolWorkshopPage",{"id":"/workspace/apps/web/src/pages/ToolWorkshopPage.tsx:ToolWorkshopPage","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/ToolWorkshopPage.tsx","symbolName":"ToolWorkshopPage","symbolKind":"function","exported":true,"metadata":{"line":11,"column":33,"cyclomaticComplexity":10,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:([\"types\", \"configs\", \"folders\"] as Tab[]).map",{"id":"external:([\"types\", \"configs\", \"folders\"] as Tab[]).map","kind":"external","language":"jsts","metadata":{"unresolvedName":"([\"types\", \"configs\", \"folders\"] as Tab[]).map"}}],["symbol-name:setActiveTab",{"id":"symbol-name:setActiveTab","kind":"symbol","language":"jsts","symbolName":"setActiveTab","metadata":{"unresolvedName":"setActiveTab"}}],["file:/workspace/apps/web/src/pages/config-profiles.tsx",{"id":"file:/workspace/apps/web/src/pages/config-profiles.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","metadata":{"lineCount":1612,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"ConfigProfilesPage","symbolKind":"function","exported":true,"metadata":{"line":29,"column":35,"cyclomaticComplexity":132,"maxNestingDepth":4,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["symbol-name:setIncludedProfileIds",{"id":"symbol-name:setIncludedProfileIds","kind":"symbol","language":"jsts","symbolName":"setIncludedProfileIds","metadata":{"unresolvedName":"setIncludedProfileIds"}}],["symbol-name:setPreviewData",{"id":"symbol-name:setPreviewData","kind":"symbol","language":"jsts","symbolName":"setPreviewData","metadata":{"unresolvedName":"setPreviewData"}}],["external:profile.includes.map",{"id":"external:profile.includes.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"profile.includes.map"}}],["symbol-name:setIsCreating",{"id":"symbol-name:setIsCreating","kind":"symbol","language":"jsts","symbolName":"setIsCreating","metadata":{"unresolvedName":"setIsCreating"}}],["symbol-name:populateForm",{"id":"symbol-name:populateForm","kind":"symbol","language":"jsts","symbolName":"populateForm","metadata":{"unresolvedName":"populateForm"}}],["external:visited.has",{"id":"external:visited.has","kind":"external","language":"jsts","metadata":{"unresolvedName":"visited.has"}}],["symbol-name:getIncludedProfile",{"id":"symbol-name:getIncludedProfile","kind":"symbol","language":"jsts","symbolName":"getIncludedProfile","metadata":{"unresolvedName":"getIncludedProfile"}}],["external:nextVisited.add",{"id":"external:nextVisited.add","kind":"external","language":"jsts","metadata":{"unresolvedName":"nextVisited.add"}}],["symbol-name:wouldCreateCycle",{"id":"symbol-name:wouldCreateCycle","kind":"symbol","language":"jsts","symbolName":"wouldCreateCycle","metadata":{"unresolvedName":"wouldCreateCycle"}}],["external:profiles.filter",{"id":"external:profiles.filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"profiles.filter"}}],["external:includedProfileIds.includes",{"id":"external:includedProfileIds.includes","kind":"external","language":"jsts","metadata":{"unresolvedName":"includedProfileIds.includes"}}],["external:e.dataTransfer.setData",{"id":"external:e.dataTransfer.setData","kind":"external","language":"jsts","metadata":{"unresolvedName":"e.dataTransfer.setData"}}],["symbol-name:setDragOverIndex",{"id":"symbol-name:setDragOverIndex","kind":"symbol","language":"jsts","symbolName":"setDragOverIndex","metadata":{"unresolvedName":"setDragOverIndex"}}],["external:e.dataTransfer.getData",{"id":"external:e.dataTransfer.getData","kind":"external","language":"jsts","metadata":{"unresolvedName":"e.dataTransfer.getData"}}],["external:newOrder.splice",{"id":"external:newOrder.splice","kind":"external","language":"jsts","metadata":{"unresolvedName":"newOrder.splice"}}],["external:formData.name?.trim",{"id":"external:formData.name?.trim","kind":"external","language":"jsts","metadata":{"unresolvedName":"formData.name?.trim"}}],["symbol-name:createConfigProfile",{"id":"symbol-name:createConfigProfile","kind":"symbol","language":"jsts","symbolName":"createConfigProfile","metadata":{"unresolvedName":"createConfigProfile"}}],["symbol-name:updateProfileIncludes",{"id":"symbol-name:updateProfileIncludes","kind":"symbol","language":"jsts","symbolName":"updateProfileIncludes","metadata":{"unresolvedName":"updateProfileIncludes"}}],["external:(await listConfigProfiles()).find",{"id":"external:(await listConfigProfiles()).find","kind":"external","language":"jsts","metadata":{"unresolvedName":"(await listConfigProfiles()).find"}}],["symbol-name:updateConfigProfile",{"id":"symbol-name:updateConfigProfile","kind":"symbol","language":"jsts","symbolName":"updateConfigProfile","metadata":{"unresolvedName":"updateConfigProfile"}}],["symbol-name:deleteConfigProfile",{"id":"symbol-name:deleteConfigProfile","kind":"symbol","language":"jsts","symbolName":"deleteConfigProfile","metadata":{"unresolvedName":"deleteConfigProfile"}}],["symbol-name:setPreviewingId",{"id":"symbol-name:setPreviewingId","kind":"symbol","language":"jsts","symbolName":"setPreviewingId","metadata":{"unresolvedName":"setPreviewingId"}}],["symbol-name:previewConfigProfile",{"id":"symbol-name:previewConfigProfile","kind":"symbol","language":"jsts","symbolName":"previewConfigProfile","metadata":{"unresolvedName":"previewConfigProfile"}}],["external:mounts.splice",{"id":"external:mounts.splice","kind":"external","language":"jsts","metadata":{"unresolvedName":"mounts.splice"}}],["symbol-name:getScopeLabel",{"id":"symbol-name:getScopeLabel","kind":"symbol","language":"jsts","symbolName":"getScopeLabel","metadata":{"unresolvedName":"getScopeLabel"}}],["symbol-name:setMobileView",{"id":"symbol-name:setMobileView","kind":"symbol","language":"jsts","symbolName":"setMobileView","metadata":{"unresolvedName":"setMobileView"}}],["external:Object.entries(selectedProfile.env_vars)\n\t\t\t\t\t\t\t\t\t.map",{"id":"external:Object.entries(selectedProfile.env_vars)\n\t\t\t\t\t\t\t\t\t.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"Object.entries(selectedProfile.env_vars)\n\t\t\t\t\t\t\t\t\t.map"}}],["external:selectedProfile.mounts\n\t\t\t\t\t\t\t\t\t.map",{"id":"external:selectedProfile.mounts\n\t\t\t\t\t\t\t\t\t.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"selectedProfile.mounts\n\t\t\t\t\t\t\t\t\t.map"}}],["symbol-name:handleSubmit",{"id":"symbol-name:handleSubmit","kind":"symbol","language":"jsts","symbolName":"handleSubmit","metadata":{"unresolvedName":"handleSubmit"}}],["symbol-name:updateFormField",{"id":"symbol-name:updateFormField","kind":"symbol","language":"jsts","symbolName":"updateFormField","metadata":{"unresolvedName":"updateFormField"}}],["external:Object.entries(formData.env_vars || {}).map",{"id":"external:Object.entries(formData.env_vars || {}).map","kind":"external","language":"jsts","metadata":{"unresolvedName":"Object.entries(formData.env_vars || {}).map"}}],["external:(formData.mounts || []).map",{"id":"external:(formData.mounts || []).map","kind":"external","language":"jsts","metadata":{"unresolvedName":"(formData.mounts || []).map"}}],["external:(formData.mounts || []).filter",{"id":"external:(formData.mounts || []).filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"(formData.mounts || []).filter"}}],["symbol-name:handleSelectProfile",{"id":"symbol-name:handleSelectProfile","kind":"symbol","language":"jsts","symbolName":"handleSelectProfile","metadata":{"unresolvedName":"handleSelectProfile"}}],["symbol-name:handlePreview",{"id":"symbol-name:handlePreview","kind":"symbol","language":"jsts","symbolName":"handlePreview","metadata":{"unresolvedName":"handlePreview"}}],["external:includedProfileIds.map",{"id":"external:includedProfileIds.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"includedProfileIds.map"}}],["symbol-name:handleDragStart",{"id":"symbol-name:handleDragStart","kind":"symbol","language":"jsts","symbolName":"handleDragStart","metadata":{"unresolvedName":"handleDragStart"}}],["symbol-name:handleDragOver",{"id":"symbol-name:handleDragOver","kind":"symbol","language":"jsts","symbolName":"handleDragOver","metadata":{"unresolvedName":"handleDragOver"}}],["symbol-name:handleDrop",{"id":"symbol-name:handleDrop","kind":"symbol","language":"jsts","symbolName":"handleDrop","metadata":{"unresolvedName":"handleDrop"}}],["symbol-name:removeInclude",{"id":"symbol-name:removeInclude","kind":"symbol","language":"jsts","symbolName":"removeInclude","metadata":{"unresolvedName":"removeInclude"}}],["symbol-name:availableProfilesForInclude",{"id":"symbol-name:availableProfilesForInclude","kind":"symbol","language":"jsts","symbolName":"availableProfilesForInclude","metadata":{"unresolvedName":"availableProfilesForInclude"}}],["symbol-name:addInclude",{"id":"symbol-name:addInclude","kind":"symbol","language":"jsts","symbolName":"addInclude","metadata":{"unresolvedName":"addInclude"}}],["external:availableProfilesForInclude().map",{"id":"external:availableProfilesForInclude().map","kind":"external","language":"jsts","metadata":{"unresolvedName":"availableProfilesForInclude().map"}}],["external:Object.entries(formData.files || {}).map",{"id":"external:Object.entries(formData.files || {}).map","kind":"external","language":"jsts","metadata":{"unresolvedName":"Object.entries(formData.files || {}).map"}}],["symbol-name:updateFile",{"id":"symbol-name:updateFile","kind":"symbol","language":"jsts","symbolName":"updateFile","metadata":{"unresolvedName":"updateFile"}}],["symbol-name:removeFile",{"id":"symbol-name:removeFile","kind":"symbol","language":"jsts","symbolName":"removeFile","metadata":{"unresolvedName":"removeFile"}}],["external:Object.entries(mount.files).map",{"id":"external:Object.entries(mount.files).map","kind":"external","language":"jsts","metadata":{"unresolvedName":"Object.entries(mount.files).map"}}],["symbol-name:updateMountFile",{"id":"symbol-name:updateMountFile","kind":"symbol","language":"jsts","symbolName":"updateMountFile","metadata":{"unresolvedName":"updateMountFile"}}],["symbol-name:removeMountFile",{"id":"symbol-name:removeMountFile","kind":"symbol","language":"jsts","symbolName":"removeMountFile","metadata":{"unresolvedName":"removeMountFile"}}],["symbol-name:addMountFile",{"id":"symbol-name:addMountFile","kind":"symbol","language":"jsts","symbolName":"addMountFile","metadata":{"unresolvedName":"addMountFile"}}],["/workspace/apps/web/src/pages/config-profiles.tsx:",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":1568,"column":20,"cyclomaticComplexity":3,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:resetForm",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:resetForm","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"resetForm","symbolKind":"function","exported":false,"metadata":{"line":88,"column":20,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:populateForm",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:populateForm","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"populateForm","symbolKind":"function","exported":false,"metadata":{"line":105,"column":23,"cyclomaticComplexity":5,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:handleSelectProfile",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:handleSelectProfile","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"handleSelectProfile","symbolKind":"function","exported":false,"metadata":{"line":128,"column":30,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:handleCreateNew",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:handleCreateNew","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"handleCreateNew","symbolKind":"function","exported":false,"metadata":{"line":138,"column":26,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:getScopeLabel",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:getScopeLabel","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"getScopeLabel","symbolKind":"function","exported":false,"metadata":{"line":148,"column":24,"cyclomaticComplexity":5,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:wouldCreateCycle",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:wouldCreateCycle","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"wouldCreateCycle","symbolKind":"function","exported":false,"metadata":{"line":156,"column":27,"cyclomaticComplexity":6,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:availableProfilesForInclude",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:availableProfilesForInclude","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"availableProfilesForInclude","symbolKind":"function","exported":false,"metadata":{"line":177,"column":38,"cyclomaticComplexity":5,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:addInclude",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:addInclude","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"addInclude","symbolKind":"function","exported":false,"metadata":{"line":188,"column":21,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:removeInclude",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:removeInclude","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"removeInclude","symbolKind":"function","exported":false,"metadata":{"line":192,"column":24,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:handleDragStart",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDragStart","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"handleDragStart","symbolKind":"function","exported":false,"metadata":{"line":197,"column":26,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:handleDragOver",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDragOver","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"handleDragOver","symbolKind":"function","exported":false,"metadata":{"line":202,"column":25,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:handleDragLeave",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDragLeave","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"handleDragLeave","symbolKind":"function","exported":false,"metadata":{"line":208,"column":26,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:handleDrop",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDrop","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"handleDrop","symbolKind":"function","exported":false,"metadata":{"line":212,"column":21,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:handleSubmit",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:handleSubmit","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"handleSubmit","symbolKind":"function","exported":false,"metadata":{"line":228,"column":23,"cyclomaticComplexity":8,"maxNestingDepth":4,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:handleDelete",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDelete","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"handleDelete","symbolKind":"function","exported":false,"metadata":{"line":275,"column":23,"cyclomaticComplexity":4,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:handlePreview",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:handlePreview","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"handlePreview","symbolKind":"function","exported":false,"metadata":{"line":292,"column":24,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:updateFormField",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:updateFormField","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"updateFormField","symbolKind":"function","exported":false,"metadata":{"line":304,"column":26,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:addEnvVar",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:addEnvVar","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"addEnvVar","symbolKind":"function","exported":false,"metadata":{"line":312,"column":20,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:updateEnvVar",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:updateEnvVar","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"updateEnvVar","symbolKind":"function","exported":false,"metadata":{"line":320,"column":23,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:removeEnvVar",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:removeEnvVar","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"removeEnvVar","symbolKind":"function","exported":false,"metadata":{"line":332,"column":23,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:addFile",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:addFile","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"addFile","symbolKind":"function","exported":false,"metadata":{"line":341,"column":18,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:updateFile",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:updateFile","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"updateFile","symbolKind":"function","exported":false,"metadata":{"line":349,"column":21,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:removeFile",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:removeFile","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"removeFile","symbolKind":"function","exported":false,"metadata":{"line":361,"column":21,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:addMount",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:addMount","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"addMount","symbolKind":"function","exported":false,"metadata":{"line":370,"column":19,"cyclomaticComplexity":2,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:updateMount",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:updateMount","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"updateMount","symbolKind":"function","exported":false,"metadata":{"line":378,"column":22,"cyclomaticComplexity":2,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:removeMount",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:removeMount","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"removeMount","symbolKind":"function","exported":false,"metadata":{"line":390,"column":22,"cyclomaticComplexity":2,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:addMountFile",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:addMountFile","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"addMountFile","symbolKind":"function","exported":false,"metadata":{"line":399,"column":23,"cyclomaticComplexity":2,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:updateMountFile",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:updateMountFile","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"updateMountFile","symbolKind":"function","exported":false,"metadata":{"line":411,"column":26,"cyclomaticComplexity":3,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:removeMountFile",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:removeMountFile","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"removeMountFile","symbolKind":"function","exported":false,"metadata":{"line":430,"column":26,"cyclomaticComplexity":2,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["/workspace/apps/web/src/pages/config-profiles.tsx:preventDefault",{"id":"/workspace/apps/web/src/pages/config-profiles.tsx:preventDefault","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/config-profiles.tsx","symbolName":"preventDefault","symbolKind":"function","exported":false,"metadata":{"line":560,"column":38,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["file:/workspace/apps/web/src/pages/workspace-detail.tsx",{"id":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/pages/workspace-detail.tsx","metadata":{"lineCount":415,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/workspace-detail.tsx:WorkspaceDetailPage",{"id":"/workspace/apps/web/src/pages/workspace-detail.tsx:WorkspaceDetailPage","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/workspace-detail.tsx","symbolName":"WorkspaceDetailPage","symbolKind":"function","exported":true,"metadata":{"line":17,"column":1,"cyclomaticComplexity":9,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:useWorkspaces",{"id":"symbol-name:useWorkspaces","kind":"symbol","language":"jsts","symbolName":"useWorkspaces","metadata":{"unresolvedName":"useWorkspaces"}}],["/workspace/apps/web/src/pages/workspace-detail.tsx:WorkspaceHeader",{"id":"/workspace/apps/web/src/pages/workspace-detail.tsx:WorkspaceHeader","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/workspace-detail.tsx","symbolName":"WorkspaceHeader","symbolKind":"function","exported":false,"metadata":{"line":53,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/workspace-detail.tsx:TabBar",{"id":"/workspace/apps/web/src/pages/workspace-detail.tsx:TabBar","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/workspace-detail.tsx","symbolName":"TabBar","symbolKind":"function","exported":false,"metadata":{"line":81,"column":1,"cyclomaticComplexity":2,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/workspace-detail.tsx:MobileTabBar",{"id":"/workspace/apps/web/src/pages/workspace-detail.tsx:MobileTabBar","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/workspace-detail.tsx","symbolName":"MobileTabBar","symbolKind":"function","exported":false,"metadata":{"line":116,"column":1,"cyclomaticComplexity":2,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/workspace-detail.tsx:FilesTab",{"id":"/workspace/apps/web/src/pages/workspace-detail.tsx:FilesTab","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/workspace-detail.tsx","symbolName":"FilesTab","symbolKind":"function","exported":false,"metadata":{"line":152,"column":1,"cyclomaticComplexity":20,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:useWorkspaceFiles",{"id":"symbol-name:useWorkspaceFiles","kind":"symbol","language":"jsts","symbolName":"useWorkspaceFiles","metadata":{"unresolvedName":"useWorkspaceFiles"}}],["symbol-name:useWorkspaceGit",{"id":"symbol-name:useWorkspaceGit","kind":"symbol","language":"jsts","symbolName":"useWorkspaceGit","metadata":{"unresolvedName":"useWorkspaceGit"}}],["symbol-name:setSelectedPath",{"id":"symbol-name:setSelectedPath","kind":"symbol","language":"jsts","symbolName":"setSelectedPath","metadata":{"unresolvedName":"setSelectedPath"}}],["symbol-name:setIsEditing",{"id":"symbol-name:setIsEditing","kind":"symbol","language":"jsts","symbolName":"setIsEditing","metadata":{"unresolvedName":"setIsEditing"}}],["symbol-name:setEditContent",{"id":"symbol-name:setEditContent","kind":"symbol","language":"jsts","symbolName":"setEditContent","metadata":{"unresolvedName":"setEditContent"}}],["symbol-name:saveFile",{"id":"symbol-name:saveFile","kind":"symbol","language":"jsts","symbolName":"saveFile","metadata":{"unresolvedName":"saveFile"}}],["symbol-name:commit",{"id":"symbol-name:commit","kind":"symbol","language":"jsts","symbolName":"commit","metadata":{"unresolvedName":"commit"}}],["/workspace/apps/web/src/pages/workspace-detail.tsx:handleSelect",{"id":"/workspace/apps/web/src/pages/workspace-detail.tsx:handleSelect","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/workspace-detail.tsx","symbolName":"handleSelect","symbolKind":"function","exported":false,"metadata":{"line":161,"column":23,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/workspace-detail.tsx:handleEdit",{"id":"/workspace/apps/web/src/pages/workspace-detail.tsx:handleEdit","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/workspace-detail.tsx","symbolName":"handleEdit","symbolKind":"function","exported":false,"metadata":{"line":169,"column":21,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/workspace-detail.tsx:handleSave",{"id":"/workspace/apps/web/src/pages/workspace-detail.tsx:handleSave","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/workspace-detail.tsx","symbolName":"handleSave","symbolKind":"function","exported":false,"metadata":{"line":176,"column":21,"cyclomaticComplexity":4,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/workspace-detail.tsx:GitTab",{"id":"/workspace/apps/web/src/pages/workspace-detail.tsx:GitTab","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/workspace-detail.tsx","symbolName":"GitTab","symbolKind":"function","exported":false,"metadata":{"line":278,"column":1,"cyclomaticComplexity":3,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:checkout",{"id":"symbol-name:checkout","kind":"symbol","language":"jsts","symbolName":"checkout","metadata":{"unresolvedName":"checkout"}}],["external:history.map",{"id":"external:history.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"history.map"}}],["external:commit.hash.slice",{"id":"external:commit.hash.slice","kind":"external","language":"jsts","metadata":{"unresolvedName":"commit.hash.slice"}}],["/workspace/apps/web/src/pages/workspace-detail.tsx:ToolsTab",{"id":"/workspace/apps/web/src/pages/workspace-detail.tsx:ToolsTab","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/workspace-detail.tsx","symbolName":"ToolsTab","symbolKind":"function","exported":false,"metadata":{"line":314,"column":1,"cyclomaticComplexity":5,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:useWorkspaceInstances",{"id":"symbol-name:useWorkspaceInstances","kind":"symbol","language":"jsts","symbolName":"useWorkspaceInstances","metadata":{"unresolvedName":"useWorkspaceInstances"}}],["symbol-name:setShowModal",{"id":"symbol-name:setShowModal","kind":"symbol","language":"jsts","symbolName":"setShowModal","metadata":{"unresolvedName":"setShowModal"}}],["/workspace/apps/web/src/pages/workspace-detail.tsx:",{"id":"/workspace/apps/web/src/pages/workspace-detail.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/workspace-detail.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":369,"column":19,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/workspace-detail.tsx:SettingsTab",{"id":"/workspace/apps/web/src/pages/workspace-detail.tsx:SettingsTab","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/workspace-detail.tsx","symbolName":"SettingsTab","symbolKind":"function","exported":false,"metadata":{"line":384,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/pages/workspaces.tsx",{"id":"file:/workspace/apps/web/src/pages/workspaces.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/pages/workspaces.tsx","metadata":{"lineCount":109,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/workspaces.tsx:WorkspacesPage",{"id":"/workspace/apps/web/src/pages/workspaces.tsx:WorkspacesPage","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/workspaces.tsx","symbolName":"WorkspacesPage","symbolKind":"function","exported":true,"metadata":{"line":12,"column":1,"cyclomaticComplexity":7,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:useWorkspaceActions",{"id":"symbol-name:useWorkspaceActions","kind":"symbol","language":"jsts","symbolName":"useWorkspaceActions","metadata":{"unresolvedName":"useWorkspaceActions"}}],["external:actions.delete",{"id":"external:actions.delete","kind":"external","language":"jsts","metadata":{"unresolvedName":"actions.delete"}}],["external:actions.sync",{"id":"external:actions.sync","kind":"external","language":"jsts","metadata":{"unresolvedName":"actions.sync"}}],["symbol-name:setStartWorkspace",{"id":"symbol-name:setStartWorkspace","kind":"symbol","language":"jsts","symbolName":"setStartWorkspace","metadata":{"unresolvedName":"setStartWorkspace"}}],["/workspace/apps/web/src/pages/workspaces.tsx:handleDelete",{"id":"/workspace/apps/web/src/pages/workspaces.tsx:handleDelete","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/workspaces.tsx","symbolName":"handleDelete","symbolKind":"function","exported":false,"metadata":{"line":18,"column":23,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/pages/workspaces.tsx:handleSync",{"id":"/workspace/apps/web/src/pages/workspaces.tsx:handleSync","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/workspaces.tsx","symbolName":"handleSync","symbolKind":"function","exported":false,"metadata":{"line":22,"column":21,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"service","trustBoundaries":["network","external-api","serialization"]}}],["/workspace/apps/web/src/pages/workspaces.tsx:",{"id":"/workspace/apps/web/src/pages/workspaces.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/pages/workspaces.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":97,"column":19,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/router.tsx:AppRouter",{"id":"/workspace/apps/web/src/router.tsx:AppRouter","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/router.tsx","symbolName":"AppRouter","symbolKind":"function","exported":true,"metadata":{"line":21,"column":26,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/state/auth.tsx:AuthProvider",{"id":"/workspace/apps/web/src/state/auth.tsx:AuthProvider","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/state/auth.tsx","symbolName":"AuthProvider","symbolKind":"function","exported":true,"metadata":{"line":17,"column":29,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["symbol-name:setUser",{"id":"symbol-name:setUser","kind":"symbol","language":"jsts","symbolName":"setUser","metadata":{"unresolvedName":"setUser"}}],["/workspace/apps/web/src/state/auth.tsx:",{"id":"/workspace/apps/web/src/state/auth.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/state/auth.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":39,"column":13,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/state/auth.tsx:useAuth",{"id":"/workspace/apps/web/src/state/auth.tsx:useAuth","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/state/auth.tsx","symbolName":"useAuth","symbolKind":"function","exported":true,"metadata":{"line":56,"column":24,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/state/events.tsx:EventProvider",{"id":"/workspace/apps/web/src/state/events.tsx:EventProvider","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/state/events.tsx","symbolName":"EventProvider","symbolKind":"function","exported":true,"metadata":{"line":17,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/state/events.tsx:useEventContext",{"id":"/workspace/apps/web/src/state/events.tsx:useEventContext","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/state/events.tsx","symbolName":"useEventContext","symbolKind":"function","exported":true,"metadata":{"line":28,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/state/notifications.tsx:NotificationProvider",{"id":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/state/notifications.tsx","symbolName":"NotificationProvider","symbolKind":"function","exported":true,"metadata":{"line":38,"column":1,"cyclomaticComplexity":37,"maxNestingDepth":3,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:getUnreadCount",{"id":"symbol-name:getUnreadCount","kind":"symbol","language":"jsts","symbolName":"getUnreadCount","metadata":{"unresolvedName":"getUnreadCount"}}],["symbol-name:setUnreadCount",{"id":"symbol-name:setUnreadCount","kind":"symbol","language":"jsts","symbolName":"setUnreadCount","metadata":{"unresolvedName":"setUnreadCount"}}],["symbol-name:setIsLoading",{"id":"symbol-name:setIsLoading","kind":"symbol","language":"jsts","symbolName":"setIsLoading","metadata":{"unresolvedName":"setIsLoading"}}],["symbol-name:getNotifications",{"id":"symbol-name:getNotifications","kind":"symbol","language":"jsts","symbolName":"getNotifications","metadata":{"unresolvedName":"getNotifications"}}],["symbol-name:setNotifications",{"id":"symbol-name:setNotifications","kind":"symbol","language":"jsts","symbolName":"setNotifications","metadata":{"unresolvedName":"setNotifications"}}],["symbol-name:fetchUnreadCount",{"id":"symbol-name:fetchUnreadCount","kind":"symbol","language":"jsts","symbolName":"fetchUnreadCount","metadata":{"unresolvedName":"fetchUnreadCount"}}],["symbol-name:fetchList",{"id":"symbol-name:fetchList","kind":"symbol","language":"jsts","symbolName":"fetchList","metadata":{"unresolvedName":"fetchList"}}],["symbol-name:stopPolling",{"id":"symbol-name:stopPolling","kind":"symbol","language":"jsts","symbolName":"stopPolling","metadata":{"unresolvedName":"stopPolling"}}],["symbol-name:startPolling",{"id":"symbol-name:startPolling","kind":"symbol","language":"jsts","symbolName":"startPolling","metadata":{"unresolvedName":"startPolling"}}],["external:currentNotifications.find",{"id":"external:currentNotifications.find","kind":"external","language":"jsts","metadata":{"unresolvedName":"currentNotifications.find"}}],["external:currentNotifications.map",{"id":"external:currentNotifications.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"currentNotifications.map"}}],["external:new Date().toISOString",{"id":"external:new Date().toISOString","kind":"external","language":"jsts","metadata":{"unresolvedName":"new Date().toISOString"}}],["symbol-name:markNotificationRead",{"id":"symbol-name:markNotificationRead","kind":"symbol","language":"jsts","symbolName":"markNotificationRead","metadata":{"unresolvedName":"markNotificationRead"}}],["symbol-name:markAllNotificationsRead",{"id":"symbol-name:markAllNotificationsRead","kind":"symbol","language":"jsts","symbolName":"markAllNotificationsRead","metadata":{"unresolvedName":"markAllNotificationsRead"}}],["external:currentNotifications.filter",{"id":"external:currentNotifications.filter","kind":"external","language":"jsts","metadata":{"unresolvedName":"currentNotifications.filter"}}],["symbol-name:dismissNotification",{"id":"symbol-name:dismissNotification","kind":"symbol","language":"jsts","symbolName":"dismissNotification","metadata":{"unresolvedName":"dismissNotification"}}],["symbol-name:clearAllNotifications",{"id":"symbol-name:clearAllNotifications","kind":"symbol","language":"jsts","symbolName":"clearAllNotifications","metadata":{"unresolvedName":"clearAllNotifications"}}],["/workspace/apps/web/src/state/notifications.tsx:",{"id":"/workspace/apps/web/src/state/notifications.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/state/notifications.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":270,"column":31,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/state/notifications.tsx:handleVisibilityChange",{"id":"/workspace/apps/web/src/state/notifications.tsx:handleVisibilityChange","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/state/notifications.tsx","symbolName":"handleVisibilityChange","symbolKind":"function","exported":false,"metadata":{"line":154,"column":34,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/state/sessions.tsx:SessionsProvider",{"id":"/workspace/apps/web/src/state/sessions.tsx:SessionsProvider","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/state/sessions.tsx","symbolName":"SessionsProvider","symbolKind":"function","exported":true,"metadata":{"line":21,"column":33,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/state/sessions.tsx:",{"id":"/workspace/apps/web/src/state/sessions.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/state/sessions.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":24,"column":37,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/state/sessions.tsx:useSessions",{"id":"/workspace/apps/web/src/state/sessions.tsx:useSessions","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/state/sessions.tsx","symbolName":"useSessions","symbolKind":"function","exported":true,"metadata":{"line":35,"column":28,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["/workspace/apps/web/src/state/toast.tsx:ToastProvider",{"id":"/workspace/apps/web/src/state/toast.tsx:ToastProvider","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/state/toast.tsx","symbolName":"ToastProvider","symbolKind":"function","exported":true,"metadata":{"line":34,"column":1,"cyclomaticComplexity":8,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:setToasts",{"id":"symbol-name:setToasts","kind":"symbol","language":"jsts","symbolName":"setToasts","metadata":{"unresolvedName":"setToasts"}}],["external:timersRef.current.get",{"id":"external:timersRef.current.get","kind":"external","language":"jsts","metadata":{"unresolvedName":"timersRef.current.get"}}],["external:timersRef.current.delete",{"id":"external:timersRef.current.delete","kind":"external","language":"jsts","metadata":{"unresolvedName":"timersRef.current.delete"}}],["symbol-name:removeToast",{"id":"symbol-name:removeToast","kind":"symbol","language":"jsts","symbolName":"removeToast","metadata":{"unresolvedName":"removeToast"}}],["external:timersRef.current.set",{"id":"external:timersRef.current.set","kind":"external","language":"jsts","metadata":{"unresolvedName":"timersRef.current.set"}}],["symbol-name:addToast",{"id":"symbol-name:addToast","kind":"symbol","language":"jsts","symbolName":"addToast","metadata":{"unresolvedName":"addToast"}}],["/workspace/apps/web/src/state/toast.tsx:",{"id":"/workspace/apps/web/src/state/toast.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/state/toast.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":87,"column":18,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/state/toast.tsx:useToast",{"id":"/workspace/apps/web/src/state/toast.tsx:useToast","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/state/toast.tsx","symbolName":"useToast","symbolKind":"function","exported":true,"metadata":{"line":99,"column":1,"cyclomaticComplexity":2,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/state/toast.tsx:info",{"id":"/workspace/apps/web/src/state/toast.tsx:info","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/state/toast.tsx","symbolName":"info","symbolKind":"function","exported":false,"metadata":{"line":115,"column":8,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/state/toast.tsx:success",{"id":"/workspace/apps/web/src/state/toast.tsx:success","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/state/toast.tsx","symbolName":"success","symbolKind":"function","exported":false,"metadata":{"line":118,"column":11,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/state/toast.tsx:warning",{"id":"/workspace/apps/web/src/state/toast.tsx:warning","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/state/toast.tsx","symbolName":"warning","symbolKind":"function","exported":false,"metadata":{"line":121,"column":11,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/state/toast.tsx:error",{"id":"/workspace/apps/web/src/state/toast.tsx:error","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/state/toast.tsx","symbolName":"error","symbolKind":"function","exported":false,"metadata":{"line":124,"column":9,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/state/toast.tsx:severityStyles",{"id":"/workspace/apps/web/src/state/toast.tsx:severityStyles","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/state/toast.tsx","symbolName":"severityStyles","symbolKind":"function","exported":false,"metadata":{"line":129,"column":1,"cyclomaticComplexity":5,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/state/toast.tsx:ToastContainer",{"id":"/workspace/apps/web/src/state/toast.tsx:ToastContainer","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/state/toast.tsx","symbolName":"ToastContainer","symbolKind":"function","exported":false,"metadata":{"line":143,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:toasts.map",{"id":"external:toasts.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"toasts.map"}}],["symbol-name:severityStyles",{"id":"symbol-name:severityStyles","kind":"symbol","language":"jsts","symbolName":"severityStyles","metadata":{"unresolvedName":"severityStyles"}}],["file:/workspace/apps/web/src/test/setup.ts",{"id":"file:/workspace/apps/web/src/test/setup.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/test/setup.ts","metadata":{"lineCount":2,"featureKind":"library","trustBoundaries":[]}}],["external:@testing-library/jest-dom/vitest",{"id":"external:@testing-library/jest-dom/vitest","kind":"external","language":"jsts","metadata":{"source":"@testing-library/jest-dom/vitest"}}],["file:/workspace/apps/web/src/types/api-response.ts",{"id":"file:/workspace/apps/web/src/types/api-response.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/types/api-response.ts","metadata":{"lineCount":11,"featureKind":"library","trustBoundaries":["network","external-api","serialization"]}}],["file:/workspace/apps/web/src/types/user.ts",{"id":"file:/workspace/apps/web/src/types/user.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/types/user.ts","metadata":{"lineCount":11,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/utils/errors.ts:extractErrorMessage",{"id":"/workspace/apps/web/src/utils/errors.ts:extractErrorMessage","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/utils/errors.ts","symbolName":"extractErrorMessage","symbolKind":"function","exported":true,"metadata":{"line":1,"column":36,"cyclomaticComplexity":5,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:detail.map((d) => typeof d === \"string\" ? d : d.msg || JSON.stringify(d)).join",{"id":"external:detail.map((d) => typeof d === \"string\" ? d : d.msg || JSON.stringify(d)).join","kind":"external","language":"jsts","metadata":{"unresolvedName":"detail.map((d) => typeof d === \"string\" ? d : d.msg || JSON.stringify(d)).join"}}],["external:detail.map",{"id":"external:detail.map","kind":"external","language":"jsts","metadata":{"unresolvedName":"detail.map"}}],["external:prismjs",{"id":"external:prismjs","kind":"external","language":"jsts","metadata":{"source":"prismjs"}}],["/workspace/apps/web/src/utils/language.ts:detectLanguage",{"id":"/workspace/apps/web/src/utils/language.ts:detectLanguage","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/utils/language.ts","symbolName":"detectLanguage","symbolKind":"function","exported":true,"metadata":{"line":69,"column":31,"cyclomaticComplexity":3,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:filename.split(\".\").pop()?.toLowerCase",{"id":"external:filename.split(\".\").pop()?.toLowerCase","kind":"external","language":"jsts","metadata":{"unresolvedName":"filename.split(\".\").pop()?.toLowerCase"}}],["external:filename.split(\".\").pop",{"id":"external:filename.split(\".\").pop","kind":"external","language":"jsts","metadata":{"unresolvedName":"filename.split(\".\").pop"}}],["external:filename.split",{"id":"external:filename.split","kind":"external","language":"jsts","metadata":{"unresolvedName":"filename.split"}}],["/workspace/apps/web/src/utils/language.ts:highlightCode",{"id":"/workspace/apps/web/src/utils/language.ts:highlightCode","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/utils/language.ts","symbolName":"highlightCode","symbolKind":"function","exported":true,"metadata":{"line":74,"column":30,"cyclomaticComplexity":2,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:Prism.highlight",{"id":"external:Prism.highlight","kind":"external","language":"jsts","metadata":{"unresolvedName":"Prism.highlight"}}],["/workspace/apps/web/src/utils/language.ts:loadLanguage",{"id":"/workspace/apps/web/src/utils/language.ts:loadLanguage","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/utils/language.ts","symbolName":"loadLanguage","symbolKind":"function","exported":true,"metadata":{"line":79,"column":29,"cyclomaticComplexity":4,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/utils/terminal-protocol.ts:encodeControlMessage",{"id":"/workspace/apps/web/src/utils/terminal-protocol.ts:encodeControlMessage","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/utils/terminal-protocol.ts","symbolName":"encodeControlMessage","symbolKind":"function","exported":true,"metadata":{"line":9,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":true,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/utils/terminal-protocol.ts:decodeControlMessage",{"id":"/workspace/apps/web/src/utils/terminal-protocol.ts:decodeControlMessage","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/utils/terminal-protocol.ts","symbolName":"decodeControlMessage","symbolKind":"function","exported":true,"metadata":{"line":17,"column":1,"cyclomaticComplexity":3,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:isServerControlMessage",{"id":"symbol-name:isServerControlMessage","kind":"symbol","language":"jsts","symbolName":"isServerControlMessage","metadata":{"unresolvedName":"isServerControlMessage"}}],["/workspace/apps/web/src/utils/terminal-protocol.ts:isControlMessage",{"id":"/workspace/apps/web/src/utils/terminal-protocol.ts:isControlMessage","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/utils/terminal-protocol.ts","symbolName":"isControlMessage","symbolKind":"function","exported":true,"metadata":{"line":35,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/utils/terminal-protocol.ts:encodeInputData",{"id":"/workspace/apps/web/src/utils/terminal-protocol.ts:encodeInputData","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/utils/terminal-protocol.ts","symbolName":"encodeInputData","symbolKind":"function","exported":true,"metadata":{"line":42,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:btoa",{"id":"symbol-name:btoa","kind":"symbol","language":"jsts","symbolName":"btoa","metadata":{"unresolvedName":"btoa"}}],["symbol-name:unescape",{"id":"symbol-name:unescape","kind":"symbol","language":"jsts","symbolName":"unescape","metadata":{"unresolvedName":"unescape"}}],["/workspace/apps/web/src/utils/terminal-protocol.ts:decodeInputData",{"id":"/workspace/apps/web/src/utils/terminal-protocol.ts:decodeInputData","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/utils/terminal-protocol.ts","symbolName":"decodeInputData","symbolKind":"function","exported":true,"metadata":{"line":49,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["symbol-name:decodeURIComponent",{"id":"symbol-name:decodeURIComponent","kind":"symbol","language":"jsts","symbolName":"decodeURIComponent","metadata":{"unresolvedName":"decodeURIComponent"}}],["symbol-name:escape",{"id":"symbol-name:escape","kind":"symbol","language":"jsts","symbolName":"escape","metadata":{"unresolvedName":"escape"}}],["symbol-name:atob",{"id":"symbol-name:atob","kind":"symbol","language":"jsts","symbolName":"atob","metadata":{"unresolvedName":"atob"}}],["/workspace/apps/web/src/utils/terminal-protocol.ts:isServerControlMessage",{"id":"/workspace/apps/web/src/utils/terminal-protocol.ts:isServerControlMessage","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/utils/terminal-protocol.ts","symbolName":"isServerControlMessage","symbolKind":"function","exported":false,"metadata":{"line":55,"column":1,"cyclomaticComplexity":11,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/utils/time.ts:formatRelativeTime",{"id":"/workspace/apps/web/src/utils/time.ts:formatRelativeTime","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/utils/time.ts","symbolName":"formatRelativeTime","symbolKind":"function","exported":true,"metadata":{"line":11,"column":1,"cyclomaticComplexity":4,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["external:Math.floor",{"id":"external:Math.floor","kind":"external","language":"jsts","metadata":{"unresolvedName":"Math.floor"}}],["external:now.getTime",{"id":"external:now.getTime","kind":"external","language":"jsts","metadata":{"unresolvedName":"now.getTime"}}],["external:date.getTime",{"id":"external:date.getTime","kind":"external","language":"jsts","metadata":{"unresolvedName":"date.getTime"}}],["file:/workspace/apps/web/vite.config.ts",{"id":"file:/workspace/apps/web/vite.config.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/vite.config.ts","metadata":{"lineCount":20,"featureKind":"service","trustBoundaries":["filesystem","database"]}}],["external:vite",{"id":"external:vite","kind":"external","language":"jsts","metadata":{"source":"vite"}}],["external:@vitejs/plugin-react",{"id":"external:@vitejs/plugin-react","kind":"external","language":"jsts","metadata":{"source":"@vitejs/plugin-react"}}],["file:/workspace/apps/web/src/api/git-repositories.ts",{"id":"file:/workspace/apps/web/src/api/git-repositories.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/api/git-repositories.ts","metadata":{"lineCount":213,"featureKind":"library","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/git-repositories.ts:parseGitUrl",{"id":"/workspace/apps/web/src/api/git-repositories.ts:parseGitUrl","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/git-repositories.ts","symbolName":"parseGitUrl","symbolKind":"function","exported":true,"metadata":{"line":36,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/git-repositories.ts:listRepositories",{"id":"/workspace/apps/web/src/api/git-repositories.ts:listRepositories","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/git-repositories.ts","symbolName":"listRepositories","symbolKind":"function","exported":true,"metadata":{"line":43,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/git-repositories.ts:listRepositoryBranches",{"id":"/workspace/apps/web/src/api/git-repositories.ts:listRepositoryBranches","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/git-repositories.ts","symbolName":"listRepositoryBranches","symbolKind":"function","exported":true,"metadata":{"line":50,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/git-repositories.ts:createRepository",{"id":"/workspace/apps/web/src/api/git-repositories.ts:createRepository","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/git-repositories.ts","symbolName":"createRepository","symbolKind":"function","exported":true,"metadata":{"line":60,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/git-repositories.ts:deleteRepository",{"id":"/workspace/apps/web/src/api/git-repositories.ts:deleteRepository","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/git-repositories.ts","symbolName":"deleteRepository","symbolKind":"function","exported":true,"metadata":{"line":71,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/git-repositories.ts:getRepositoryHistory",{"id":"/workspace/apps/web/src/api/git-repositories.ts:getRepositoryHistory","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/git-repositories.ts","symbolName":"getRepositoryHistory","symbolKind":"function","exported":true,"metadata":{"line":78,"column":1,"cyclomaticComplexity":4,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/git-repositories.ts:getCommitDetail",{"id":"/workspace/apps/web/src/api/git-repositories.ts:getCommitDetail","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/git-repositories.ts","symbolName":"getCommitDetail","symbolKind":"function","exported":true,"metadata":{"line":95,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/git-repositories.ts:getRepositoryStatus",{"id":"/workspace/apps/web/src/api/git-repositories.ts:getRepositoryStatus","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/git-repositories.ts","symbolName":"getRepositoryStatus","symbolKind":"function","exported":true,"metadata":{"line":106,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/git-repositories.ts:createBranch",{"id":"/workspace/apps/web/src/api/git-repositories.ts:createBranch","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/git-repositories.ts","symbolName":"createBranch","symbolKind":"function","exported":true,"metadata":{"line":116,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/git-repositories.ts:deleteBranch",{"id":"/workspace/apps/web/src/api/git-repositories.ts:deleteBranch","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/git-repositories.ts","symbolName":"deleteBranch","symbolKind":"function","exported":true,"metadata":{"line":129,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/git-repositories.ts:checkoutBranch",{"id":"/workspace/apps/web/src/api/git-repositories.ts:checkoutBranch","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/git-repositories.ts","symbolName":"checkoutBranch","symbolKind":"function","exported":true,"metadata":{"line":141,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/git-repositories.ts:commitChanges",{"id":"/workspace/apps/web/src/api/git-repositories.ts:commitChanges","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/git-repositories.ts","symbolName":"commitChanges","symbolKind":"function","exported":true,"metadata":{"line":153,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/git-repositories.ts:fetchRepository",{"id":"/workspace/apps/web/src/api/git-repositories.ts:fetchRepository","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/git-repositories.ts","symbolName":"fetchRepository","symbolKind":"function","exported":true,"metadata":{"line":166,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/git-repositories.ts:pullRepository",{"id":"/workspace/apps/web/src/api/git-repositories.ts:pullRepository","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/git-repositories.ts","symbolName":"pullRepository","symbolKind":"function","exported":true,"metadata":{"line":176,"column":1,"cyclomaticComplexity":2,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/git-repositories.ts:pushRepository",{"id":"/workspace/apps/web/src/api/git-repositories.ts:pushRepository","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/git-repositories.ts","symbolName":"pushRepository","symbolKind":"function","exported":true,"metadata":{"line":188,"column":1,"cyclomaticComplexity":2,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["/workspace/apps/web/src/api/git-repositories.ts:mergeBranches",{"id":"/workspace/apps/web/src/api/git-repositories.ts:mergeBranches","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/git-repositories.ts","symbolName":"mergeBranches","symbolKind":"function","exported":true,"metadata":{"line":200,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["filesystem","database","network","external-api","serialization"]}}],["file:/workspace/apps/web/src/types/session.ts",{"id":"file:/workspace/apps/web/src/types/session.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/types/session.ts","metadata":{"lineCount":17,"featureKind":"library","trustBoundaries":["auth","user-input"]}}],["file:/workspace/apps/web/src/types/git-repository.ts",{"id":"file:/workspace/apps/web/src/types/git-repository.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/types/git-repository.ts","metadata":{"lineCount":87,"featureKind":"library","trustBoundaries":["filesystem","database"]}}],["file:/workspace/apps/web/src/types/project.ts",{"id":"file:/workspace/apps/web/src/types/project.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/types/project.ts","metadata":{"lineCount":29,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/types/index.ts",{"id":"file:/workspace/apps/web/src/types/index.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/types/index.ts","metadata":{"lineCount":30,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/types.ts",{"id":"file:/workspace/apps/web/src/types.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/types.ts","metadata":{"lineCount":6,"featureKind":"library","trustBoundaries":[]}}],["file:/workspace/apps/web/src/api/sessions.ts",{"id":"file:/workspace/apps/web/src/api/sessions.ts","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/api/sessions.ts","metadata":{"lineCount":131,"featureKind":"library","trustBoundaries":["network","external-api","serialization","auth","user-input"]}}],["/workspace/apps/web/src/api/sessions.ts:listInstances",{"id":"/workspace/apps/web/src/api/sessions.ts:listInstances","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/sessions.ts","symbolName":"listInstances","symbolKind":"function","exported":true,"metadata":{"line":20,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization","auth","user-input"]}}],["/workspace/apps/web/src/api/sessions.ts:createInstance",{"id":"/workspace/apps/web/src/api/sessions.ts:createInstance","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/sessions.ts","symbolName":"createInstance","symbolKind":"function","exported":true,"metadata":{"line":30,"column":1,"cyclomaticComplexity":3,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization","auth","user-input"]}}],["/workspace/apps/web/src/api/sessions.ts:startInstance",{"id":"/workspace/apps/web/src/api/sessions.ts:startInstance","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/sessions.ts","symbolName":"startInstance","symbolKind":"function","exported":true,"metadata":{"line":54,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization","auth","user-input"]}}],["/workspace/apps/web/src/api/sessions.ts:stopInstance",{"id":"/workspace/apps/web/src/api/sessions.ts:stopInstance","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/sessions.ts","symbolName":"stopInstance","symbolKind":"function","exported":true,"metadata":{"line":71,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization","auth","user-input"]}}],["/workspace/apps/web/src/api/sessions.ts:restartInstance",{"id":"/workspace/apps/web/src/api/sessions.ts:restartInstance","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/sessions.ts","symbolName":"restartInstance","symbolKind":"function","exported":true,"metadata":{"line":82,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization","auth","user-input"]}}],["/workspace/apps/web/src/api/sessions.ts:deleteInstance",{"id":"/workspace/apps/web/src/api/sessions.ts:deleteInstance","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/sessions.ts","symbolName":"deleteInstance","symbolKind":"function","exported":true,"metadata":{"line":93,"column":1,"cyclomaticComplexity":2,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization","auth","user-input"]}}],["/workspace/apps/web/src/api/sessions.ts:getUserSessions",{"id":"/workspace/apps/web/src/api/sessions.ts:getUserSessions","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/sessions.ts","symbolName":"getUserSessions","symbolKind":"function","exported":true,"metadata":{"line":105,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization","auth","user-input"]}}],["/workspace/apps/web/src/api/sessions.ts:checkInstanceHealth",{"id":"/workspace/apps/web/src/api/sessions.ts:checkInstanceHealth","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/sessions.ts","symbolName":"checkInstanceHealth","symbolKind":"function","exported":true,"metadata":{"line":110,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization","auth","user-input"]}}],["/workspace/apps/web/src/api/sessions.ts:recreateInstanceTunnel",{"id":"/workspace/apps/web/src/api/sessions.ts:recreateInstanceTunnel","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/api/sessions.ts","symbolName":"recreateInstanceTunnel","symbolKind":"function","exported":true,"metadata":{"line":121,"column":1,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":["network","external-api","serialization","auth","user-input"]}}],["file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx",{"id":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","kind":"file","language":"jsts","filePath":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","metadata":{"lineCount":330,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:getStatusText",{"id":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:getStatusText","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","symbolName":"getStatusText","symbolKind":"function","exported":false,"metadata":{"line":36,"column":1,"cyclomaticComplexity":9,"maxNestingDepth":2,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:TerminalComponent",{"id":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:TerminalComponent","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","symbolName":"TerminalComponent","symbolKind":"function","exported":true,"metadata":{"line":53,"column":59,"cyclomaticComplexity":23,"maxNestingDepth":1,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}],["/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:",{"id":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","kind":"symbol","language":"jsts","filePath":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","symbolName":"","symbolKind":"function","exported":false,"metadata":{"line":297,"column":18,"cyclomaticComplexity":1,"maxNestingDepth":0,"isBoundaryWrapper":false,"isPassThroughWrapper":false,"featureKind":"library","trustBoundaries":[]}}]],"edges":[{"from":"file:/workspace/apps/web/scripts/check-structure.js","to":"external:fs","kind":"imports"},{"from":"file:/workspace/apps/web/scripts/check-structure.js","to":"external:path","kind":"imports"},{"from":"file:/workspace/apps/web/scripts/check-structure.js","to":"external:url","kind":"imports"},{"from":"file:/workspace/apps/web/scripts/check-structure.js","to":"/workspace/apps/web/scripts/check-structure.js:checkFileSize","kind":"contains"},{"from":"file:/workspace/apps/web/scripts/check-structure.js","to":"/workspace/apps/web/scripts/check-structure.js:checkFileSize","kind":"defines"},{"from":"/workspace/apps/web/scripts/check-structure.js:checkFileSize","to":"external:fs.readFileSync","kind":"calls","metadata":{"unresolvedName":"fs.readFileSync"}},{"from":"/workspace/apps/web/scripts/check-structure.js:checkFileSize","to":"external:content.split","kind":"calls","metadata":{"unresolvedName":"content.split"}},{"from":"/workspace/apps/web/scripts/check-structure.js:checkFileSize","to":"external:path.relative","kind":"calls","metadata":{"unresolvedName":"path.relative"}},{"from":"/workspace/apps/web/scripts/check-structure.js:checkFileSize","to":"external:OVERSIZE_ALLOWLIST.includes","kind":"calls","metadata":{"unresolvedName":"OVERSIZE_ALLOWLIST.includes"}},{"from":"/workspace/apps/web/scripts/check-structure.js:checkFileSize","to":"external:console.warn","kind":"calls","metadata":{"unresolvedName":"console.warn"}},{"from":"/workspace/apps/web/scripts/check-structure.js:checkFileSize","to":"external:console.error","kind":"calls","metadata":{"unresolvedName":"console.error"}},{"from":"file:/workspace/apps/web/scripts/check-structure.js","to":"/workspace/apps/web/scripts/check-structure.js:walk","kind":"contains"},{"from":"file:/workspace/apps/web/scripts/check-structure.js","to":"/workspace/apps/web/scripts/check-structure.js:walk","kind":"defines"},{"from":"/workspace/apps/web/scripts/check-structure.js:walk","to":"external:fs.readdirSync","kind":"calls","metadata":{"unresolvedName":"fs.readdirSync"}},{"from":"/workspace/apps/web/scripts/check-structure.js:walk","to":"external:path.join","kind":"calls","metadata":{"unresolvedName":"path.join"}},{"from":"/workspace/apps/web/scripts/check-structure.js:walk","to":"external:entry.isDirectory","kind":"calls","metadata":{"unresolvedName":"entry.isDirectory"}},{"from":"/workspace/apps/web/scripts/check-structure.js:walk","to":"external:entry.name.startsWith","kind":"calls","metadata":{"unresolvedName":"entry.name.startsWith"}},{"from":"/workspace/apps/web/scripts/check-structure.js:walk","to":"/workspace/apps/web/scripts/check-structure.js:walk","kind":"calls","metadata":{"unresolvedName":"walk"}},{"from":"/workspace/apps/web/scripts/check-structure.js:walk","to":"symbol-name:callback","kind":"calls","metadata":{"unresolvedName":"callback"}},{"from":"file:/workspace/apps/web/scripts/check-structure.js","to":"/workspace/apps/web/scripts/check-structure.js:","kind":"contains"},{"from":"file:/workspace/apps/web/scripts/check-structure.js","to":"/workspace/apps/web/scripts/check-structure.js:","kind":"defines"},{"from":"/workspace/apps/web/scripts/check-structure.js:","to":"external:path.extname","kind":"calls","metadata":{"unresolvedName":"path.extname"}},{"from":"/workspace/apps/web/scripts/check-structure.js:","to":"external:[\".ts\", \".tsx\", \".py\", \".css\"].includes","kind":"calls","metadata":{"unresolvedName":"[\".ts\", \".tsx\", \".py\", \".css\"].includes"}},{"from":"/workspace/apps/web/scripts/check-structure.js:","to":"/workspace/apps/web/scripts/check-structure.js:checkFileSize","kind":"calls","metadata":{"unresolvedName":"checkFileSize"}},{"from":"file:/workspace/apps/web/src/api/client.ts","to":"external:axios","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/client.ts","to":"/workspace/apps/web/src/api/client.ts:shouldSkipAuthRedirect","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/client.ts","to":"/workspace/apps/web/src/api/client.ts:shouldSkipAuthRedirect","kind":"defines"},{"from":"/workspace/apps/web/src/api/client.ts:shouldSkipAuthRedirect","to":"external:path.startsWith","kind":"calls","metadata":{"unresolvedName":"path.startsWith"}},{"from":"file:/workspace/apps/web/src/api/client.ts","to":"/workspace/apps/web/src/api/client.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/client.ts","to":"/workspace/apps/web/src/api/client.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/api/client.ts:","to":"/workspace/apps/web/src/api/client.ts:shouldSkipAuthRedirect","kind":"calls","metadata":{"unresolvedName":"shouldSkipAuthRedirect"}},{"from":"/workspace/apps/web/src/api/client.ts:","to":"external:window.location.assign","kind":"calls","metadata":{"unresolvedName":"window.location.assign"}},{"from":"/workspace/apps/web/src/api/client.ts:","to":"external:Promise.reject","kind":"calls","metadata":{"unresolvedName":"Promise.reject"}},{"from":"/workspace/apps/web/src/api/client.ts:","to":"external:error.message?.includes","kind":"calls","metadata":{"unresolvedName":"error.message?.includes"}},{"from":"/workspace/apps/web/src/api/client.ts:","to":"external:retryCount.get","kind":"calls","metadata":{"unresolvedName":"retryCount.get"}},{"from":"/workspace/apps/web/src/api/client.ts:","to":"external:retryCount.set","kind":"calls","metadata":{"unresolvedName":"retryCount.set"}},{"from":"/workspace/apps/web/src/api/client.ts:","to":"symbol-name:setTimeout","kind":"calls","metadata":{"unresolvedName":"setTimeout"}},{"from":"/workspace/apps/web/src/api/client.ts:","to":"symbol-name:apiClient","kind":"calls","metadata":{"unresolvedName":"apiClient"}},{"from":"file:/workspace/apps/web/src/api/config-folders.test.ts","to":"external:vitest","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/config-folders.test.ts","to":"file:/workspace/apps/web/src/api/config-folders.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/config-folders.test.ts","to":"/workspace/apps/web/src/api/config-folders.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/config-folders.test.ts","to":"/workspace/apps/web/src/api/config-folders.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"symbol-name:describe","kind":"calls","metadata":{"unresolvedName":"describe"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:mockGet.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockGet.mockResolvedValue"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"/workspace/apps/web/src/api/config-folders.ts:listConfigFolders","kind":"calls","metadata":{"unresolvedName":"listConfigFolders"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:expect(result[0].name).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result[0].name).toBe"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:expect(result[0].files).toEqual","kind":"calls","metadata":{"unresolvedName":"expect(result[0].files).toEqual"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:expect(mockGet).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(mockGet).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:mockPost.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockPost.mockResolvedValue"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"/workspace/apps/web/src/api/config-folders.ts:createConfigFolder","kind":"calls","metadata":{"unresolvedName":"createConfigFolder"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:expect(result.name).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.name).toBe"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:expect(mockPost).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(mockPost).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:expect.objectContaining","kind":"calls","metadata":{"unresolvedName":"expect.objectContaining"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:mockPut.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockPut.mockResolvedValue"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"/workspace/apps/web/src/api/config-folders.ts:updateConfigFolder","kind":"calls","metadata":{"unresolvedName":"updateConfigFolder"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:expect(result.files).toEqual","kind":"calls","metadata":{"unresolvedName":"expect(result.files).toEqual"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:expect(mockPut).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(mockPut).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:mockDelete.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockDelete.mockResolvedValue"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"/workspace/apps/web/src/api/config-folders.ts:deleteConfigFolder","kind":"calls","metadata":{"unresolvedName":"deleteConfigFolder"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:expect(mockDelete).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(mockDelete).toHaveBeenCalledWith"}},{"from":"file:/workspace/apps/web/src/api/config-folders.test.ts","to":"/workspace/apps/web/src/api/config-folders.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/config-folders.test.ts","to":"/workspace/apps/web/src/api/config-folders.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:mockGet.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockGet.mockResolvedValue"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"/workspace/apps/web/src/api/config-folders.ts:listConfigFolders","kind":"calls","metadata":{"unresolvedName":"listConfigFolders"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:expect(result[0].name).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result[0].name).toBe"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:expect(result[0].files).toEqual","kind":"calls","metadata":{"unresolvedName":"expect(result[0].files).toEqual"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:expect(mockGet).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(mockGet).toHaveBeenCalledWith"}},{"from":"file:/workspace/apps/web/src/api/config-folders.test.ts","to":"/workspace/apps/web/src/api/config-folders.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/config-folders.test.ts","to":"/workspace/apps/web/src/api/config-folders.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:mockGet.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockGet.mockResolvedValue"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"/workspace/apps/web/src/api/config-folders.ts:listConfigFolders","kind":"calls","metadata":{"unresolvedName":"listConfigFolders"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:expect(result[0].name).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result[0].name).toBe"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:expect(result[0].files).toEqual","kind":"calls","metadata":{"unresolvedName":"expect(result[0].files).toEqual"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:expect(mockGet).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(mockGet).toHaveBeenCalledWith"}},{"from":"file:/workspace/apps/web/src/api/config-folders.test.ts","to":"/workspace/apps/web/src/api/config-folders.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/config-folders.test.ts","to":"/workspace/apps/web/src/api/config-folders.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:mockPost.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockPost.mockResolvedValue"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"/workspace/apps/web/src/api/config-folders.ts:createConfigFolder","kind":"calls","metadata":{"unresolvedName":"createConfigFolder"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:expect(result.name).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.name).toBe"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:expect(mockPost).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(mockPost).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:expect.objectContaining","kind":"calls","metadata":{"unresolvedName":"expect.objectContaining"}},{"from":"file:/workspace/apps/web/src/api/config-folders.test.ts","to":"/workspace/apps/web/src/api/config-folders.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/config-folders.test.ts","to":"/workspace/apps/web/src/api/config-folders.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:mockPost.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockPost.mockResolvedValue"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"/workspace/apps/web/src/api/config-folders.ts:createConfigFolder","kind":"calls","metadata":{"unresolvedName":"createConfigFolder"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:expect(result.name).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.name).toBe"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:expect(mockPost).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(mockPost).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:expect.objectContaining","kind":"calls","metadata":{"unresolvedName":"expect.objectContaining"}},{"from":"file:/workspace/apps/web/src/api/config-folders.test.ts","to":"/workspace/apps/web/src/api/config-folders.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/config-folders.test.ts","to":"/workspace/apps/web/src/api/config-folders.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:mockPut.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockPut.mockResolvedValue"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"/workspace/apps/web/src/api/config-folders.ts:updateConfigFolder","kind":"calls","metadata":{"unresolvedName":"updateConfigFolder"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:expect(result.files).toEqual","kind":"calls","metadata":{"unresolvedName":"expect(result.files).toEqual"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:expect(mockPut).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(mockPut).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:expect.objectContaining","kind":"calls","metadata":{"unresolvedName":"expect.objectContaining"}},{"from":"file:/workspace/apps/web/src/api/config-folders.test.ts","to":"/workspace/apps/web/src/api/config-folders.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/config-folders.test.ts","to":"/workspace/apps/web/src/api/config-folders.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:mockPut.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockPut.mockResolvedValue"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"/workspace/apps/web/src/api/config-folders.ts:updateConfigFolder","kind":"calls","metadata":{"unresolvedName":"updateConfigFolder"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:expect(result.files).toEqual","kind":"calls","metadata":{"unresolvedName":"expect(result.files).toEqual"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:expect(mockPut).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(mockPut).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:expect.objectContaining","kind":"calls","metadata":{"unresolvedName":"expect.objectContaining"}},{"from":"file:/workspace/apps/web/src/api/config-folders.test.ts","to":"/workspace/apps/web/src/api/config-folders.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/config-folders.test.ts","to":"/workspace/apps/web/src/api/config-folders.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:mockDelete.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockDelete.mockResolvedValue"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"/workspace/apps/web/src/api/config-folders.ts:deleteConfigFolder","kind":"calls","metadata":{"unresolvedName":"deleteConfigFolder"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:expect(mockDelete).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(mockDelete).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/api/config-folders.test.ts","to":"/workspace/apps/web/src/api/config-folders.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/config-folders.test.ts","to":"/workspace/apps/web/src/api/config-folders.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:mockDelete.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockDelete.mockResolvedValue"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"/workspace/apps/web/src/api/config-folders.ts:deleteConfigFolder","kind":"calls","metadata":{"unresolvedName":"deleteConfigFolder"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"external:expect(mockDelete).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(mockDelete).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/api/config-folders.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/api/config-folders.ts","to":"file:/workspace/apps/web/src/api/client.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/config-folders.ts","to":"file:/workspace/apps/web/src/types/config-folder.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/config-folders.ts","to":"/workspace/apps/web/src/api/config-folders.ts:listConfigFolders","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/config-folders.ts","to":"/workspace/apps/web/src/api/config-folders.ts:listConfigFolders","kind":"defines"},{"from":"/workspace/apps/web/src/api/config-folders.ts:listConfigFolders","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/config-folders.ts","to":"/workspace/apps/web/src/api/config-folders.ts:getConfigFolder","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/config-folders.ts","to":"/workspace/apps/web/src/api/config-folders.ts:getConfigFolder","kind":"defines"},{"from":"/workspace/apps/web/src/api/config-folders.ts:getConfigFolder","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/config-folders.ts","to":"/workspace/apps/web/src/api/config-folders.ts:createConfigFolder","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/config-folders.ts","to":"/workspace/apps/web/src/api/config-folders.ts:createConfigFolder","kind":"defines"},{"from":"/workspace/apps/web/src/api/config-folders.ts:createConfigFolder","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/config-folders.ts","to":"/workspace/apps/web/src/api/config-folders.ts:updateConfigFolder","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/config-folders.ts","to":"/workspace/apps/web/src/api/config-folders.ts:updateConfigFolder","kind":"defines"},{"from":"/workspace/apps/web/src/api/config-folders.ts:updateConfigFolder","to":"external:apiClient.put","kind":"calls","metadata":{"unresolvedName":"apiClient.put"}},{"from":"file:/workspace/apps/web/src/api/config-folders.ts","to":"/workspace/apps/web/src/api/config-folders.ts:deleteConfigFolder","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/config-folders.ts","to":"/workspace/apps/web/src/api/config-folders.ts:deleteConfigFolder","kind":"defines"},{"from":"/workspace/apps/web/src/api/config-folders.ts:deleteConfigFolder","to":"external:apiClient.delete","kind":"calls","metadata":{"unresolvedName":"apiClient.delete"}},{"from":"file:/workspace/apps/web/src/api/config-folders.ts","to":"/workspace/apps/web/src/api/config-folders.ts:addProjectOverride","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/config-folders.ts","to":"/workspace/apps/web/src/api/config-folders.ts:addProjectOverride","kind":"defines"},{"from":"/workspace/apps/web/src/api/config-folders.ts:addProjectOverride","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/config-folders.ts","to":"/workspace/apps/web/src/api/config-folders.ts:updateProjectOverride","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/config-folders.ts","to":"/workspace/apps/web/src/api/config-folders.ts:updateProjectOverride","kind":"defines"},{"from":"/workspace/apps/web/src/api/config-folders.ts:updateProjectOverride","to":"external:apiClient.put","kind":"calls","metadata":{"unresolvedName":"apiClient.put"}},{"from":"file:/workspace/apps/web/src/api/config-folders.ts","to":"/workspace/apps/web/src/api/config-folders.ts:deleteProjectOverride","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/config-folders.ts","to":"/workspace/apps/web/src/api/config-folders.ts:deleteProjectOverride","kind":"defines"},{"from":"/workspace/apps/web/src/api/config-folders.ts:deleteProjectOverride","to":"external:apiClient.delete","kind":"calls","metadata":{"unresolvedName":"apiClient.delete"}},{"from":"file:/workspace/apps/web/src/api/config_profiles.ts","to":"file:/workspace/apps/web/src/api/client.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/config_profiles.ts","to":"/workspace/apps/web/src/api/config_profiles.ts:listConfigProfiles","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/config_profiles.ts","to":"/workspace/apps/web/src/api/config_profiles.ts:listConfigProfiles","kind":"defines"},{"from":"/workspace/apps/web/src/api/config_profiles.ts:listConfigProfiles","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/config_profiles.ts","to":"/workspace/apps/web/src/api/config_profiles.ts:getConfigProfile","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/config_profiles.ts","to":"/workspace/apps/web/src/api/config_profiles.ts:getConfigProfile","kind":"defines"},{"from":"/workspace/apps/web/src/api/config_profiles.ts:getConfigProfile","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/config_profiles.ts","to":"/workspace/apps/web/src/api/config_profiles.ts:createConfigProfile","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/config_profiles.ts","to":"/workspace/apps/web/src/api/config_profiles.ts:createConfigProfile","kind":"defines"},{"from":"/workspace/apps/web/src/api/config_profiles.ts:createConfigProfile","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/config_profiles.ts","to":"/workspace/apps/web/src/api/config_profiles.ts:updateConfigProfile","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/config_profiles.ts","to":"/workspace/apps/web/src/api/config_profiles.ts:updateConfigProfile","kind":"defines"},{"from":"/workspace/apps/web/src/api/config_profiles.ts:updateConfigProfile","to":"external:apiClient.put","kind":"calls","metadata":{"unresolvedName":"apiClient.put"}},{"from":"file:/workspace/apps/web/src/api/config_profiles.ts","to":"/workspace/apps/web/src/api/config_profiles.ts:deleteConfigProfile","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/config_profiles.ts","to":"/workspace/apps/web/src/api/config_profiles.ts:deleteConfigProfile","kind":"defines"},{"from":"/workspace/apps/web/src/api/config_profiles.ts:deleteConfigProfile","to":"external:apiClient.delete","kind":"calls","metadata":{"unresolvedName":"apiClient.delete"}},{"from":"file:/workspace/apps/web/src/api/config_profiles.ts","to":"/workspace/apps/web/src/api/config_profiles.ts:updateProfileIncludes","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/config_profiles.ts","to":"/workspace/apps/web/src/api/config_profiles.ts:updateProfileIncludes","kind":"defines"},{"from":"/workspace/apps/web/src/api/config_profiles.ts:updateProfileIncludes","to":"external:apiClient.put","kind":"calls","metadata":{"unresolvedName":"apiClient.put"}},{"from":"file:/workspace/apps/web/src/api/config_profiles.ts","to":"/workspace/apps/web/src/api/config_profiles.ts:previewConfigProfile","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/config_profiles.ts","to":"/workspace/apps/web/src/api/config_profiles.ts:previewConfigProfile","kind":"defines"},{"from":"/workspace/apps/web/src/api/config_profiles.ts:previewConfigProfile","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/config_profiles.ts","to":"/workspace/apps/web/src/api/config_profiles.ts:resolveDefaultProfile","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/config_profiles.ts","to":"/workspace/apps/web/src/api/config_profiles.ts:resolveDefaultProfile","kind":"defines"},{"from":"/workspace/apps/web/src/api/config_profiles.ts:resolveDefaultProfile","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/config_profiles.ts","to":"/workspace/apps/web/src/api/config_profiles.ts:validateGitUrl","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/config_profiles.ts","to":"/workspace/apps/web/src/api/config_profiles.ts:validateGitUrl","kind":"defines"},{"from":"/workspace/apps/web/src/api/config_profiles.ts:validateGitUrl","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/dashboard.ts","to":"file:/workspace/apps/web/src/api/client.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/dashboard.ts","to":"/workspace/apps/web/src/api/dashboard.ts:getDashboardSummary","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/dashboard.ts","to":"/workspace/apps/web/src/api/dashboard.ts:getDashboardSummary","kind":"defines"},{"from":"/workspace/apps/web/src/api/dashboard.ts:getDashboardSummary","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/events.ts","to":"/workspace/apps/web/src/api/events.ts:createEventSource","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/events.ts","to":"/workspace/apps/web/src/api/events.ts:createEventSource","kind":"defines"},{"from":"file:/workspace/apps/web/src/api/events.ts","to":"/workspace/apps/web/src/api/events.ts:probeEventStreamStatus","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/events.ts","to":"/workspace/apps/web/src/api/events.ts:probeEventStreamStatus","kind":"defines"},{"from":"/workspace/apps/web/src/api/events.ts:probeEventStreamStatus","to":"symbol-name:setTimeout","kind":"calls","metadata":{"unresolvedName":"setTimeout"}},{"from":"/workspace/apps/web/src/api/events.ts:probeEventStreamStatus","to":"external:controller.abort","kind":"calls","metadata":{"unresolvedName":"controller.abort"}},{"from":"/workspace/apps/web/src/api/events.ts:probeEventStreamStatus","to":"symbol-name:fetch","kind":"calls","metadata":{"unresolvedName":"fetch"}},{"from":"/workspace/apps/web/src/api/events.ts:probeEventStreamStatus","to":"symbol-name:clearTimeout","kind":"calls","metadata":{"unresolvedName":"clearTimeout"}},{"from":"file:/workspace/apps/web/src/api/notifications.ts","to":"file:/workspace/apps/web/src/api/client.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/notifications.ts","to":"/workspace/apps/web/src/api/notifications.ts:getNotifications","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/notifications.ts","to":"/workspace/apps/web/src/api/notifications.ts:getNotifications","kind":"defines"},{"from":"/workspace/apps/web/src/api/notifications.ts:getNotifications","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/notifications.ts","to":"/workspace/apps/web/src/api/notifications.ts:getUnreadCount","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/notifications.ts","to":"/workspace/apps/web/src/api/notifications.ts:getUnreadCount","kind":"defines"},{"from":"/workspace/apps/web/src/api/notifications.ts:getUnreadCount","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/notifications.ts","to":"/workspace/apps/web/src/api/notifications.ts:markNotificationRead","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/notifications.ts","to":"/workspace/apps/web/src/api/notifications.ts:markNotificationRead","kind":"defines"},{"from":"/workspace/apps/web/src/api/notifications.ts:markNotificationRead","to":"external:apiClient.patch","kind":"calls","metadata":{"unresolvedName":"apiClient.patch"}},{"from":"file:/workspace/apps/web/src/api/notifications.ts","to":"/workspace/apps/web/src/api/notifications.ts:markAllNotificationsRead","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/notifications.ts","to":"/workspace/apps/web/src/api/notifications.ts:markAllNotificationsRead","kind":"defines"},{"from":"/workspace/apps/web/src/api/notifications.ts:markAllNotificationsRead","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/notifications.ts","to":"/workspace/apps/web/src/api/notifications.ts:dismissNotification","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/notifications.ts","to":"/workspace/apps/web/src/api/notifications.ts:dismissNotification","kind":"defines"},{"from":"/workspace/apps/web/src/api/notifications.ts:dismissNotification","to":"external:apiClient.delete","kind":"calls","metadata":{"unresolvedName":"apiClient.delete"}},{"from":"file:/workspace/apps/web/src/api/notifications.ts","to":"/workspace/apps/web/src/api/notifications.ts:clearAllNotifications","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/notifications.ts","to":"/workspace/apps/web/src/api/notifications.ts:clearAllNotifications","kind":"defines"},{"from":"/workspace/apps/web/src/api/notifications.ts:clearAllNotifications","to":"external:apiClient.delete","kind":"calls","metadata":{"unresolvedName":"apiClient.delete"}},{"from":"file:/workspace/apps/web/src/api/profile.ts","to":"file:/workspace/apps/web/src/api/client.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/profile.ts","to":"file:/workspace/apps/web/src/types","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/profile.ts","to":"/workspace/apps/web/src/api/profile.ts:getProfile","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/profile.ts","to":"/workspace/apps/web/src/api/profile.ts:getProfile","kind":"defines"},{"from":"/workspace/apps/web/src/api/profile.ts:getProfile","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/profile.ts","to":"/workspace/apps/web/src/api/profile.ts:updateProfile","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/profile.ts","to":"/workspace/apps/web/src/api/profile.ts:updateProfile","kind":"defines"},{"from":"/workspace/apps/web/src/api/profile.ts:updateProfile","to":"external:apiClient.put","kind":"calls","metadata":{"unresolvedName":"apiClient.put"}},{"from":"file:/workspace/apps/web/src/api/profile.ts","to":"/workspace/apps/web/src/api/profile.ts:uploadAvatar","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/profile.ts","to":"/workspace/apps/web/src/api/profile.ts:uploadAvatar","kind":"defines"},{"from":"/workspace/apps/web/src/api/profile.ts:uploadAvatar","to":"external:formData.append","kind":"calls","metadata":{"unresolvedName":"formData.append"}},{"from":"/workspace/apps/web/src/api/profile.ts:uploadAvatar","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/projects.ts","to":"file:/workspace/apps/web/src/api/client.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/projects.ts","to":"file:/workspace/apps/web/src/types","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/projects.ts","to":"/workspace/apps/web/src/api/projects.ts:listProjects","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/projects.ts","to":"/workspace/apps/web/src/api/projects.ts:listProjects","kind":"defines"},{"from":"/workspace/apps/web/src/api/projects.ts:listProjects","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/projects.ts","to":"/workspace/apps/web/src/api/projects.ts:createProject","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/projects.ts","to":"/workspace/apps/web/src/api/projects.ts:createProject","kind":"defines"},{"from":"/workspace/apps/web/src/api/projects.ts:createProject","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/projects.ts","to":"/workspace/apps/web/src/api/projects.ts:updateProject","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/projects.ts","to":"/workspace/apps/web/src/api/projects.ts:updateProject","kind":"defines"},{"from":"/workspace/apps/web/src/api/projects.ts:updateProject","to":"external:apiClient.patch","kind":"calls","metadata":{"unresolvedName":"apiClient.patch"}},{"from":"file:/workspace/apps/web/src/api/projects.ts","to":"/workspace/apps/web/src/api/projects.ts:deleteProject","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/projects.ts","to":"/workspace/apps/web/src/api/projects.ts:deleteProject","kind":"defines"},{"from":"/workspace/apps/web/src/api/projects.ts:deleteProject","to":"external:apiClient.delete","kind":"calls","metadata":{"unresolvedName":"apiClient.delete"}},{"from":"file:/workspace/apps/web/src/api/projects.ts","to":"/workspace/apps/web/src/api/projects.ts:setDefaultSSHKey","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/projects.ts","to":"/workspace/apps/web/src/api/projects.ts:setDefaultSSHKey","kind":"defines"},{"from":"/workspace/apps/web/src/api/projects.ts:setDefaultSSHKey","to":"external:apiClient.patch","kind":"calls","metadata":{"unresolvedName":"apiClient.patch"}},{"from":"file:/workspace/apps/web/src/api/settings.ts","to":"file:/workspace/apps/web/src/api/client.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/settings.ts","to":"/workspace/apps/web/src/api/settings.ts:getUserConfig","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/settings.ts","to":"/workspace/apps/web/src/api/settings.ts:getUserConfig","kind":"defines"},{"from":"/workspace/apps/web/src/api/settings.ts:getUserConfig","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/settings.ts","to":"/workspace/apps/web/src/api/settings.ts:updateUserConfig","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/settings.ts","to":"/workspace/apps/web/src/api/settings.ts:updateUserConfig","kind":"defines"},{"from":"/workspace/apps/web/src/api/settings.ts:updateUserConfig","to":"external:apiClient.patch","kind":"calls","metadata":{"unresolvedName":"apiClient.patch"}},{"from":"file:/workspace/apps/web/src/api/ssh-keys.ts","to":"file:/workspace/apps/web/src/api/client.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/ssh-keys.ts","to":"/workspace/apps/web/src/api/ssh-keys.ts:listSSHKeys","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/ssh-keys.ts","to":"/workspace/apps/web/src/api/ssh-keys.ts:listSSHKeys","kind":"defines"},{"from":"/workspace/apps/web/src/api/ssh-keys.ts:listSSHKeys","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/ssh-keys.ts","to":"/workspace/apps/web/src/api/ssh-keys.ts:createSSHKey","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/ssh-keys.ts","to":"/workspace/apps/web/src/api/ssh-keys.ts:createSSHKey","kind":"defines"},{"from":"/workspace/apps/web/src/api/ssh-keys.ts:createSSHKey","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/ssh-keys.ts","to":"/workspace/apps/web/src/api/ssh-keys.ts:deleteSSHKey","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/ssh-keys.ts","to":"/workspace/apps/web/src/api/ssh-keys.ts:deleteSSHKey","kind":"defines"},{"from":"/workspace/apps/web/src/api/ssh-keys.ts:deleteSSHKey","to":"external:apiClient.delete","kind":"calls","metadata":{"unresolvedName":"apiClient.delete"}},{"from":"file:/workspace/apps/web/src/api/ssh-keys.ts","to":"/workspace/apps/web/src/api/ssh-keys.ts:signPayload","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/ssh-keys.ts","to":"/workspace/apps/web/src/api/ssh-keys.ts:signPayload","kind":"defines"},{"from":"/workspace/apps/web/src/api/ssh-keys.ts:signPayload","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/ssh-keys.ts","to":"/workspace/apps/web/src/api/ssh-keys.ts:verifySignature","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/ssh-keys.ts","to":"/workspace/apps/web/src/api/ssh-keys.ts:verifySignature","kind":"defines"},{"from":"/workspace/apps/web/src/api/ssh-keys.ts:verifySignature","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/terminal.ts","to":"file:/workspace/apps/web/src/api/client.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/terminal.ts","to":"/workspace/apps/web/src/api/terminal.ts:listTerminalSessions","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/terminal.ts","to":"/workspace/apps/web/src/api/terminal.ts:listTerminalSessions","kind":"defines"},{"from":"/workspace/apps/web/src/api/terminal.ts:listTerminalSessions","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/terminal.ts","to":"/workspace/apps/web/src/api/terminal.ts:createTerminalSession","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/terminal.ts","to":"/workspace/apps/web/src/api/terminal.ts:createTerminalSession","kind":"defines"},{"from":"/workspace/apps/web/src/api/terminal.ts:createTerminalSession","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/terminal.ts","to":"/workspace/apps/web/src/api/terminal.ts:closeTerminalSession","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/terminal.ts","to":"/workspace/apps/web/src/api/terminal.ts:closeTerminalSession","kind":"defines"},{"from":"/workspace/apps/web/src/api/terminal.ts:closeTerminalSession","to":"external:apiClient.delete","kind":"calls","metadata":{"unresolvedName":"apiClient.delete"}},{"from":"file:/workspace/apps/web/src/api/terminal.ts","to":"/workspace/apps/web/src/api/terminal.ts:resetTerminalSession","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/terminal.ts","to":"/workspace/apps/web/src/api/terminal.ts:resetTerminalSession","kind":"defines"},{"from":"/workspace/apps/web/src/api/terminal.ts:resetTerminalSession","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/terminal.ts","to":"/workspace/apps/web/src/api/terminal.ts:renameTerminalSession","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/terminal.ts","to":"/workspace/apps/web/src/api/terminal.ts:renameTerminalSession","kind":"defines"},{"from":"/workspace/apps/web/src/api/terminal.ts:renameTerminalSession","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/tool-configs.ts","to":"file:/workspace/apps/web/src/api/client.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/tool-configs.ts","to":"file:/workspace/apps/web/src/types/tool-config.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/tool-configs.ts","to":"/workspace/apps/web/src/api/tool-configs.ts:listToolConfigs","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool-configs.ts","to":"/workspace/apps/web/src/api/tool-configs.ts:listToolConfigs","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool-configs.ts:listToolConfigs","to":"external:params.append","kind":"calls","metadata":{"unresolvedName":"params.append"}},{"from":"/workspace/apps/web/src/api/tool-configs.ts:listToolConfigs","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"/workspace/apps/web/src/api/tool-configs.ts:listToolConfigs","to":"external:params.toString","kind":"calls","metadata":{"unresolvedName":"params.toString"}},{"from":"file:/workspace/apps/web/src/api/tool-configs.ts","to":"/workspace/apps/web/src/api/tool-configs.ts:createToolConfig","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool-configs.ts","to":"/workspace/apps/web/src/api/tool-configs.ts:createToolConfig","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool-configs.ts:createToolConfig","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/tool-configs.ts","to":"/workspace/apps/web/src/api/tool-configs.ts:updateToolConfig","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool-configs.ts","to":"/workspace/apps/web/src/api/tool-configs.ts:updateToolConfig","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool-configs.ts:updateToolConfig","to":"external:apiClient.put","kind":"calls","metadata":{"unresolvedName":"apiClient.put"}},{"from":"file:/workspace/apps/web/src/api/tool-configs.ts","to":"/workspace/apps/web/src/api/tool-configs.ts:deleteToolConfig","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool-configs.ts","to":"/workspace/apps/web/src/api/tool-configs.ts:deleteToolConfig","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool-configs.ts:deleteToolConfig","to":"external:apiClient.delete","kind":"calls","metadata":{"unresolvedName":"apiClient.delete"}},{"from":"file:/workspace/apps/web/src/api/tool-configs.ts","to":"/workspace/apps/web/src/api/tool-configs.ts:getToolConfigDefaults","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool-configs.ts","to":"/workspace/apps/web/src/api/tool-configs.ts:getToolConfigDefaults","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool-configs.ts:getToolConfigDefaults","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/tool-types.test.ts","to":"external:vitest","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/tool-types.test.ts","to":"file:/workspace/apps/web/src/api/tool-types.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/tool-types.test.ts","to":"/workspace/apps/web/src/api/tool-types.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool-types.test.ts","to":"/workspace/apps/web/src/api/tool-types.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"symbol-name:describe","kind":"calls","metadata":{"unresolvedName":"describe"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:mockGet.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockGet.mockResolvedValue"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"/workspace/apps/web/src/api/tool-types.ts:listToolTypes","kind":"calls","metadata":{"unresolvedName":"listToolTypes"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(result[0].definition_type).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result[0].definition_type).toBe"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(result[0].dockerfile_template).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result[0].dockerfile_template).toBe"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(result[0].readiness_probe).toEqual","kind":"calls","metadata":{"unresolvedName":"expect(result[0].readiness_probe).toEqual"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(result[0].dockerfile_template).toBeNull","kind":"calls","metadata":{"unresolvedName":"expect(result[0].dockerfile_template).toBeNull"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:mockPost.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockPost.mockResolvedValue"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"/workspace/apps/web/src/api/tool-types.ts:createToolType","kind":"calls","metadata":{"unresolvedName":"createToolType"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(result.definition_type).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.definition_type).toBe"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(mockPost).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(mockPost).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect.objectContaining","kind":"calls","metadata":{"unresolvedName":"expect.objectContaining"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(result.readiness_probe).toEqual","kind":"calls","metadata":{"unresolvedName":"expect(result.readiness_probe).toEqual"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"/workspace/apps/web/src/api/tool-types.ts:validateToolType","kind":"calls","metadata":{"unresolvedName":"validateToolType"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(result.valid).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.valid).toBe"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(mockGet).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(mockGet).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(result.errors).toContain","kind":"calls","metadata":{"unresolvedName":"expect(result.errors).toContain"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:mockPut.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockPut.mockResolvedValue"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"/workspace/apps/web/src/api/tool-types.ts:updateToolType","kind":"calls","metadata":{"unresolvedName":"updateToolType"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(mockPut).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(mockPut).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:mockDelete.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockDelete.mockResolvedValue"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"/workspace/apps/web/src/api/tool-types.ts:deleteToolType","kind":"calls","metadata":{"unresolvedName":"deleteToolType"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(mockDelete).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(mockDelete).toHaveBeenCalledWith"}},{"from":"file:/workspace/apps/web/src/api/tool-types.test.ts","to":"/workspace/apps/web/src/api/tool-types.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool-types.test.ts","to":"/workspace/apps/web/src/api/tool-types.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:mockGet.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockGet.mockResolvedValue"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"/workspace/apps/web/src/api/tool-types.ts:listToolTypes","kind":"calls","metadata":{"unresolvedName":"listToolTypes"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(result[0].definition_type).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result[0].definition_type).toBe"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(result[0].dockerfile_template).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result[0].dockerfile_template).toBe"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(result[0].readiness_probe).toEqual","kind":"calls","metadata":{"unresolvedName":"expect(result[0].readiness_probe).toEqual"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(result[0].dockerfile_template).toBeNull","kind":"calls","metadata":{"unresolvedName":"expect(result[0].dockerfile_template).toBeNull"}},{"from":"file:/workspace/apps/web/src/api/tool-types.test.ts","to":"/workspace/apps/web/src/api/tool-types.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool-types.test.ts","to":"/workspace/apps/web/src/api/tool-types.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:mockGet.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockGet.mockResolvedValue"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"/workspace/apps/web/src/api/tool-types.ts:listToolTypes","kind":"calls","metadata":{"unresolvedName":"listToolTypes"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(result[0].definition_type).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result[0].definition_type).toBe"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(result[0].dockerfile_template).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result[0].dockerfile_template).toBe"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(result[0].readiness_probe).toEqual","kind":"calls","metadata":{"unresolvedName":"expect(result[0].readiness_probe).toEqual"}},{"from":"file:/workspace/apps/web/src/api/tool-types.test.ts","to":"/workspace/apps/web/src/api/tool-types.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool-types.test.ts","to":"/workspace/apps/web/src/api/tool-types.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:mockGet.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockGet.mockResolvedValue"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"/workspace/apps/web/src/api/tool-types.ts:listToolTypes","kind":"calls","metadata":{"unresolvedName":"listToolTypes"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(result[0].definition_type).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result[0].definition_type).toBe"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(result[0].dockerfile_template).toBeNull","kind":"calls","metadata":{"unresolvedName":"expect(result[0].dockerfile_template).toBeNull"}},{"from":"file:/workspace/apps/web/src/api/tool-types.test.ts","to":"/workspace/apps/web/src/api/tool-types.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool-types.test.ts","to":"/workspace/apps/web/src/api/tool-types.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:mockPost.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockPost.mockResolvedValue"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"/workspace/apps/web/src/api/tool-types.ts:createToolType","kind":"calls","metadata":{"unresolvedName":"createToolType"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(result.definition_type).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.definition_type).toBe"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(mockPost).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(mockPost).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect.objectContaining","kind":"calls","metadata":{"unresolvedName":"expect.objectContaining"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(result.readiness_probe).toEqual","kind":"calls","metadata":{"unresolvedName":"expect(result.readiness_probe).toEqual"}},{"from":"file:/workspace/apps/web/src/api/tool-types.test.ts","to":"/workspace/apps/web/src/api/tool-types.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool-types.test.ts","to":"/workspace/apps/web/src/api/tool-types.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:mockPost.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockPost.mockResolvedValue"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"/workspace/apps/web/src/api/tool-types.ts:createToolType","kind":"calls","metadata":{"unresolvedName":"createToolType"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(result.definition_type).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.definition_type).toBe"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(mockPost).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(mockPost).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect.objectContaining","kind":"calls","metadata":{"unresolvedName":"expect.objectContaining"}},{"from":"file:/workspace/apps/web/src/api/tool-types.test.ts","to":"/workspace/apps/web/src/api/tool-types.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool-types.test.ts","to":"/workspace/apps/web/src/api/tool-types.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:mockPost.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockPost.mockResolvedValue"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"/workspace/apps/web/src/api/tool-types.ts:createToolType","kind":"calls","metadata":{"unresolvedName":"createToolType"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(result.readiness_probe).toEqual","kind":"calls","metadata":{"unresolvedName":"expect(result.readiness_probe).toEqual"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/api/tool-types.test.ts","to":"/workspace/apps/web/src/api/tool-types.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool-types.test.ts","to":"/workspace/apps/web/src/api/tool-types.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:mockGet.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockGet.mockResolvedValue"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"/workspace/apps/web/src/api/tool-types.ts:validateToolType","kind":"calls","metadata":{"unresolvedName":"validateToolType"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(result.valid).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.valid).toBe"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(mockGet).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(mockGet).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(result.errors).toContain","kind":"calls","metadata":{"unresolvedName":"expect(result.errors).toContain"}},{"from":"file:/workspace/apps/web/src/api/tool-types.test.ts","to":"/workspace/apps/web/src/api/tool-types.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool-types.test.ts","to":"/workspace/apps/web/src/api/tool-types.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:mockGet.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockGet.mockResolvedValue"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"/workspace/apps/web/src/api/tool-types.ts:validateToolType","kind":"calls","metadata":{"unresolvedName":"validateToolType"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(result.valid).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.valid).toBe"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(mockGet).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(mockGet).toHaveBeenCalledWith"}},{"from":"file:/workspace/apps/web/src/api/tool-types.test.ts","to":"/workspace/apps/web/src/api/tool-types.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool-types.test.ts","to":"/workspace/apps/web/src/api/tool-types.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:mockGet.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockGet.mockResolvedValue"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"/workspace/apps/web/src/api/tool-types.ts:validateToolType","kind":"calls","metadata":{"unresolvedName":"validateToolType"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(result.valid).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.valid).toBe"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(result.errors).toContain","kind":"calls","metadata":{"unresolvedName":"expect(result.errors).toContain"}},{"from":"file:/workspace/apps/web/src/api/tool-types.test.ts","to":"/workspace/apps/web/src/api/tool-types.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool-types.test.ts","to":"/workspace/apps/web/src/api/tool-types.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:mockPut.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockPut.mockResolvedValue"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"/workspace/apps/web/src/api/tool-types.ts:updateToolType","kind":"calls","metadata":{"unresolvedName":"updateToolType"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(result.definition_type).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.definition_type).toBe"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(mockPut).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(mockPut).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect.objectContaining","kind":"calls","metadata":{"unresolvedName":"expect.objectContaining"}},{"from":"file:/workspace/apps/web/src/api/tool-types.test.ts","to":"/workspace/apps/web/src/api/tool-types.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool-types.test.ts","to":"/workspace/apps/web/src/api/tool-types.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:mockPut.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockPut.mockResolvedValue"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"/workspace/apps/web/src/api/tool-types.ts:updateToolType","kind":"calls","metadata":{"unresolvedName":"updateToolType"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(result.definition_type).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.definition_type).toBe"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(mockPut).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(mockPut).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect.objectContaining","kind":"calls","metadata":{"unresolvedName":"expect.objectContaining"}},{"from":"file:/workspace/apps/web/src/api/tool-types.test.ts","to":"/workspace/apps/web/src/api/tool-types.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool-types.test.ts","to":"/workspace/apps/web/src/api/tool-types.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:mockDelete.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockDelete.mockResolvedValue"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"/workspace/apps/web/src/api/tool-types.ts:deleteToolType","kind":"calls","metadata":{"unresolvedName":"deleteToolType"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(mockDelete).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(mockDelete).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/api/tool-types.test.ts","to":"/workspace/apps/web/src/api/tool-types.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool-types.test.ts","to":"/workspace/apps/web/src/api/tool-types.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:mockDelete.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockDelete.mockResolvedValue"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"/workspace/apps/web/src/api/tool-types.ts:deleteToolType","kind":"calls","metadata":{"unresolvedName":"deleteToolType"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"external:expect(mockDelete).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(mockDelete).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/api/tool-types.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/api/tool-types.ts","to":"file:/workspace/apps/web/src/api/client.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/tool-types.ts","to":"file:/workspace/apps/web/src/types/tool-type.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/tool-types.ts","to":"/workspace/apps/web/src/api/tool-types.ts:listToolTypes","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool-types.ts","to":"/workspace/apps/web/src/api/tool-types.ts:listToolTypes","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool-types.ts:listToolTypes","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/tool-types.ts","to":"/workspace/apps/web/src/api/tool-types.ts:getToolType","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool-types.ts","to":"/workspace/apps/web/src/api/tool-types.ts:getToolType","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool-types.ts:getToolType","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/tool-types.ts","to":"/workspace/apps/web/src/api/tool-types.ts:createToolType","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool-types.ts","to":"/workspace/apps/web/src/api/tool-types.ts:createToolType","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool-types.ts:createToolType","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/tool-types.ts","to":"/workspace/apps/web/src/api/tool-types.ts:updateToolType","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool-types.ts","to":"/workspace/apps/web/src/api/tool-types.ts:updateToolType","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool-types.ts:updateToolType","to":"external:apiClient.put","kind":"calls","metadata":{"unresolvedName":"apiClient.put"}},{"from":"file:/workspace/apps/web/src/api/tool-types.ts","to":"/workspace/apps/web/src/api/tool-types.ts:deleteToolType","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool-types.ts","to":"/workspace/apps/web/src/api/tool-types.ts:deleteToolType","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool-types.ts:deleteToolType","to":"external:apiClient.delete","kind":"calls","metadata":{"unresolvedName":"apiClient.delete"}},{"from":"file:/workspace/apps/web/src/api/tool-types.ts","to":"/workspace/apps/web/src/api/tool-types.ts:validateToolType","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool-types.ts","to":"/workspace/apps/web/src/api/tool-types.ts:validateToolType","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool-types.ts:validateToolType","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/tool_definitions.ts","to":"file:/workspace/apps/web/src/api/client.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/tool_definitions.ts","to":"/workspace/apps/web/src/api/tool_definitions.ts:listToolDefinitions","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool_definitions.ts","to":"/workspace/apps/web/src/api/tool_definitions.ts:listToolDefinitions","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool_definitions.ts:listToolDefinitions","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/tool_definitions.ts","to":"/workspace/apps/web/src/api/tool_definitions.ts:getToolDefinition","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool_definitions.ts","to":"/workspace/apps/web/src/api/tool_definitions.ts:getToolDefinition","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool_definitions.ts:getToolDefinition","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/tool_definitions.ts","to":"/workspace/apps/web/src/api/tool_definitions.ts:createToolDefinition","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool_definitions.ts","to":"/workspace/apps/web/src/api/tool_definitions.ts:createToolDefinition","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool_definitions.ts:createToolDefinition","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/tool_definitions.ts","to":"/workspace/apps/web/src/api/tool_definitions.ts:updateToolDefinition","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool_definitions.ts","to":"/workspace/apps/web/src/api/tool_definitions.ts:updateToolDefinition","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool_definitions.ts:updateToolDefinition","to":"external:apiClient.put","kind":"calls","metadata":{"unresolvedName":"apiClient.put"}},{"from":"file:/workspace/apps/web/src/api/tool_definitions.ts","to":"/workspace/apps/web/src/api/tool_definitions.ts:deleteToolDefinition","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool_definitions.ts","to":"/workspace/apps/web/src/api/tool_definitions.ts:deleteToolDefinition","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool_definitions.ts:deleteToolDefinition","to":"external:apiClient.delete","kind":"calls","metadata":{"unresolvedName":"apiClient.delete"}},{"from":"file:/workspace/apps/web/src/api/tool_definitions.ts","to":"/workspace/apps/web/src/api/tool_definitions.ts:compileToolDefinition","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/tool_definitions.ts","to":"/workspace/apps/web/src/api/tool_definitions.ts:compileToolDefinition","kind":"defines"},{"from":"/workspace/apps/web/src/api/tool_definitions.ts:compileToolDefinition","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/workspace-files.ts","to":"file:/workspace/apps/web/src/api/client.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/workspace-files.ts","to":"/workspace/apps/web/src/api/workspace-files.ts:listWorkspaceFiles","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/workspace-files.ts","to":"/workspace/apps/web/src/api/workspace-files.ts:listWorkspaceFiles","kind":"defines"},{"from":"/workspace/apps/web/src/api/workspace-files.ts:listWorkspaceFiles","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/workspace-files.ts","to":"/workspace/apps/web/src/api/workspace-files.ts:getWorkspaceFileContent","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/workspace-files.ts","to":"/workspace/apps/web/src/api/workspace-files.ts:getWorkspaceFileContent","kind":"defines"},{"from":"/workspace/apps/web/src/api/workspace-files.ts:getWorkspaceFileContent","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/workspace-files.ts","to":"/workspace/apps/web/src/api/workspace-files.ts:saveWorkspaceFile","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/workspace-files.ts","to":"/workspace/apps/web/src/api/workspace-files.ts:saveWorkspaceFile","kind":"defines"},{"from":"/workspace/apps/web/src/api/workspace-files.ts:saveWorkspaceFile","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/workspace-git.ts","to":"file:/workspace/apps/web/src/api/client.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/workspace-git.ts","to":"/workspace/apps/web/src/api/workspace-git.ts:getGitStatus","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/workspace-git.ts","to":"/workspace/apps/web/src/api/workspace-git.ts:getGitStatus","kind":"defines"},{"from":"/workspace/apps/web/src/api/workspace-git.ts:getGitStatus","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/workspace-git.ts","to":"/workspace/apps/web/src/api/workspace-git.ts:getGitBranches","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/workspace-git.ts","to":"/workspace/apps/web/src/api/workspace-git.ts:getGitBranches","kind":"defines"},{"from":"/workspace/apps/web/src/api/workspace-git.ts:getGitBranches","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/workspace-git.ts","to":"/workspace/apps/web/src/api/workspace-git.ts:gitCommit","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/workspace-git.ts","to":"/workspace/apps/web/src/api/workspace-git.ts:gitCommit","kind":"defines"},{"from":"/workspace/apps/web/src/api/workspace-git.ts:gitCommit","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/workspace-git.ts","to":"/workspace/apps/web/src/api/workspace-git.ts:gitPush","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/workspace-git.ts","to":"/workspace/apps/web/src/api/workspace-git.ts:gitPush","kind":"defines"},{"from":"/workspace/apps/web/src/api/workspace-git.ts:gitPush","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/workspace-git.ts","to":"/workspace/apps/web/src/api/workspace-git.ts:gitPull","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/workspace-git.ts","to":"/workspace/apps/web/src/api/workspace-git.ts:gitPull","kind":"defines"},{"from":"/workspace/apps/web/src/api/workspace-git.ts:gitPull","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/workspace-git.ts","to":"/workspace/apps/web/src/api/workspace-git.ts:gitFetch","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/workspace-git.ts","to":"/workspace/apps/web/src/api/workspace-git.ts:gitFetch","kind":"defines"},{"from":"/workspace/apps/web/src/api/workspace-git.ts:gitFetch","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/workspace-git.ts","to":"/workspace/apps/web/src/api/workspace-git.ts:gitCheckout","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/workspace-git.ts","to":"/workspace/apps/web/src/api/workspace-git.ts:gitCheckout","kind":"defines"},{"from":"/workspace/apps/web/src/api/workspace-git.ts:gitCheckout","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/workspace-git.ts","to":"/workspace/apps/web/src/api/workspace-git.ts:getGitHistory","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/workspace-git.ts","to":"/workspace/apps/web/src/api/workspace-git.ts:getGitHistory","kind":"defines"},{"from":"/workspace/apps/web/src/api/workspace-git.ts:getGitHistory","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/workspace-instances.ts","to":"file:/workspace/apps/web/src/api/client.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/workspace-instances.ts","to":"file:/workspace/apps/web/src/api/sessions.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/workspace-instances.ts","to":"/workspace/apps/web/src/api/workspace-instances.ts:listWorkspaceInstances","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/workspace-instances.ts","to":"/workspace/apps/web/src/api/workspace-instances.ts:listWorkspaceInstances","kind":"defines"},{"from":"/workspace/apps/web/src/api/workspace-instances.ts:listWorkspaceInstances","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/workspace-instances.ts","to":"/workspace/apps/web/src/api/workspace-instances.ts:createWorkspaceInstance","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/workspace-instances.ts","to":"/workspace/apps/web/src/api/workspace-instances.ts:createWorkspaceInstance","kind":"defines"},{"from":"/workspace/apps/web/src/api/workspace-instances.ts:createWorkspaceInstance","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/workspaces.ts","to":"file:/workspace/apps/web/src/api/client.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/workspaces.ts","to":"file:/workspace/apps/web/src/types/workspace.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/workspaces.ts","to":"/workspace/apps/web/src/api/workspaces.ts:workspaceUrl","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/workspaces.ts","to":"/workspace/apps/web/src/api/workspaces.ts:workspaceUrl","kind":"defines"},{"from":"file:/workspace/apps/web/src/api/workspaces.ts","to":"/workspace/apps/web/src/api/workspaces.ts:listWorkspaces","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/workspaces.ts","to":"/workspace/apps/web/src/api/workspaces.ts:listWorkspaces","kind":"defines"},{"from":"/workspace/apps/web/src/api/workspaces.ts:listWorkspaces","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"/workspace/apps/web/src/api/workspaces.ts:listWorkspaces","to":"/workspace/apps/web/src/api/workspaces.ts:workspaceUrl","kind":"calls","metadata":{"unresolvedName":"workspaceUrl"}},{"from":"file:/workspace/apps/web/src/api/workspaces.ts","to":"/workspace/apps/web/src/api/workspaces.ts:listAllWorkspaces","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/workspaces.ts","to":"/workspace/apps/web/src/api/workspaces.ts:listAllWorkspaces","kind":"defines"},{"from":"/workspace/apps/web/src/api/workspaces.ts:listAllWorkspaces","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/workspaces.ts","to":"/workspace/apps/web/src/api/workspaces.ts:createWorkspace","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/workspaces.ts","to":"/workspace/apps/web/src/api/workspaces.ts:createWorkspace","kind":"defines"},{"from":"/workspace/apps/web/src/api/workspaces.ts:createWorkspace","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"/workspace/apps/web/src/api/workspaces.ts:createWorkspace","to":"/workspace/apps/web/src/api/workspaces.ts:workspaceUrl","kind":"calls","metadata":{"unresolvedName":"workspaceUrl"}},{"from":"file:/workspace/apps/web/src/api/workspaces.ts","to":"/workspace/apps/web/src/api/workspaces.ts:createWorkspaceTopLevel","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/workspaces.ts","to":"/workspace/apps/web/src/api/workspaces.ts:createWorkspaceTopLevel","kind":"defines"},{"from":"/workspace/apps/web/src/api/workspaces.ts:createWorkspaceTopLevel","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/workspaces.ts","to":"/workspace/apps/web/src/api/workspaces.ts:getWorkspace","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/workspaces.ts","to":"/workspace/apps/web/src/api/workspaces.ts:getWorkspace","kind":"defines"},{"from":"/workspace/apps/web/src/api/workspaces.ts:getWorkspace","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"/workspace/apps/web/src/api/workspaces.ts:getWorkspace","to":"/workspace/apps/web/src/api/workspaces.ts:workspaceUrl","kind":"calls","metadata":{"unresolvedName":"workspaceUrl"}},{"from":"file:/workspace/apps/web/src/api/workspaces.ts","to":"/workspace/apps/web/src/api/workspaces.ts:updateWorkspace","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/workspaces.ts","to":"/workspace/apps/web/src/api/workspaces.ts:updateWorkspace","kind":"defines"},{"from":"/workspace/apps/web/src/api/workspaces.ts:updateWorkspace","to":"external:apiClient.patch","kind":"calls","metadata":{"unresolvedName":"apiClient.patch"}},{"from":"/workspace/apps/web/src/api/workspaces.ts:updateWorkspace","to":"/workspace/apps/web/src/api/workspaces.ts:workspaceUrl","kind":"calls","metadata":{"unresolvedName":"workspaceUrl"}},{"from":"file:/workspace/apps/web/src/api/workspaces.ts","to":"/workspace/apps/web/src/api/workspaces.ts:deleteWorkspace","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/workspaces.ts","to":"/workspace/apps/web/src/api/workspaces.ts:deleteWorkspace","kind":"defines"},{"from":"/workspace/apps/web/src/api/workspaces.ts:deleteWorkspace","to":"external:apiClient.delete","kind":"calls","metadata":{"unresolvedName":"apiClient.delete"}},{"from":"file:/workspace/apps/web/src/api/workspaces.ts","to":"/workspace/apps/web/src/api/workspaces.ts:syncWorkspace","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/workspaces.ts","to":"/workspace/apps/web/src/api/workspaces.ts:syncWorkspace","kind":"defines"},{"from":"/workspace/apps/web/src/api/workspaces.ts:syncWorkspace","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"/workspace/apps/web/src/api/workspaces.ts:syncWorkspace","to":"/workspace/apps/web/src/api/workspaces.ts:workspaceUrl","kind":"calls","metadata":{"unresolvedName":"workspaceUrl"}},{"from":"file:/workspace/apps/web/src/components/ProtectedRoute.test.tsx","to":"external:@testing-library/react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/ProtectedRoute.test.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/ProtectedRoute.test.tsx","to":"external:vitest","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/ProtectedRoute.test.tsx","to":"file:/workspace/apps/web/src/components/ProtectedRoute.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/ProtectedRoute.test.tsx","to":"/workspace/apps/web/src/components/ProtectedRoute.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/ProtectedRoute.test.tsx","to":"/workspace/apps/web/src/components/ProtectedRoute.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/ProtectedRoute.test.tsx:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/components/ProtectedRoute.test.tsx:","to":"external:mockUseAuth.mockReturnValue","kind":"calls","metadata":{"unresolvedName":"mockUseAuth.mockReturnValue"}},{"from":"/workspace/apps/web/src/components/ProtectedRoute.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/ProtectedRoute.test.tsx:","to":"external:expect(screen.getByText(\"Checking session...\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Checking session...\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/ProtectedRoute.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/ProtectedRoute.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/components/ProtectedRoute.test.tsx:","to":"external:expect(screen.getByText(\"login page\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"login page\")).toBeInTheDocument"}},{"from":"file:/workspace/apps/web/src/components/ProtectedRoute.test.tsx","to":"/workspace/apps/web/src/components/ProtectedRoute.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/ProtectedRoute.test.tsx","to":"/workspace/apps/web/src/components/ProtectedRoute.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/ProtectedRoute.test.tsx:","to":"external:mockUseAuth.mockReturnValue","kind":"calls","metadata":{"unresolvedName":"mockUseAuth.mockReturnValue"}},{"from":"/workspace/apps/web/src/components/ProtectedRoute.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/ProtectedRoute.test.tsx:","to":"external:expect(screen.getByText(\"Checking session...\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Checking session...\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/ProtectedRoute.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/ProtectedRoute.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/components/ProtectedRoute.test.tsx","to":"/workspace/apps/web/src/components/ProtectedRoute.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/ProtectedRoute.test.tsx","to":"/workspace/apps/web/src/components/ProtectedRoute.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/ProtectedRoute.test.tsx:","to":"external:mockUseAuth.mockReturnValue","kind":"calls","metadata":{"unresolvedName":"mockUseAuth.mockReturnValue"}},{"from":"/workspace/apps/web/src/components/ProtectedRoute.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/ProtectedRoute.test.tsx:","to":"external:expect(screen.getByText(\"login page\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"login page\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/ProtectedRoute.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/ProtectedRoute.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/components/ProtectedRoute.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/ProtectedRoute.tsx","to":"file:/workspace/apps/web/src/state/auth.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/ProtectedRoute.tsx","to":"/workspace/apps/web/src/components/ProtectedRoute.tsx:ProtectedRoute","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/ProtectedRoute.tsx","to":"/workspace/apps/web/src/components/ProtectedRoute.tsx:ProtectedRoute","kind":"defines"},{"from":"/workspace/apps/web/src/components/ProtectedRoute.tsx:ProtectedRoute","to":"/workspace/apps/web/src/state/auth.tsx:useAuth","kind":"calls","metadata":{"unresolvedName":"useAuth"}},{"from":"/workspace/apps/web/src/components/ProtectedRoute.tsx:ProtectedRoute","to":"symbol-name:useLocation","kind":"calls","metadata":{"unresolvedName":"useLocation"}},{"from":"/workspace/apps/web/src/components/ProtectedRoute.tsx:ProtectedRoute","to":"symbol-name:encodeURIComponent","kind":"calls","metadata":{"unresolvedName":"encodeURIComponent"}},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"external:@/components/ui/Icon","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"file:/workspace/apps/web/src/api/sessions.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"file:/workspace/apps/web/src/types","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"external:@/api/git-repositories","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"external:@/api/tool-types","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"external:@/api/ssh-keys","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"file:/workspace/apps/web/src/api/config_profiles.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","kind":"defines"},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"/workspace/apps/web/src/api/ssh-keys.ts:listSSHKeys","kind":"calls","metadata":{"unresolvedName":"listSSHKeys"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"symbol-name:setSshKeys","kind":"calls","metadata":{"unresolvedName":"setSshKeys"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"symbol-name:loadKeys","kind":"calls","metadata":{"unresolvedName":"loadKeys"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"symbol-name:setConfigProfiles","kind":"calls","metadata":{"unresolvedName":"setConfigProfiles"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"symbol-name:setSelectedConfigProfile","kind":"calls","metadata":{"unresolvedName":"setSelectedConfigProfile"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"/workspace/apps/web/src/api/config_profiles.ts:listConfigProfiles","kind":"calls","metadata":{"unresolvedName":"listConfigProfiles"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"external:profiles.find","kind":"calls","metadata":{"unresolvedName":"profiles.find"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"/workspace/apps/web/src/components/create-session-form.tsx:loadProfiles","kind":"calls","metadata":{"unresolvedName":"loadProfiles"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"symbol-name:setBranches","kind":"calls","metadata":{"unresolvedName":"setBranches"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"symbol-name:setIsLoadingBranches","kind":"calls","metadata":{"unresolvedName":"setIsLoadingBranches"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"/workspace/apps/web/src/api/git-repositories.ts:listRepositoryBranches","kind":"calls","metadata":{"unresolvedName":"listRepositoryBranches"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"symbol-name:setBranch","kind":"calls","metadata":{"unresolvedName":"setBranch"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"symbol-name:setBaseBranch","kind":"calls","metadata":{"unresolvedName":"setBaseBranch"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"/workspace/apps/web/src/components/create-session-form.tsx:loadBranches","kind":"calls","metadata":{"unresolvedName":"loadBranches"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"external:repositories.filter","kind":"calls","metadata":{"unresolvedName":"repositories.filter"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"external:event.preventDefault","kind":"calls","metadata":{"unresolvedName":"event.preventDefault"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"external:repositories.find","kind":"calls","metadata":{"unresolvedName":"repositories.find"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"symbol-name:setProgress","kind":"calls","metadata":{"unresolvedName":"setProgress"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"symbol-name:setSelectedProject","kind":"calls","metadata":{"unresolvedName":"setSelectedProject"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"symbol-name:setSelectedRepo","kind":"calls","metadata":{"unresolvedName":"setSelectedRepo"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"symbol-name:setSelectedToolType","kind":"calls","metadata":{"unresolvedName":"setSelectedToolType"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"symbol-name:setDisplayName","kind":"calls","metadata":{"unresolvedName":"setDisplayName"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"symbol-name:setCloneMode","kind":"calls","metadata":{"unresolvedName":"setCloneMode"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"symbol-name:setIsCreatingNewBranch","kind":"calls","metadata":{"unresolvedName":"setIsCreatingNewBranch"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"symbol-name:setNewBranchName","kind":"calls","metadata":{"unresolvedName":"setNewBranchName"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"symbol-name:setSelectedSshKeyIds","kind":"calls","metadata":{"unresolvedName":"setSelectedSshKeyIds"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"symbol-name:onSuccess","kind":"calls","metadata":{"unresolvedName":"onSuccess"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"/workspace/apps/web/src/components/create-session-form.tsx:renderStep","kind":"calls","metadata":{"unresolvedName":"renderStep"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"external:projects.find","kind":"calls","metadata":{"unresolvedName":"projects.find"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"symbol-name:onProjectChange","kind":"calls","metadata":{"unresolvedName":"onProjectChange"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"external:projects.map","kind":"calls","metadata":{"unresolvedName":"projects.map"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"external:availableRepos.map","kind":"calls","metadata":{"unresolvedName":"availableRepos.map"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"external:toolTypes.map","kind":"calls","metadata":{"unresolvedName":"toolTypes.map"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"external:configProfiles.map","kind":"calls","metadata":{"unresolvedName":"configProfiles.map"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"external:sshKeys.map","kind":"calls","metadata":{"unresolvedName":"sshKeys.map"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"external:selectedSshKeyIds.includes","kind":"calls","metadata":{"unresolvedName":"selectedSshKeyIds.includes"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"external:prev.filter","kind":"calls","metadata":{"unresolvedName":"prev.filter"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"external:branches.map","kind":"calls","metadata":{"unresolvedName":"branches.map"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"external:sshKeys.find","kind":"calls","metadata":{"unresolvedName":"sshKeys.find"}},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"/workspace/apps/web/src/api/ssh-keys.ts:listSSHKeys","kind":"calls","metadata":{"unresolvedName":"listSSHKeys"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"symbol-name:setSshKeys","kind":"calls","metadata":{"unresolvedName":"setSshKeys"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"symbol-name:loadKeys","kind":"calls","metadata":{"unresolvedName":"loadKeys"}},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:loadKeys","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:loadKeys","kind":"defines"},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:loadKeys","to":"/workspace/apps/web/src/api/ssh-keys.ts:listSSHKeys","kind":"calls","metadata":{"unresolvedName":"listSSHKeys"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:loadKeys","to":"symbol-name:setSshKeys","kind":"calls","metadata":{"unresolvedName":"setSshKeys"}},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"symbol-name:setConfigProfiles","kind":"calls","metadata":{"unresolvedName":"setConfigProfiles"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"symbol-name:setSelectedConfigProfile","kind":"calls","metadata":{"unresolvedName":"setSelectedConfigProfile"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"/workspace/apps/web/src/api/config_profiles.ts:listConfigProfiles","kind":"calls","metadata":{"unresolvedName":"listConfigProfiles"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"external:profiles.find","kind":"calls","metadata":{"unresolvedName":"profiles.find"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"/workspace/apps/web/src/components/create-session-form.tsx:loadProfiles","kind":"calls","metadata":{"unresolvedName":"loadProfiles"}},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:loadProfiles","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:loadProfiles","kind":"defines"},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:loadProfiles","to":"/workspace/apps/web/src/api/config_profiles.ts:listConfigProfiles","kind":"calls","metadata":{"unresolvedName":"listConfigProfiles"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:loadProfiles","to":"symbol-name:setConfigProfiles","kind":"calls","metadata":{"unresolvedName":"setConfigProfiles"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:loadProfiles","to":"external:profiles.find","kind":"calls","metadata":{"unresolvedName":"profiles.find"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:loadProfiles","to":"symbol-name:setSelectedConfigProfile","kind":"calls","metadata":{"unresolvedName":"setSelectedConfigProfile"}},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"symbol-name:setBranches","kind":"calls","metadata":{"unresolvedName":"setBranches"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"symbol-name:setIsLoadingBranches","kind":"calls","metadata":{"unresolvedName":"setIsLoadingBranches"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"/workspace/apps/web/src/api/git-repositories.ts:listRepositoryBranches","kind":"calls","metadata":{"unresolvedName":"listRepositoryBranches"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"symbol-name:setBranch","kind":"calls","metadata":{"unresolvedName":"setBranch"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"symbol-name:setBaseBranch","kind":"calls","metadata":{"unresolvedName":"setBaseBranch"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"/workspace/apps/web/src/components/create-session-form.tsx:loadBranches","kind":"calls","metadata":{"unresolvedName":"loadBranches"}},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:loadBranches","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:loadBranches","kind":"defines"},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:loadBranches","to":"symbol-name:setIsLoadingBranches","kind":"calls","metadata":{"unresolvedName":"setIsLoadingBranches"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:loadBranches","to":"/workspace/apps/web/src/api/git-repositories.ts:listRepositoryBranches","kind":"calls","metadata":{"unresolvedName":"listRepositoryBranches"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:loadBranches","to":"symbol-name:setBranches","kind":"calls","metadata":{"unresolvedName":"setBranches"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:loadBranches","to":"symbol-name:setBranch","kind":"calls","metadata":{"unresolvedName":"setBranch"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:loadBranches","to":"symbol-name:setBaseBranch","kind":"calls","metadata":{"unresolvedName":"setBaseBranch"}},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:handleSubmit","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:handleSubmit","kind":"defines"},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:handleSubmit","to":"external:event.preventDefault","kind":"calls","metadata":{"unresolvedName":"event.preventDefault"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:handleSubmit","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:handleSubmit","to":"external:repositories.find","kind":"calls","metadata":{"unresolvedName":"repositories.find"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:handleSubmit","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:handleSubmit","to":"symbol-name:setProgress","kind":"calls","metadata":{"unresolvedName":"setProgress"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:handleSubmit","to":"symbol-name:setSelectedProject","kind":"calls","metadata":{"unresolvedName":"setSelectedProject"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:handleSubmit","to":"symbol-name:setSelectedRepo","kind":"calls","metadata":{"unresolvedName":"setSelectedRepo"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:handleSubmit","to":"symbol-name:setSelectedToolType","kind":"calls","metadata":{"unresolvedName":"setSelectedToolType"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:handleSubmit","to":"symbol-name:setDisplayName","kind":"calls","metadata":{"unresolvedName":"setDisplayName"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:handleSubmit","to":"symbol-name:setCloneMode","kind":"calls","metadata":{"unresolvedName":"setCloneMode"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:handleSubmit","to":"symbol-name:setBranch","kind":"calls","metadata":{"unresolvedName":"setBranch"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:handleSubmit","to":"symbol-name:setIsCreatingNewBranch","kind":"calls","metadata":{"unresolvedName":"setIsCreatingNewBranch"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:handleSubmit","to":"symbol-name:setNewBranchName","kind":"calls","metadata":{"unresolvedName":"setNewBranchName"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:handleSubmit","to":"symbol-name:setBaseBranch","kind":"calls","metadata":{"unresolvedName":"setBaseBranch"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:handleSubmit","to":"symbol-name:setBranches","kind":"calls","metadata":{"unresolvedName":"setBranches"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:handleSubmit","to":"symbol-name:setSelectedSshKeyIds","kind":"calls","metadata":{"unresolvedName":"setSelectedSshKeyIds"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:handleSubmit","to":"symbol-name:onSuccess","kind":"calls","metadata":{"unresolvedName":"onSuccess"}},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:renderStep","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:renderStep","kind":"defines"},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"symbol-name:setSelectedProject","kind":"calls","metadata":{"unresolvedName":"setSelectedProject"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"symbol-name:setSelectedRepo","kind":"calls","metadata":{"unresolvedName":"setSelectedRepo"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"symbol-name:setSelectedToolType","kind":"calls","metadata":{"unresolvedName":"setSelectedToolType"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"symbol-name:setCloneMode","kind":"calls","metadata":{"unresolvedName":"setCloneMode"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"symbol-name:setIsCreatingNewBranch","kind":"calls","metadata":{"unresolvedName":"setIsCreatingNewBranch"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"symbol-name:onProjectChange","kind":"calls","metadata":{"unresolvedName":"onProjectChange"}},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"symbol-name:setSelectedRepo","kind":"calls","metadata":{"unresolvedName":"setSelectedRepo"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"symbol-name:setSelectedToolType","kind":"calls","metadata":{"unresolvedName":"setSelectedToolType"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"symbol-name:setCloneMode","kind":"calls","metadata":{"unresolvedName":"setCloneMode"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"symbol-name:setIsCreatingNewBranch","kind":"calls","metadata":{"unresolvedName":"setIsCreatingNewBranch"}},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"symbol-name:setSelectedToolType","kind":"calls","metadata":{"unresolvedName":"setSelectedToolType"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"symbol-name:setCloneMode","kind":"calls","metadata":{"unresolvedName":"setCloneMode"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"symbol-name:setIsCreatingNewBranch","kind":"calls","metadata":{"unresolvedName":"setIsCreatingNewBranch"}},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"symbol-name:setSelectedSshKeyIds","kind":"calls","metadata":{"unresolvedName":"setSelectedSshKeyIds"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"external:prev.filter","kind":"calls","metadata":{"unresolvedName":"prev.filter"}},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"symbol-name:setCloneMode","kind":"calls","metadata":{"unresolvedName":"setCloneMode"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"symbol-name:setIsCreatingNewBranch","kind":"calls","metadata":{"unresolvedName":"setIsCreatingNewBranch"}},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"symbol-name:setCloneMode","kind":"calls","metadata":{"unresolvedName":"setCloneMode"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"symbol-name:setIsCreatingNewBranch","kind":"calls","metadata":{"unresolvedName":"setIsCreatingNewBranch"}},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"symbol-name:setIsCreatingNewBranch","kind":"calls","metadata":{"unresolvedName":"setIsCreatingNewBranch"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"symbol-name:setNewBranchName","kind":"calls","metadata":{"unresolvedName":"setNewBranchName"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"symbol-name:setBranch","kind":"calls","metadata":{"unresolvedName":"setBranch"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"symbol-name:setBaseBranch","kind":"calls","metadata":{"unresolvedName":"setBaseBranch"}},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/create-session-form.tsx","to":"/workspace/apps/web/src/components/create-session-form.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"external:repositories.find","kind":"calls","metadata":{"unresolvedName":"repositories.find"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:","to":"external:sshKeys.find","kind":"calls","metadata":{"unresolvedName":"sshKeys.find"}},{"from":"file:/workspace/apps/web/src/components/data-states.tsx","to":"external:@/components/ui/Icon","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"external:vitest","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"external:@testing-library/react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"file:/workspace/apps/web/src/components/event-toast-bridge.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"file:/workspace/apps/web/src/state/events.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"file:/workspace/apps/web/src/api/settings.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"file:/workspace/apps/web/src/components/toast-rules.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"file:/workspace/apps/web/src/types/events.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:importOriginal","kind":"calls","metadata":{"unresolvedName":"importOriginal"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:makeEvent","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:makeEvent","kind":"defines"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:flushPromises","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:flushPromises","kind":"defines"},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:flushPromises","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:flushPromises","to":"external:Promise.resolve","kind":"calls","metadata":{"unresolvedName":"Promise.resolve"}},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:Promise.resolve","kind":"calls","metadata":{"unresolvedName":"Promise.resolve"}},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:beforeEach","kind":"calls","metadata":{"unresolvedName":"beforeEach"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:vi.clearAllMocks","kind":"calls","metadata":{"unresolvedName":"vi.clearAllMocks"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:mockedUseEventContext.mockReturnValue","kind":"calls","metadata":{"unresolvedName":"mockedUseEventContext.mockReturnValue"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:mockedGetUserConfig.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetUserConfig.mockResolvedValue"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:afterEach","kind":"calls","metadata":{"unresolvedName":"afterEach"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:vi.restoreAllMocks","kind":"calls","metadata":{"unresolvedName":"vi.restoreAllMocks"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:makeEvent","kind":"calls","metadata":{"unresolvedName":"makeEvent"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:flushPromises","kind":"calls","metadata":{"unresolvedName":"flushPromises"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:expect(mockedHandleEventToast).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(mockedHandleEventToast).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:expect(mockedHandleEventToast).not.toHaveBeenCalled","kind":"calls","metadata":{"unresolvedName":"expect(mockedHandleEventToast).not.toHaveBeenCalled"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:expect(mockedHandleEventToast).toHaveBeenCalledTimes","kind":"calls","metadata":{"unresolvedName":"expect(mockedHandleEventToast).toHaveBeenCalledTimes"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:window.dispatchEvent","kind":"calls","metadata":{"unresolvedName":"window.dispatchEvent"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:rerender","kind":"calls","metadata":{"unresolvedName":"rerender"}},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:vi.clearAllMocks","kind":"calls","metadata":{"unresolvedName":"vi.clearAllMocks"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:mockedUseEventContext.mockReturnValue","kind":"calls","metadata":{"unresolvedName":"mockedUseEventContext.mockReturnValue"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:mockedGetUserConfig.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetUserConfig.mockResolvedValue"}},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:vi.restoreAllMocks","kind":"calls","metadata":{"unresolvedName":"vi.restoreAllMocks"}},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:makeEvent","kind":"calls","metadata":{"unresolvedName":"makeEvent"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:mockedUseEventContext.mockReturnValue","kind":"calls","metadata":{"unresolvedName":"mockedUseEventContext.mockReturnValue"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:flushPromises","kind":"calls","metadata":{"unresolvedName":"flushPromises"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:expect(mockedHandleEventToast).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(mockedHandleEventToast).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:mockedGetUserConfig.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetUserConfig.mockResolvedValue"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:makeEvent","kind":"calls","metadata":{"unresolvedName":"makeEvent"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:mockedUseEventContext.mockReturnValue","kind":"calls","metadata":{"unresolvedName":"mockedUseEventContext.mockReturnValue"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:flushPromises","kind":"calls","metadata":{"unresolvedName":"flushPromises"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:expect(mockedHandleEventToast).not.toHaveBeenCalled","kind":"calls","metadata":{"unresolvedName":"expect(mockedHandleEventToast).not.toHaveBeenCalled"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:mockedGetUserConfig.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetUserConfig.mockResolvedValue"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:makeEvent","kind":"calls","metadata":{"unresolvedName":"makeEvent"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:mockedUseEventContext.mockReturnValue","kind":"calls","metadata":{"unresolvedName":"mockedUseEventContext.mockReturnValue"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:flushPromises","kind":"calls","metadata":{"unresolvedName":"flushPromises"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:expect(mockedHandleEventToast).not.toHaveBeenCalled","kind":"calls","metadata":{"unresolvedName":"expect(mockedHandleEventToast).not.toHaveBeenCalled"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:mockedGetUserConfig.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetUserConfig.mockResolvedValue"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:makeEvent","kind":"calls","metadata":{"unresolvedName":"makeEvent"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:mockedUseEventContext.mockReturnValue","kind":"calls","metadata":{"unresolvedName":"mockedUseEventContext.mockReturnValue"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:flushPromises","kind":"calls","metadata":{"unresolvedName":"flushPromises"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:expect(mockedHandleEventToast).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(mockedHandleEventToast).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:mockedGetUserConfig.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetUserConfig.mockResolvedValue"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:makeEvent","kind":"calls","metadata":{"unresolvedName":"makeEvent"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:mockedUseEventContext.mockReturnValue","kind":"calls","metadata":{"unresolvedName":"mockedUseEventContext.mockReturnValue"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:flushPromises","kind":"calls","metadata":{"unresolvedName":"flushPromises"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:expect(mockedHandleEventToast).not.toHaveBeenCalled","kind":"calls","metadata":{"unresolvedName":"expect(mockedHandleEventToast).not.toHaveBeenCalled"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:makeEvent","kind":"calls","metadata":{"unresolvedName":"makeEvent"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:mockedUseEventContext.mockReturnValue","kind":"calls","metadata":{"unresolvedName":"mockedUseEventContext.mockReturnValue"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:flushPromises","kind":"calls","metadata":{"unresolvedName":"flushPromises"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:expect(mockedHandleEventToast).toHaveBeenCalledTimes","kind":"calls","metadata":{"unresolvedName":"expect(mockedHandleEventToast).toHaveBeenCalledTimes"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:window.dispatchEvent","kind":"calls","metadata":{"unresolvedName":"window.dispatchEvent"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:rerender","kind":"calls","metadata":{"unresolvedName":"rerender"}},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:window.dispatchEvent","kind":"calls","metadata":{"unresolvedName":"window.dispatchEvent"}},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:mockedGetUserConfig.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetUserConfig.mockResolvedValue"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:makeEvent","kind":"calls","metadata":{"unresolvedName":"makeEvent"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:mockedUseEventContext.mockReturnValue","kind":"calls","metadata":{"unresolvedName":"mockedUseEventContext.mockReturnValue"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:flushPromises","kind":"calls","metadata":{"unresolvedName":"flushPromises"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:expect(mockedHandleEventToast).not.toHaveBeenCalled","kind":"calls","metadata":{"unresolvedName":"expect(mockedHandleEventToast).not.toHaveBeenCalled"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:makeEvent","kind":"calls","metadata":{"unresolvedName":"makeEvent"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:mockedUseEventContext.mockReturnValue","kind":"calls","metadata":{"unresolvedName":"mockedUseEventContext.mockReturnValue"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:flushPromises","kind":"calls","metadata":{"unresolvedName":"flushPromises"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:expect(mockedHandleEventToast).toHaveBeenCalledTimes","kind":"calls","metadata":{"unresolvedName":"expect(mockedHandleEventToast).toHaveBeenCalledTimes"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.test.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:makeEvent","kind":"calls","metadata":{"unresolvedName":"makeEvent"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:mockedUseEventContext.mockReturnValue","kind":"calls","metadata":{"unresolvedName":"mockedUseEventContext.mockReturnValue"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:flushPromises","kind":"calls","metadata":{"unresolvedName":"flushPromises"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"external:expect(mockedHandleEventToast).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(mockedHandleEventToast).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.tsx","to":"file:/workspace/apps/web/src/state/events.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.tsx","to":"file:/workspace/apps/web/src/components/toast-rules.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.tsx","to":"file:/workspace/apps/web/src/api/settings.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.tsx","to":"file:/workspace/apps/web/src/api/settings.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.tsx:EventToastBridge","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.tsx:EventToastBridge","kind":"defines"},{"from":"/workspace/apps/web/src/components/event-toast-bridge.tsx:EventToastBridge","to":"/workspace/apps/web/src/state/events.tsx:useEventContext","kind":"calls","metadata":{"unresolvedName":"useEventContext"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.tsx:EventToastBridge","to":"symbol-name:useRef","kind":"calls","metadata":{"unresolvedName":"useRef"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.tsx:EventToastBridge","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.tsx:EventToastBridge","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.tsx:EventToastBridge","to":"external:getUserConfig()\n\t\t\t.then","kind":"calls","metadata":{"unresolvedName":"getUserConfig()\n\t\t\t.then"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.tsx:EventToastBridge","to":"/workspace/apps/web/src/api/settings.ts:getUserConfig","kind":"calls","metadata":{"unresolvedName":"getUserConfig"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.tsx:EventToastBridge","to":"symbol-name:setConfig","kind":"calls","metadata":{"unresolvedName":"setConfig"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.tsx:EventToastBridge","to":"external:window.addEventListener","kind":"calls","metadata":{"unresolvedName":"window.addEventListener"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.tsx:EventToastBridge","to":"external:window.removeEventListener","kind":"calls","metadata":{"unresolvedName":"window.removeEventListener"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.tsx:EventToastBridge","to":"external:processedRef.current.has","kind":"calls","metadata":{"unresolvedName":"processedRef.current.has"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.tsx:EventToastBridge","to":"external:processedRef.current.add","kind":"calls","metadata":{"unresolvedName":"processedRef.current.add"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.tsx:EventToastBridge","to":"/workspace/apps/web/src/components/toast-rules.ts:mapEventToCategory","kind":"calls","metadata":{"unresolvedName":"mapEventToCategory"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.tsx:EventToastBridge","to":"/workspace/apps/web/src/components/toast-rules.ts:mapEventToSeverity","kind":"calls","metadata":{"unresolvedName":"mapEventToSeverity"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.tsx:EventToastBridge","to":"external:config.notification_mute_categories.includes","kind":"calls","metadata":{"unresolvedName":"config.notification_mute_categories.includes"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.tsx:EventToastBridge","to":"/workspace/apps/web/src/components/toast-rules.ts:handleEventToast","kind":"calls","metadata":{"unresolvedName":"handleEventToast"}},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/event-toast-bridge.tsx:","to":"external:getUserConfig()\n\t\t\t.then","kind":"calls","metadata":{"unresolvedName":"getUserConfig()\n\t\t\t.then"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.tsx:","to":"/workspace/apps/web/src/api/settings.ts:getUserConfig","kind":"calls","metadata":{"unresolvedName":"getUserConfig"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.tsx:","to":"symbol-name:setConfig","kind":"calls","metadata":{"unresolvedName":"setConfig"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.tsx:","to":"external:window.addEventListener","kind":"calls","metadata":{"unresolvedName":"window.addEventListener"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.tsx:","to":"external:window.removeEventListener","kind":"calls","metadata":{"unresolvedName":"window.removeEventListener"}},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/event-toast-bridge.tsx:","to":"symbol-name:setConfig","kind":"calls","metadata":{"unresolvedName":"setConfig"}},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/event-toast-bridge.tsx:","to":"symbol-name:setConfig","kind":"calls","metadata":{"unresolvedName":"setConfig"}},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.tsx:handler","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.tsx:handler","kind":"defines"},{"from":"/workspace/apps/web/src/components/event-toast-bridge.tsx:handler","to":"symbol-name:setConfig","kind":"calls","metadata":{"unresolvedName":"setConfig"}},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/event-toast-bridge.tsx","to":"/workspace/apps/web/src/components/event-toast-bridge.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/event-toast-bridge.tsx:","to":"external:processedRef.current.has","kind":"calls","metadata":{"unresolvedName":"processedRef.current.has"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.tsx:","to":"external:processedRef.current.add","kind":"calls","metadata":{"unresolvedName":"processedRef.current.add"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.tsx:","to":"/workspace/apps/web/src/components/toast-rules.ts:mapEventToCategory","kind":"calls","metadata":{"unresolvedName":"mapEventToCategory"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.tsx:","to":"/workspace/apps/web/src/components/toast-rules.ts:mapEventToSeverity","kind":"calls","metadata":{"unresolvedName":"mapEventToSeverity"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.tsx:","to":"external:config.notification_mute_categories.includes","kind":"calls","metadata":{"unresolvedName":"config.notification_mute_categories.includes"}},{"from":"/workspace/apps/web/src/components/event-toast-bridge.tsx:","to":"/workspace/apps/web/src/components/toast-rules.ts:handleEventToast","kind":"calls","metadata":{"unresolvedName":"handleEventToast"}},{"from":"file:/workspace/apps/web/src/components/features/dashboard/ActiveSessionsList.tsx","to":"file:/workspace/apps/web/src/types/session.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/dashboard/ActiveSessionsList.tsx","to":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/dashboard/ActiveSessionsList.tsx","to":"/workspace/apps/web/src/components/features/dashboard/ActiveSessionsList.tsx:ActiveSessionsList","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/dashboard/ActiveSessionsList.tsx","to":"/workspace/apps/web/src/components/features/dashboard/ActiveSessionsList.tsx:ActiveSessionsList","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/dashboard/ActiveSessionsList.tsx:ActiveSessionsList","to":"external:sessions.map","kind":"calls","metadata":{"unresolvedName":"sessions.map"}},{"from":"/workspace/apps/web/src/components/features/dashboard/ActiveSessionsList.tsx:ActiveSessionsList","to":"symbol-name:onOpen","kind":"calls","metadata":{"unresolvedName":"onOpen"}},{"from":"/workspace/apps/web/src/components/features/dashboard/ActiveSessionsList.tsx:ActiveSessionsList","to":"symbol-name:onRecreateTunnel","kind":"calls","metadata":{"unresolvedName":"onRecreateTunnel"}},{"from":"/workspace/apps/web/src/components/features/dashboard/ActiveSessionsList.tsx:ActiveSessionsList","to":"symbol-name:onStop","kind":"calls","metadata":{"unresolvedName":"onStop"}},{"from":"/workspace/apps/web/src/components/features/dashboard/ActiveSessionsList.tsx:ActiveSessionsList","to":"symbol-name:onDelete","kind":"calls","metadata":{"unresolvedName":"onDelete"}},{"from":"file:/workspace/apps/web/src/components/features/dashboard/DashboardSummary.tsx","to":"file:/workspace/apps/web/src/api/dashboard.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/dashboard/DashboardSummary.tsx","to":"/workspace/apps/web/src/components/features/dashboard/DashboardSummary.tsx:DashboardSummary","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/dashboard/DashboardSummary.tsx","to":"/workspace/apps/web/src/components/features/dashboard/DashboardSummary.tsx:DashboardSummary","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/dashboard/DashboardSummary.tsx:DashboardSummary","to":"external:summaryCards.map","kind":"calls","metadata":{"unresolvedName":"summaryCards.map"}},{"from":"file:/workspace/apps/web/src/components/features/dashboard/ProjectsSection.tsx","to":"file:/workspace/apps/web/src/types/project.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/dashboard/ProjectsSection.tsx","to":"/workspace/apps/web/src/components/features/dashboard/ProjectsSection.tsx:ProjectsSection","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/dashboard/ProjectsSection.tsx","to":"/workspace/apps/web/src/components/features/dashboard/ProjectsSection.tsx:ProjectsSection","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/dashboard/ProjectsSection.tsx:ProjectsSection","to":"external:projects.map","kind":"calls","metadata":{"unresolvedName":"projects.map"}},{"from":"/workspace/apps/web/src/components/features/dashboard/ProjectsSection.tsx:ProjectsSection","to":"symbol-name:onOpenProject","kind":"calls","metadata":{"unresolvedName":"onOpenProject"}},{"from":"file:/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx","to":"file:/workspace/apps/web/src/types/project.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx","to":"file:/workspace/apps/web/src/types/git-repository.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx","to":"file:/workspace/apps/web/src/types/tool-type.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx","to":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx","to":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:QuickCreateForm","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx","to":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:QuickCreateForm","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:QuickCreateForm","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:QuickCreateForm","to":"symbol-name:setSelectedProject","kind":"calls","metadata":{"unresolvedName":"setSelectedProject"}},{"from":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:QuickCreateForm","to":"symbol-name:setSelectedRepo","kind":"calls","metadata":{"unresolvedName":"setSelectedRepo"}},{"from":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:QuickCreateForm","to":"symbol-name:onProjectChange","kind":"calls","metadata":{"unresolvedName":"onProjectChange"}},{"from":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:QuickCreateForm","to":"external:event.preventDefault","kind":"calls","metadata":{"unresolvedName":"event.preventDefault"}},{"from":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:QuickCreateForm","to":"symbol-name:onSubmit","kind":"calls","metadata":{"unresolvedName":"onSubmit"}},{"from":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:QuickCreateForm","to":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:handleProjectChange","kind":"calls","metadata":{"unresolvedName":"handleProjectChange"}},{"from":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:QuickCreateForm","to":"external:projects.map","kind":"calls","metadata":{"unresolvedName":"projects.map"}},{"from":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:QuickCreateForm","to":"external:repositories.map","kind":"calls","metadata":{"unresolvedName":"repositories.map"}},{"from":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:QuickCreateForm","to":"symbol-name:setSelectedToolType","kind":"calls","metadata":{"unresolvedName":"setSelectedToolType"}},{"from":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:QuickCreateForm","to":"external:toolTypes.map","kind":"calls","metadata":{"unresolvedName":"toolTypes.map"}},{"from":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:QuickCreateForm","to":"symbol-name:setDisplayName","kind":"calls","metadata":{"unresolvedName":"setDisplayName"}},{"from":"file:/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx","to":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:handleProjectChange","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx","to":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:handleProjectChange","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:handleProjectChange","to":"symbol-name:setSelectedProject","kind":"calls","metadata":{"unresolvedName":"setSelectedProject"}},{"from":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:handleProjectChange","to":"symbol-name:setSelectedRepo","kind":"calls","metadata":{"unresolvedName":"setSelectedRepo"}},{"from":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:handleProjectChange","to":"symbol-name:onProjectChange","kind":"calls","metadata":{"unresolvedName":"onProjectChange"}},{"from":"file:/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx","to":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:handleSubmit","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx","to":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:handleSubmit","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:handleSubmit","to":"external:event.preventDefault","kind":"calls","metadata":{"unresolvedName":"event.preventDefault"}},{"from":"/workspace/apps/web/src/components/features/dashboard/QuickCreateForm.tsx:handleSubmit","to":"symbol-name:onSubmit","kind":"calls","metadata":{"unresolvedName":"onSubmit"}},{"from":"file:/workspace/apps/web/src/components/features/dashboard/RecentSessionsSection.tsx","to":"file:/workspace/apps/web/src/types/session.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/dashboard/RecentSessionsSection.tsx","to":"/workspace/apps/web/src/components/features/dashboard/RecentSessionsSection.tsx:RecentSessionsSection","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/dashboard/RecentSessionsSection.tsx","to":"/workspace/apps/web/src/components/features/dashboard/RecentSessionsSection.tsx:RecentSessionsSection","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/dashboard/RecentSessionsSection.tsx:RecentSessionsSection","to":"external:sessions.map","kind":"calls","metadata":{"unresolvedName":"sessions.map"}},{"from":"/workspace/apps/web/src/components/features/dashboard/RecentSessionsSection.tsx:RecentSessionsSection","to":"symbol-name:onOpen","kind":"calls","metadata":{"unresolvedName":"onOpen"}},{"from":"file:/workspace/apps/web/src/components/features/git/CommitDialog.tsx","to":"file:/workspace/apps/web/src/components/features/git/CommitDialog.module.css","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/CommitDialog.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/CommitDialog.tsx","to":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/CommitDialog.tsx","to":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:CommitDialog","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/CommitDialog.tsx","to":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:CommitDialog","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:CommitDialog","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:CommitDialog","to":"external:originalContent.split","kind":"calls","metadata":{"unresolvedName":"originalContent.split"}},{"from":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:CommitDialog","to":"external:newContent.split","kind":"calls","metadata":{"unresolvedName":"newContent.split"}},{"from":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:CommitDialog","to":"external:Math.max","kind":"calls","metadata":{"unresolvedName":"Math.max"}},{"from":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:CommitDialog","to":"external:diff.push","kind":"calls","metadata":{"unresolvedName":"diff.push"}},{"from":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:CommitDialog","to":"external:message.trim","kind":"calls","metadata":{"unresolvedName":"message.trim"}},{"from":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:CommitDialog","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:CommitDialog","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:CommitDialog","to":"symbol-name:onCommit","kind":"calls","metadata":{"unresolvedName":"onCommit"}},{"from":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:CommitDialog","to":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:generateDiff","kind":"calls","metadata":{"unresolvedName":"generateDiff"}},{"from":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:CommitDialog","to":"external:diff.some","kind":"calls","metadata":{"unresolvedName":"diff.some"}},{"from":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:CommitDialog","to":"external:diff.map","kind":"calls","metadata":{"unresolvedName":"diff.map"}},{"from":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:CommitDialog","to":"symbol-name:setMessage","kind":"calls","metadata":{"unresolvedName":"setMessage"}},{"from":"file:/workspace/apps/web/src/components/features/git/CommitDialog.tsx","to":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:generateDiff","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/CommitDialog.tsx","to":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:generateDiff","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:generateDiff","to":"external:originalContent.split","kind":"calls","metadata":{"unresolvedName":"originalContent.split"}},{"from":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:generateDiff","to":"external:newContent.split","kind":"calls","metadata":{"unresolvedName":"newContent.split"}},{"from":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:generateDiff","to":"external:Math.max","kind":"calls","metadata":{"unresolvedName":"Math.max"}},{"from":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:generateDiff","to":"external:diff.push","kind":"calls","metadata":{"unresolvedName":"diff.push"}},{"from":"file:/workspace/apps/web/src/components/features/git/CommitDialog.tsx","to":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:handleCommit","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/CommitDialog.tsx","to":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:handleCommit","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:handleCommit","to":"external:message.trim","kind":"calls","metadata":{"unresolvedName":"message.trim"}},{"from":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:handleCommit","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:handleCommit","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/components/features/git/CommitDialog.tsx:handleCommit","to":"symbol-name:onCommit","kind":"calls","metadata":{"unresolvedName":"onCommit"}},{"from":"file:/workspace/apps/web/src/components/features/git/CommitPanel.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/CommitPanel.tsx","to":"file:/workspace/apps/web/src/api/git-repositories.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/CommitPanel.tsx","to":"file:/workspace/apps/web/src/components/features/git/CommitPanel.module.css","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/CommitPanel.tsx","to":"/workspace/apps/web/src/components/features/git/CommitPanel.tsx:CommitPanel","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/CommitPanel.tsx","to":"/workspace/apps/web/src/components/features/git/CommitPanel.tsx:CommitPanel","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/CommitPanel.tsx:CommitPanel","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/features/git/CommitPanel.tsx:CommitPanel","to":"external:message.trim","kind":"calls","metadata":{"unresolvedName":"message.trim"}},{"from":"/workspace/apps/web/src/components/features/git/CommitPanel.tsx:CommitPanel","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/components/features/git/CommitPanel.tsx:CommitPanel","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/components/features/git/CommitPanel.tsx:CommitPanel","to":"symbol-name:setMessage","kind":"calls","metadata":{"unresolvedName":"setMessage"}},{"from":"/workspace/apps/web/src/components/features/git/CommitPanel.tsx:CommitPanel","to":"symbol-name:onCommit","kind":"calls","metadata":{"unresolvedName":"onCommit"}},{"from":"/workspace/apps/web/src/components/features/git/CommitPanel.tsx:CommitPanel","to":"external:modified.map","kind":"calls","metadata":{"unresolvedName":"modified.map"}},{"from":"/workspace/apps/web/src/components/features/git/CommitPanel.tsx:CommitPanel","to":"external:added.map","kind":"calls","metadata":{"unresolvedName":"added.map"}},{"from":"/workspace/apps/web/src/components/features/git/CommitPanel.tsx:CommitPanel","to":"external:deleted.map","kind":"calls","metadata":{"unresolvedName":"deleted.map"}},{"from":"/workspace/apps/web/src/components/features/git/CommitPanel.tsx:CommitPanel","to":"external:untracked.map","kind":"calls","metadata":{"unresolvedName":"untracked.map"}},{"from":"file:/workspace/apps/web/src/components/features/git/CommitPanel.tsx","to":"/workspace/apps/web/src/components/features/git/CommitPanel.tsx:handleCommit","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/CommitPanel.tsx","to":"/workspace/apps/web/src/components/features/git/CommitPanel.tsx:handleCommit","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/CommitPanel.tsx:handleCommit","to":"external:message.trim","kind":"calls","metadata":{"unresolvedName":"message.trim"}},{"from":"/workspace/apps/web/src/components/features/git/CommitPanel.tsx:handleCommit","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/components/features/git/CommitPanel.tsx:handleCommit","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/components/features/git/CommitPanel.tsx:handleCommit","to":"symbol-name:setMessage","kind":"calls","metadata":{"unresolvedName":"setMessage"}},{"from":"/workspace/apps/web/src/components/features/git/CommitPanel.tsx:handleCommit","to":"symbol-name:onCommit","kind":"calls","metadata":{"unresolvedName":"onCommit"}},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx","to":"external:@testing-library/react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx","to":"external:vitest","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx","to":"file:/workspace/apps/web/src/components/features/git/FileBrowser.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx","to":"file:/workspace/apps/web/src/api/client.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx","to":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx","to":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"external:expect(screen.getByText(/loading files/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/loading files/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"external:mockedGet.mockResolvedValueOnce","kind":"calls","metadata":{"unresolvedName":"mockedGet.mockResolvedValueOnce"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"external:expect(screen.getByText(\"src\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"src\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"external:expect(screen.getByText(\"README.md\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"README.md\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"external:mockedGet.mockRejectedValueOnce","kind":"calls","metadata":{"unresolvedName":"mockedGet.mockRejectedValueOnce"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"external:expect(screen.getByText(/failed to load files/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/failed to load files/i)).toBeInTheDocument"}},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx","to":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx","to":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"external:expect(screen.getByText(/loading files/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/loading files/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx","to":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx","to":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"external:mockedGet.mockResolvedValueOnce","kind":"calls","metadata":{"unresolvedName":"mockedGet.mockResolvedValueOnce"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"external:expect(screen.getByText(\"src\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"src\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"external:expect(screen.getByText(\"README.md\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"README.md\")).toBeInTheDocument"}},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx","to":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx","to":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"external:expect(screen.getByText(\"src\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"src\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx","to":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx","to":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"external:mockedGet.mockRejectedValueOnce","kind":"calls","metadata":{"unresolvedName":"mockedGet.mockRejectedValueOnce"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"external:expect(screen.getByText(/failed to load files/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/failed to load files/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx","to":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx","to":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"external:expect(screen.getByText(/failed to load files/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/failed to load files/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.tsx","to":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.tsx","to":"file:/workspace/apps/web/src/api/client.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.tsx","to":"file:/workspace/apps/web/src/types/git-repository.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.tsx","to":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:FileBrowser","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.tsx","to":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:FileBrowser","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:FileBrowser","to":"symbol-name:useSearchParams","kind":"calls","metadata":{"unresolvedName":"useSearchParams"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:FileBrowser","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:FileBrowser","to":"external:searchParams.get","kind":"calls","metadata":{"unresolvedName":"searchParams.get"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:FileBrowser","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:FileBrowser","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:FileBrowser","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:FileBrowser","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:FileBrowser","to":"symbol-name:setEntries","kind":"calls","metadata":{"unresolvedName":"setEntries"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:FileBrowser","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:FileBrowser","to":"symbol-name:loadFiles","kind":"calls","metadata":{"unresolvedName":"loadFiles"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:FileBrowser","to":"external:window.addEventListener","kind":"calls","metadata":{"unresolvedName":"window.addEventListener"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:FileBrowser","to":"external:window.removeEventListener","kind":"calls","metadata":{"unresolvedName":"window.removeEventListener"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:FileBrowser","to":"external:newParams.set","kind":"calls","metadata":{"unresolvedName":"newParams.set"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:FileBrowser","to":"symbol-name:setSearchParams","kind":"calls","metadata":{"unresolvedName":"setSearchParams"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:FileBrowser","to":"external:path.split(\"/\").slice(0, -1).join","kind":"calls","metadata":{"unresolvedName":"path.split(\"/\").slice(0, -1).join"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:FileBrowser","to":"external:path.split(\"/\").slice","kind":"calls","metadata":{"unresolvedName":"path.split(\"/\").slice"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:FileBrowser","to":"external:path.split","kind":"calls","metadata":{"unresolvedName":"path.split"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:FileBrowser","to":"external:newParams.delete","kind":"calls","metadata":{"unresolvedName":"newParams.delete"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:FileBrowser","to":"external:gitStatus.modified.includes","kind":"calls","metadata":{"unresolvedName":"gitStatus.modified.includes"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:FileBrowser","to":"external:gitStatus.added.includes","kind":"calls","metadata":{"unresolvedName":"gitStatus.added.includes"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:FileBrowser","to":"external:gitStatus.deleted.includes","kind":"calls","metadata":{"unresolvedName":"gitStatus.deleted.includes"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:FileBrowser","to":"external:gitStatus.untracked.includes","kind":"calls","metadata":{"unresolvedName":"gitStatus.untracked.includes"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:FileBrowser","to":"external:entries.map","kind":"calls","metadata":{"unresolvedName":"entries.map"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:FileBrowser","to":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:getFileStatus","kind":"calls","metadata":{"unresolvedName":"getFileStatus"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:FileBrowser","to":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:handleEntryClick","kind":"calls","metadata":{"unresolvedName":"handleEntryClick"}},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.tsx","to":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.tsx","to":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:","to":"symbol-name:setEntries","kind":"calls","metadata":{"unresolvedName":"setEntries"}},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.tsx","to":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.tsx","to":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:","to":"symbol-name:loadFiles","kind":"calls","metadata":{"unresolvedName":"loadFiles"}},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.tsx","to":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.tsx","to":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:","to":"symbol-name:loadFiles","kind":"calls","metadata":{"unresolvedName":"loadFiles"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:","to":"external:window.addEventListener","kind":"calls","metadata":{"unresolvedName":"window.addEventListener"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:","to":"external:window.removeEventListener","kind":"calls","metadata":{"unresolvedName":"window.removeEventListener"}},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.tsx","to":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:handleEntryClick","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.tsx","to":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:handleEntryClick","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:handleEntryClick","to":"external:newParams.set","kind":"calls","metadata":{"unresolvedName":"newParams.set"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:handleEntryClick","to":"symbol-name:setSearchParams","kind":"calls","metadata":{"unresolvedName":"setSearchParams"}},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.tsx","to":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:navigateUp","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.tsx","to":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:navigateUp","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:navigateUp","to":"external:path.split(\"/\").slice(0, -1).join","kind":"calls","metadata":{"unresolvedName":"path.split(\"/\").slice(0, -1).join"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:navigateUp","to":"external:path.split(\"/\").slice","kind":"calls","metadata":{"unresolvedName":"path.split(\"/\").slice"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:navigateUp","to":"external:path.split","kind":"calls","metadata":{"unresolvedName":"path.split"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:navigateUp","to":"external:newParams.set","kind":"calls","metadata":{"unresolvedName":"newParams.set"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:navigateUp","to":"external:newParams.delete","kind":"calls","metadata":{"unresolvedName":"newParams.delete"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:navigateUp","to":"symbol-name:setSearchParams","kind":"calls","metadata":{"unresolvedName":"setSearchParams"}},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.tsx","to":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:getFileStatus","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.tsx","to":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:getFileStatus","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:getFileStatus","to":"external:gitStatus.modified.includes","kind":"calls","metadata":{"unresolvedName":"gitStatus.modified.includes"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:getFileStatus","to":"external:gitStatus.added.includes","kind":"calls","metadata":{"unresolvedName":"gitStatus.added.includes"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:getFileStatus","to":"external:gitStatus.deleted.includes","kind":"calls","metadata":{"unresolvedName":"gitStatus.deleted.includes"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:getFileStatus","to":"external:gitStatus.untracked.includes","kind":"calls","metadata":{"unresolvedName":"gitStatus.untracked.includes"}},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.tsx","to":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/FileBrowser.tsx","to":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:","to":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:getFileStatus","kind":"calls","metadata":{"unresolvedName":"getFileStatus"}},{"from":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:","to":"/workspace/apps/web/src/components/features/git/FileBrowser.tsx:handleEntryClick","kind":"calls","metadata":{"unresolvedName":"handleEntryClick"}},{"from":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","to":"file:/workspace/apps/web/src/components/features/git/FileEditor.module.css","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","to":"file:/workspace/apps/web/src/api/client.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","to":"file:/workspace/apps/web/src/state/auth.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","to":"file:/workspace/apps/web/src/components/ui/CodeEditor.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","to":"file:/workspace/apps/web/src/components/features/git/CommitDialog.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","to":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","to":"file:/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","to":"file:/workspace/apps/web/src/utils/language.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","to":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","to":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"symbol-name:useSearchParams","kind":"calls","metadata":{"unresolvedName":"useSearchParams"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"/workspace/apps/web/src/state/auth.tsx:useAuth","kind":"calls","metadata":{"unresolvedName":"useAuth"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"symbol-name:setIsBinary","kind":"calls","metadata":{"unresolvedName":"setIsBinary"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"symbol-name:setContent","kind":"calls","metadata":{"unresolvedName":"setContent"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"symbol-name:setOriginalContent","kind":"calls","metadata":{"unresolvedName":"setOriginalContent"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"symbol-name:setMode","kind":"calls","metadata":{"unresolvedName":"setMode"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"external:searchParams.get","kind":"calls","metadata":{"unresolvedName":"searchParams.get"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"external:gitStatus.modified.includes","kind":"calls","metadata":{"unresolvedName":"gitStatus.modified.includes"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"external:gitStatus.added.includes","kind":"calls","metadata":{"unresolvedName":"gitStatus.added.includes"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"external:gitStatus.deleted.includes","kind":"calls","metadata":{"unresolvedName":"gitStatus.deleted.includes"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"external:gitStatus.untracked.includes","kind":"calls","metadata":{"unresolvedName":"gitStatus.untracked.includes"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"symbol-name:setLanguage","kind":"calls","metadata":{"unresolvedName":"setLanguage"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"/workspace/apps/web/src/utils/language.ts:detectLanguage","kind":"calls","metadata":{"unresolvedName":"detectLanguage"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"symbol-name:loadFile","kind":"calls","metadata":{"unresolvedName":"loadFile"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"symbol-name:setShowCommitDialog","kind":"calls","metadata":{"unresolvedName":"setShowCommitDialog"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"symbol-name:setSaving","kind":"calls","metadata":{"unresolvedName":"setSaving"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"symbol-name:handleEdit","kind":"calls","metadata":{"unresolvedName":"handleEdit"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleCancel","kind":"calls","metadata":{"unresolvedName":"handleCancel"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"symbol-name:handleSave","kind":"calls","metadata":{"unresolvedName":"handleSave"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"external:window.addEventListener","kind":"calls","metadata":{"unresolvedName":"window.addEventListener"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"external:window.removeEventListener","kind":"calls","metadata":{"unresolvedName":"window.removeEventListener"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"external:filePath?.split(\"/\").map","kind":"calls","metadata":{"unresolvedName":"filePath?.split(\"/\").map"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:FileEditor","to":"external:filePath?.split","kind":"calls","metadata":{"unresolvedName":"filePath?.split"}},{"from":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","to":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleDiscard","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","to":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleDiscard","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleDiscard","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleDiscard","to":"symbol-name:setIsBinary","kind":"calls","metadata":{"unresolvedName":"setIsBinary"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleDiscard","to":"symbol-name:setContent","kind":"calls","metadata":{"unresolvedName":"setContent"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleDiscard","to":"symbol-name:setOriginalContent","kind":"calls","metadata":{"unresolvedName":"setOriginalContent"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleDiscard","to":"symbol-name:setMode","kind":"calls","metadata":{"unresolvedName":"setMode"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleDiscard","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","to":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","to":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:","to":"symbol-name:setContent","kind":"calls","metadata":{"unresolvedName":"setContent"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:","to":"symbol-name:setOriginalContent","kind":"calls","metadata":{"unresolvedName":"setOriginalContent"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:","to":"symbol-name:setIsBinary","kind":"calls","metadata":{"unresolvedName":"setIsBinary"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:","to":"symbol-name:setLanguage","kind":"calls","metadata":{"unresolvedName":"setLanguage"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:","to":"/workspace/apps/web/src/utils/language.ts:detectLanguage","kind":"calls","metadata":{"unresolvedName":"detectLanguage"}},{"from":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","to":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","to":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:","to":"symbol-name:loadFile","kind":"calls","metadata":{"unresolvedName":"loadFile"}},{"from":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","to":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleEdit","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","to":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleEdit","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleEdit","to":"symbol-name:setMode","kind":"calls","metadata":{"unresolvedName":"setMode"}},{"from":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","to":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleCancel","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","to":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleCancel","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleCancel","to":"symbol-name:setContent","kind":"calls","metadata":{"unresolvedName":"setContent"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleCancel","to":"symbol-name:setMode","kind":"calls","metadata":{"unresolvedName":"setMode"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleCancel","to":"symbol-name:setShowCommitDialog","kind":"calls","metadata":{"unresolvedName":"setShowCommitDialog"}},{"from":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","to":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleSave","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","to":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleSave","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleSave","to":"symbol-name:setMode","kind":"calls","metadata":{"unresolvedName":"setMode"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleSave","to":"symbol-name:setShowCommitDialog","kind":"calls","metadata":{"unresolvedName":"setShowCommitDialog"}},{"from":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","to":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleCommit","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","to":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleCommit","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleCommit","to":"symbol-name:setSaving","kind":"calls","metadata":{"unresolvedName":"setSaving"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleCommit","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleCommit","to":"symbol-name:setOriginalContent","kind":"calls","metadata":{"unresolvedName":"setOriginalContent"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleCommit","to":"symbol-name:setMode","kind":"calls","metadata":{"unresolvedName":"setMode"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleCommit","to":"symbol-name:setShowCommitDialog","kind":"calls","metadata":{"unresolvedName":"setShowCommitDialog"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleCommit","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","to":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","to":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:","to":"symbol-name:handleEdit","kind":"calls","metadata":{"unresolvedName":"handleEdit"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:","to":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleCancel","kind":"calls","metadata":{"unresolvedName":"handleCancel"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:","to":"symbol-name:handleSave","kind":"calls","metadata":{"unresolvedName":"handleSave"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:","to":"external:window.addEventListener","kind":"calls","metadata":{"unresolvedName":"window.addEventListener"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:","to":"external:window.removeEventListener","kind":"calls","metadata":{"unresolvedName":"window.removeEventListener"}},{"from":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","to":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleKeyDown","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","to":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleKeyDown","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleKeyDown","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleKeyDown","to":"symbol-name:handleEdit","kind":"calls","metadata":{"unresolvedName":"handleEdit"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleKeyDown","to":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleCancel","kind":"calls","metadata":{"unresolvedName":"handleCancel"}},{"from":"/workspace/apps/web/src/components/features/git/FileEditor.tsx:handleKeyDown","to":"symbol-name:handleSave","kind":"calls","metadata":{"unresolvedName":"handleSave"}},{"from":"file:/workspace/apps/web/src/components/features/git/GitToolbar.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/GitToolbar.tsx","to":"file:/workspace/apps/web/src/api/git-repositories.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/GitToolbar.tsx","to":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/GitToolbar.tsx","to":"file:/workspace/apps/web/src/components/features/git/MergeDialog.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/GitToolbar.tsx","to":"file:/workspace/apps/web/src/components/features/git/GitToolbar.module.css","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/GitToolbar.tsx","to":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:GitToolbar","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/GitToolbar.tsx","to":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:GitToolbar","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:GitToolbar","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:GitToolbar","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:GitToolbar","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:GitToolbar","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:GitToolbar","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:GitToolbar","to":"symbol-name:loadStatus","kind":"calls","metadata":{"unresolvedName":"loadStatus"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:GitToolbar","to":"symbol-name:setInterval","kind":"calls","metadata":{"unresolvedName":"setInterval"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:GitToolbar","to":"symbol-name:clearInterval","kind":"calls","metadata":{"unresolvedName":"clearInterval"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:GitToolbar","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:GitToolbar","to":"symbol-name:onRefresh","kind":"calls","metadata":{"unresolvedName":"onRefresh"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:GitToolbar","to":"symbol-name:onBranchChange","kind":"calls","metadata":{"unresolvedName":"onBranchChange"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:GitToolbar","to":"external:newBranchName.trim","kind":"calls","metadata":{"unresolvedName":"newBranchName.trim"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:GitToolbar","to":"symbol-name:setShowNewBranch","kind":"calls","metadata":{"unresolvedName":"setShowNewBranch"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:GitToolbar","to":"symbol-name:setNewBranchName","kind":"calls","metadata":{"unresolvedName":"setNewBranchName"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:GitToolbar","to":"symbol-name:setNewBranchBase","kind":"calls","metadata":{"unresolvedName":"setNewBranchBase"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:GitToolbar","to":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handleCheckout","kind":"calls","metadata":{"unresolvedName":"handleCheckout"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:GitToolbar","to":"external:branches.map","kind":"calls","metadata":{"unresolvedName":"branches.map"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:GitToolbar","to":"symbol-name:setShowMergeDialog","kind":"calls","metadata":{"unresolvedName":"setShowMergeDialog"}},{"from":"file:/workspace/apps/web/src/components/features/git/GitToolbar.tsx","to":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/GitToolbar.tsx","to":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"file:/workspace/apps/web/src/components/features/git/GitToolbar.tsx","to":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/GitToolbar.tsx","to":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:","to":"symbol-name:loadStatus","kind":"calls","metadata":{"unresolvedName":"loadStatus"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:","to":"symbol-name:setInterval","kind":"calls","metadata":{"unresolvedName":"setInterval"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:","to":"symbol-name:clearInterval","kind":"calls","metadata":{"unresolvedName":"clearInterval"}},{"from":"file:/workspace/apps/web/src/components/features/git/GitToolbar.tsx","to":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handleFetch","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/GitToolbar.tsx","to":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handleFetch","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handleFetch","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handleFetch","to":"symbol-name:loadStatus","kind":"calls","metadata":{"unresolvedName":"loadStatus"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handleFetch","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"file:/workspace/apps/web/src/components/features/git/GitToolbar.tsx","to":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handlePull","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/GitToolbar.tsx","to":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handlePull","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handlePull","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handlePull","to":"symbol-name:loadStatus","kind":"calls","metadata":{"unresolvedName":"loadStatus"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handlePull","to":"symbol-name:onRefresh","kind":"calls","metadata":{"unresolvedName":"onRefresh"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handlePull","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"file:/workspace/apps/web/src/components/features/git/GitToolbar.tsx","to":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handlePush","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/GitToolbar.tsx","to":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handlePush","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handlePush","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handlePush","to":"symbol-name:loadStatus","kind":"calls","metadata":{"unresolvedName":"loadStatus"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handlePush","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"file:/workspace/apps/web/src/components/features/git/GitToolbar.tsx","to":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handleCheckout","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/GitToolbar.tsx","to":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handleCheckout","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handleCheckout","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handleCheckout","to":"symbol-name:onBranchChange","kind":"calls","metadata":{"unresolvedName":"onBranchChange"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handleCheckout","to":"symbol-name:onRefresh","kind":"calls","metadata":{"unresolvedName":"onRefresh"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handleCheckout","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"file:/workspace/apps/web/src/components/features/git/GitToolbar.tsx","to":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handleCreateBranch","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/GitToolbar.tsx","to":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handleCreateBranch","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handleCreateBranch","to":"external:newBranchName.trim","kind":"calls","metadata":{"unresolvedName":"newBranchName.trim"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handleCreateBranch","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handleCreateBranch","to":"symbol-name:setShowNewBranch","kind":"calls","metadata":{"unresolvedName":"setShowNewBranch"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handleCreateBranch","to":"symbol-name:setNewBranchName","kind":"calls","metadata":{"unresolvedName":"setNewBranchName"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handleCreateBranch","to":"symbol-name:setNewBranchBase","kind":"calls","metadata":{"unresolvedName":"setNewBranchBase"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handleCreateBranch","to":"symbol-name:onRefresh","kind":"calls","metadata":{"unresolvedName":"onRefresh"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handleCreateBranch","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"file:/workspace/apps/web/src/components/features/git/GitToolbar.tsx","to":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/GitToolbar.tsx","to":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:","to":"symbol-name:loadStatus","kind":"calls","metadata":{"unresolvedName":"loadStatus"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:","to":"symbol-name:onRefresh","kind":"calls","metadata":{"unresolvedName":"onRefresh"}},{"from":"file:/workspace/apps/web/src/components/features/git/MergeDialog.tsx","to":"file:/workspace/apps/web/src/components/features/git/MergeDialog.module.css","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/MergeDialog.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/MergeDialog.tsx","to":"file:/workspace/apps/web/src/api/git-repositories.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/MergeDialog.tsx","to":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/MergeDialog.tsx","to":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:MergeDialog","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/MergeDialog.tsx","to":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:MergeDialog","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:MergeDialog","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:MergeDialog","to":"external:branches.filter","kind":"calls","metadata":{"unresolvedName":"branches.filter"}},{"from":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:MergeDialog","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:MergeDialog","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:MergeDialog","to":"symbol-name:setSuccess","kind":"calls","metadata":{"unresolvedName":"setSuccess"}},{"from":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:MergeDialog","to":"symbol-name:setTimeout","kind":"calls","metadata":{"unresolvedName":"setTimeout"}},{"from":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:MergeDialog","to":"symbol-name:onMerge","kind":"calls","metadata":{"unresolvedName":"onMerge"}},{"from":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:MergeDialog","to":"symbol-name:onClose","kind":"calls","metadata":{"unresolvedName":"onClose"}},{"from":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:MergeDialog","to":"external:e.stopPropagation","kind":"calls","metadata":{"unresolvedName":"e.stopPropagation"}},{"from":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:MergeDialog","to":"symbol-name:setSourceBranch","kind":"calls","metadata":{"unresolvedName":"setSourceBranch"}},{"from":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:MergeDialog","to":"external:availableBranches.map","kind":"calls","metadata":{"unresolvedName":"availableBranches.map"}},{"from":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:MergeDialog","to":"symbol-name:setCommitMessage","kind":"calls","metadata":{"unresolvedName":"setCommitMessage"}},{"from":"file:/workspace/apps/web/src/components/features/git/MergeDialog.tsx","to":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:handleMerge","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/MergeDialog.tsx","to":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:handleMerge","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:handleMerge","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:handleMerge","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:handleMerge","to":"symbol-name:setSuccess","kind":"calls","metadata":{"unresolvedName":"setSuccess"}},{"from":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:handleMerge","to":"symbol-name:setTimeout","kind":"calls","metadata":{"unresolvedName":"setTimeout"}},{"from":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:handleMerge","to":"symbol-name:onMerge","kind":"calls","metadata":{"unresolvedName":"onMerge"}},{"from":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:handleMerge","to":"symbol-name:onClose","kind":"calls","metadata":{"unresolvedName":"onClose"}},{"from":"file:/workspace/apps/web/src/components/features/git/MergeDialog.tsx","to":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/MergeDialog.tsx","to":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:","to":"symbol-name:onMerge","kind":"calls","metadata":{"unresolvedName":"onMerge"}},{"from":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:","to":"symbol-name:onClose","kind":"calls","metadata":{"unresolvedName":"onClose"}},{"from":"file:/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx","to":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx","to":"file:/workspace/apps/web/src/utils/language.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx","to":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:SyntaxHighlighter","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx","to":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:SyntaxHighlighter","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:SyntaxHighlighter","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:SyntaxHighlighter","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:SyntaxHighlighter","to":"/workspace/apps/web/src/utils/language.ts:loadLanguage","kind":"calls","metadata":{"unresolvedName":"loadLanguage"}},{"from":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:SyntaxHighlighter","to":"symbol-name:setHighlighted","kind":"calls","metadata":{"unresolvedName":"setHighlighted"}},{"from":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:SyntaxHighlighter","to":"/workspace/apps/web/src/utils/language.ts:highlightCode","kind":"calls","metadata":{"unresolvedName":"highlightCode"}},{"from":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:SyntaxHighlighter","to":"symbol-name:highlight","kind":"calls","metadata":{"unresolvedName":"highlight"}},{"from":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:SyntaxHighlighter","to":"external:navigator.clipboard.writeText","kind":"calls","metadata":{"unresolvedName":"navigator.clipboard.writeText"}},{"from":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:SyntaxHighlighter","to":"symbol-name:setCopied","kind":"calls","metadata":{"unresolvedName":"setCopied"}},{"from":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:SyntaxHighlighter","to":"symbol-name:setTimeout","kind":"calls","metadata":{"unresolvedName":"setTimeout"}},{"from":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:SyntaxHighlighter","to":"external:code.split","kind":"calls","metadata":{"unresolvedName":"code.split"}},{"from":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:SyntaxHighlighter","to":"external:lines.map","kind":"calls","metadata":{"unresolvedName":"lines.map"}},{"from":"file:/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx","to":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx","to":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:","to":"/workspace/apps/web/src/utils/language.ts:loadLanguage","kind":"calls","metadata":{"unresolvedName":"loadLanguage"}},{"from":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:","to":"symbol-name:setHighlighted","kind":"calls","metadata":{"unresolvedName":"setHighlighted"}},{"from":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:","to":"/workspace/apps/web/src/utils/language.ts:highlightCode","kind":"calls","metadata":{"unresolvedName":"highlightCode"}},{"from":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:","to":"symbol-name:highlight","kind":"calls","metadata":{"unresolvedName":"highlight"}},{"from":"file:/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx","to":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:highlight","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx","to":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:highlight","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:highlight","to":"/workspace/apps/web/src/utils/language.ts:loadLanguage","kind":"calls","metadata":{"unresolvedName":"loadLanguage"}},{"from":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:highlight","to":"symbol-name:setHighlighted","kind":"calls","metadata":{"unresolvedName":"setHighlighted"}},{"from":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:highlight","to":"/workspace/apps/web/src/utils/language.ts:highlightCode","kind":"calls","metadata":{"unresolvedName":"highlightCode"}},{"from":"file:/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx","to":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:handleCopy","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx","to":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:handleCopy","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:handleCopy","to":"external:navigator.clipboard.writeText","kind":"calls","metadata":{"unresolvedName":"navigator.clipboard.writeText"}},{"from":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:handleCopy","to":"symbol-name:setCopied","kind":"calls","metadata":{"unresolvedName":"setCopied"}},{"from":"/workspace/apps/web/src/components/features/git/SyntaxHighlighter.tsx:handleCopy","to":"symbol-name:setTimeout","kind":"calls","metadata":{"unresolvedName":"setTimeout"}},{"from":"file:/workspace/apps/web/src/components/features/git/WorkspaceSidebar.tsx","to":"file:/workspace/apps/web/src/types/git-repository.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/WorkspaceSidebar.tsx","to":"file:/workspace/apps/web/src/api/git-repositories.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/WorkspaceSidebar.tsx","to":"file:/workspace/apps/web/src/types/tool-type.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/WorkspaceSidebar.tsx","to":"file:/workspace/apps/web/src/components/features/git/FileBrowser.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/WorkspaceSidebar.tsx","to":"file:/workspace/apps/web/src/components/features/git/CommitPanel.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/WorkspaceSidebar.tsx","to":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/git/WorkspaceSidebar.tsx","to":"/workspace/apps/web/src/components/features/git/WorkspaceSidebar.tsx:WorkspaceSidebar","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/git/WorkspaceSidebar.tsx","to":"/workspace/apps/web/src/components/features/git/WorkspaceSidebar.tsx:WorkspaceSidebar","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/git/WorkspaceSidebar.tsx:WorkspaceSidebar","to":"symbol-name:onRepoChange","kind":"calls","metadata":{"unresolvedName":"onRepoChange"}},{"from":"/workspace/apps/web/src/components/features/git/WorkspaceSidebar.tsx:WorkspaceSidebar","to":"external:repositories.map","kind":"calls","metadata":{"unresolvedName":"repositories.map"}},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx","to":"external:@testing-library/react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx","to":"external:vitest","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx","to":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx","to":"file:/workspace/apps/web/src/api/git-repositories.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:vi.importActual","kind":"calls","metadata":{"unresolvedName":"vi.importActual"}},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"symbol-name:cleanup","kind":"calls","metadata":{"unresolvedName":"cleanup"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:vi.restoreAllMocks","kind":"calls","metadata":{"unresolvedName":"vi.restoreAllMocks"}},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:vi.spyOn(gitRepositoriesApi, \"listRepositories\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(gitRepositoriesApi, \"listRepositories\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:vi.spyOn","kind":"calls","metadata":{"unresolvedName":"vi.spyOn"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:vi.spyOn(gitRepositoriesApi, \"createRepository\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(gitRepositoriesApi, \"createRepository\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:expect(screen.getByText(\"Main Repo\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Main Repo\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:fireEvent.change","kind":"calls","metadata":{"unresolvedName":"fireEvent.change"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:screen.getByPlaceholderText","kind":"calls","metadata":{"unresolvedName":"screen.getByPlaceholderText"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:expect(createMock).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(createMock).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:expect(listMock).toHaveBeenCalledTimes","kind":"calls","metadata":{"unresolvedName":"expect(listMock).toHaveBeenCalledTimes"}},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:vi.spyOn(gitRepositoriesApi, \"listRepositories\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(gitRepositoriesApi, \"listRepositories\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:vi.spyOn","kind":"calls","metadata":{"unresolvedName":"vi.spyOn"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:vi.spyOn(gitRepositoriesApi, \"createRepository\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(gitRepositoriesApi, \"createRepository\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:expect(screen.getByText(\"Main Repo\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Main Repo\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:fireEvent.change","kind":"calls","metadata":{"unresolvedName":"fireEvent.change"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:screen.getByPlaceholderText","kind":"calls","metadata":{"unresolvedName":"screen.getByPlaceholderText"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:expect(createMock).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(createMock).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:expect(listMock).toHaveBeenCalledTimes","kind":"calls","metadata":{"unresolvedName":"expect(listMock).toHaveBeenCalledTimes"}},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:expect(screen.getByText(\"Main Repo\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Main Repo\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:expect(createMock).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(createMock).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:vi.spyOn(gitRepositoriesApi, \"listRepositories\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(gitRepositoriesApi, \"listRepositories\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:vi.spyOn","kind":"calls","metadata":{"unresolvedName":"vi.spyOn"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:vi.spyOn(gitRepositoriesApi, \"createRepository\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(gitRepositoriesApi, \"createRepository\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:expect(screen.getByText(\"Main Repo\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Main Repo\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:fireEvent.change","kind":"calls","metadata":{"unresolvedName":"fireEvent.change"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:screen.getByPlaceholderText","kind":"calls","metadata":{"unresolvedName":"screen.getByPlaceholderText"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:expect(createMock).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(createMock).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:expect(listMock).toHaveBeenCalledTimes","kind":"calls","metadata":{"unresolvedName":"expect(listMock).toHaveBeenCalledTimes"}},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:expect(screen.getByText(\"Main Repo\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Main Repo\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:expect(createMock).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(createMock).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:vi.spyOn(gitRepositoriesApi, \"listRepositories\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(gitRepositoriesApi, \"listRepositories\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:vi.spyOn","kind":"calls","metadata":{"unresolvedName":"vi.spyOn"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:expect(screen.getByText(\"Main Repo\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Main Repo\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:fireEvent.change","kind":"calls","metadata":{"unresolvedName":"fireEvent.change"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:screen.getByPlaceholderText","kind":"calls","metadata":{"unresolvedName":"screen.getByPlaceholderText"}},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:expect(screen.getByText(\"Main Repo\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Main Repo\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx","to":"file:/workspace/apps/web/src/types/git-repository.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx","to":"file:/workspace/apps/web/src/api/git-repositories.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx","to":"file:/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx","to":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:RepositoriesSettingsTab","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:RepositoriesSettingsTab","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:RepositoriesSettingsTab","to":"symbol-name:useParams","kind":"calls","metadata":{"unresolvedName":"useParams"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:RepositoriesSettingsTab","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:RepositoriesSettingsTab","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:RepositoriesSettingsTab","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:RepositoriesSettingsTab","to":"symbol-name:setRepositories","kind":"calls","metadata":{"unresolvedName":"setRepositories"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:RepositoriesSettingsTab","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:RepositoriesSettingsTab","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:RepositoriesSettingsTab","to":"symbol-name:loadRepositories","kind":"calls","metadata":{"unresolvedName":"loadRepositories"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:RepositoriesSettingsTab","to":"external:window.confirm","kind":"calls","metadata":{"unresolvedName":"window.confirm"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:RepositoriesSettingsTab","to":"external:current.filter","kind":"calls","metadata":{"unresolvedName":"current.filter"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:RepositoriesSettingsTab","to":"symbol-name:setShowCreate","kind":"calls","metadata":{"unresolvedName":"setShowCreate"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:RepositoriesSettingsTab","to":"external:repositories.map","kind":"calls","metadata":{"unresolvedName":"repositories.map"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:RepositoriesSettingsTab","to":"symbol-name:handleDelete","kind":"calls","metadata":{"unresolvedName":"handleDelete"}},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:","to":"symbol-name:setRepositories","kind":"calls","metadata":{"unresolvedName":"setRepositories"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:","to":"symbol-name:loadRepositories","kind":"calls","metadata":{"unresolvedName":"loadRepositories"}},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:handleDelete","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:handleDelete","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:handleDelete","to":"external:window.confirm","kind":"calls","metadata":{"unresolvedName":"window.confirm"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:handleDelete","to":"symbol-name:setRepositories","kind":"calls","metadata":{"unresolvedName":"setRepositories"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:handleDelete","to":"external:current.filter","kind":"calls","metadata":{"unresolvedName":"current.filter"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:handleDelete","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","to":"file:/workspace/apps/web/src/types/git-repository.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","to":"file:/workspace/apps/web/src/api/git-repositories.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","to":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:RepositoryCreateDialog","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:RepositoryCreateDialog","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:RepositoryCreateDialog","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:RepositoryCreateDialog","to":"symbol-name:useRef","kind":"calls","metadata":{"unresolvedName":"useRef"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:RepositoryCreateDialog","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:RepositoryCreateDialog","to":"symbol-name:clearTimeout","kind":"calls","metadata":{"unresolvedName":"clearTimeout"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:RepositoryCreateDialog","to":"symbol-name:setUrlValidation","kind":"calls","metadata":{"unresolvedName":"setUrlValidation"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:RepositoryCreateDialog","to":"external:advancedUrl.trim","kind":"calls","metadata":{"unresolvedName":"advancedUrl.trim"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:RepositoryCreateDialog","to":"symbol-name:setTimeout","kind":"calls","metadata":{"unresolvedName":"setTimeout"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:RepositoryCreateDialog","to":"symbol-name:setCreateMode","kind":"calls","metadata":{"unresolvedName":"setCreateMode"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:RepositoryCreateDialog","to":"symbol-name:setFormName","kind":"calls","metadata":{"unresolvedName":"setFormName"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:RepositoryCreateDialog","to":"symbol-name:setOwner","kind":"calls","metadata":{"unresolvedName":"setOwner"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:RepositoryCreateDialog","to":"symbol-name:setRepoName","kind":"calls","metadata":{"unresolvedName":"setRepoName"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:RepositoryCreateDialog","to":"symbol-name:setAdvancedUrl","kind":"calls","metadata":{"unresolvedName":"setAdvancedUrl"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:RepositoryCreateDialog","to":"symbol-name:setUseAdvancedUrl","kind":"calls","metadata":{"unresolvedName":"setUseAdvancedUrl"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:RepositoryCreateDialog","to":"symbol-name:setFormError","kind":"calls","metadata":{"unresolvedName":"setFormError"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:RepositoryCreateDialog","to":"symbol-name:resetForm","kind":"calls","metadata":{"unresolvedName":"resetForm"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:RepositoryCreateDialog","to":"symbol-name:onClose","kind":"calls","metadata":{"unresolvedName":"onClose"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:RepositoryCreateDialog","to":"external:event.preventDefault","kind":"calls","metadata":{"unresolvedName":"event.preventDefault"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:RepositoryCreateDialog","to":"external:formName.trim","kind":"calls","metadata":{"unresolvedName":"formName.trim"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:RepositoryCreateDialog","to":"external:owner.trim","kind":"calls","metadata":{"unresolvedName":"owner.trim"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:RepositoryCreateDialog","to":"external:repoName.trim","kind":"calls","metadata":{"unresolvedName":"repoName.trim"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:RepositoryCreateDialog","to":"symbol-name:handleClose","kind":"calls","metadata":{"unresolvedName":"handleClose"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:RepositoryCreateDialog","to":"symbol-name:onCreated","kind":"calls","metadata":{"unresolvedName":"onCreated"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:RepositoryCreateDialog","to":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:getUrlInputClass","kind":"calls","metadata":{"unresolvedName":"getUrlInputClass"}},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:","to":"symbol-name:clearTimeout","kind":"calls","metadata":{"unresolvedName":"clearTimeout"}},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:","to":"symbol-name:setUrlValidation","kind":"calls","metadata":{"unresolvedName":"setUrlValidation"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:","to":"symbol-name:clearTimeout","kind":"calls","metadata":{"unresolvedName":"clearTimeout"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:","to":"external:advancedUrl.trim","kind":"calls","metadata":{"unresolvedName":"advancedUrl.trim"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:","to":"symbol-name:setTimeout","kind":"calls","metadata":{"unresolvedName":"setTimeout"}},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:","to":"external:advancedUrl.trim","kind":"calls","metadata":{"unresolvedName":"advancedUrl.trim"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:","to":"symbol-name:setUrlValidation","kind":"calls","metadata":{"unresolvedName":"setUrlValidation"}},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:","to":"symbol-name:clearTimeout","kind":"calls","metadata":{"unresolvedName":"clearTimeout"}},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:resetForm","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:resetForm","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:resetForm","to":"symbol-name:setCreateMode","kind":"calls","metadata":{"unresolvedName":"setCreateMode"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:resetForm","to":"symbol-name:setFormName","kind":"calls","metadata":{"unresolvedName":"setFormName"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:resetForm","to":"symbol-name:setOwner","kind":"calls","metadata":{"unresolvedName":"setOwner"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:resetForm","to":"symbol-name:setRepoName","kind":"calls","metadata":{"unresolvedName":"setRepoName"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:resetForm","to":"symbol-name:setAdvancedUrl","kind":"calls","metadata":{"unresolvedName":"setAdvancedUrl"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:resetForm","to":"symbol-name:setUseAdvancedUrl","kind":"calls","metadata":{"unresolvedName":"setUseAdvancedUrl"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:resetForm","to":"symbol-name:setFormError","kind":"calls","metadata":{"unresolvedName":"setFormError"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:resetForm","to":"symbol-name:setUrlValidation","kind":"calls","metadata":{"unresolvedName":"setUrlValidation"}},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:handleClose","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:handleClose","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:handleClose","to":"symbol-name:resetForm","kind":"calls","metadata":{"unresolvedName":"resetForm"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:handleClose","to":"symbol-name:onClose","kind":"calls","metadata":{"unresolvedName":"onClose"}},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:handleSubmit","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:handleSubmit","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:handleSubmit","to":"external:event.preventDefault","kind":"calls","metadata":{"unresolvedName":"event.preventDefault"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:handleSubmit","to":"symbol-name:setFormError","kind":"calls","metadata":{"unresolvedName":"setFormError"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:handleSubmit","to":"external:formName.trim","kind":"calls","metadata":{"unresolvedName":"formName.trim"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:handleSubmit","to":"external:advancedUrl.trim","kind":"calls","metadata":{"unresolvedName":"advancedUrl.trim"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:handleSubmit","to":"external:owner.trim","kind":"calls","metadata":{"unresolvedName":"owner.trim"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:handleSubmit","to":"external:repoName.trim","kind":"calls","metadata":{"unresolvedName":"repoName.trim"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:handleSubmit","to":"symbol-name:handleClose","kind":"calls","metadata":{"unresolvedName":"handleClose"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:handleSubmit","to":"symbol-name:onCreated","kind":"calls","metadata":{"unresolvedName":"onCreated"}},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:handleUseSuggestedUrl","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:handleUseSuggestedUrl","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:handleUseSuggestedUrl","to":"symbol-name:setAdvancedUrl","kind":"calls","metadata":{"unresolvedName":"setAdvancedUrl"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:handleUseSuggestedUrl","to":"symbol-name:setUrlValidation","kind":"calls","metadata":{"unresolvedName":"setUrlValidation"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:handleUseSuggestedUrl","to":"symbol-name:setFormError","kind":"calls","metadata":{"unresolvedName":"setFormError"}},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:getUrlInputClass","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","to":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:getUrlInputClass","kind":"defines"},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx","to":"external:@testing-library/react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx","to":"external:vitest","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx","to":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx","to":"external:@/types/project","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx","to":"external:@/types/tool-type","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx","to":"external:@/api/git-repositories","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx","to":"external:@/api/sessions","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx","to":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx","to":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"external:expect(screen.getByText(\"Create New Session\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Create New Session\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"external:expect(screen.getByText(\"Create Session\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Create Session\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"external:container.querySelector","kind":"calls","metadata":{"unresolvedName":"container.querySelector"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"external:expect(createInstance).not.toHaveBeenCalled","kind":"calls","metadata":{"unresolvedName":"expect(createInstance).not.toHaveBeenCalled"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"external:mockedList.mockResolvedValueOnce","kind":"calls","metadata":{"unresolvedName":"mockedList.mockResolvedValueOnce"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"external:fireEvent.change","kind":"calls","metadata":{"unresolvedName":"fireEvent.change"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"external:expect(listRepositories).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(listRepositories).toHaveBeenCalledWith"}},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx","to":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx","to":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"external:expect(screen.getByText(\"Create New Session\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Create New Session\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"external:expect(screen.getByText(\"Create Session\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Create Session\")).toBeInTheDocument"}},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx","to":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx","to":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"external:container.querySelector","kind":"calls","metadata":{"unresolvedName":"container.querySelector"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"external:expect(createInstance).not.toHaveBeenCalled","kind":"calls","metadata":{"unresolvedName":"expect(createInstance).not.toHaveBeenCalled"}},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx","to":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx","to":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx","to":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx","to":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"external:mockedList.mockResolvedValueOnce","kind":"calls","metadata":{"unresolvedName":"mockedList.mockResolvedValueOnce"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"external:container.querySelector","kind":"calls","metadata":{"unresolvedName":"container.querySelector"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"external:fireEvent.change","kind":"calls","metadata":{"unresolvedName":"fireEvent.change"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"external:expect(listRepositories).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(listRepositories).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx","to":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx","to":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"external:expect(listRepositories).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(listRepositories).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx","to":"external:@/api/git-repositories","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx","to":"external:@/api/sessions","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx","to":"external:@/api/settings","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx","to":"external:@/components/ui/Icon","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx","to":"external:@/types/project","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx","to":"external:@/types/git-repository","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx","to":"external:@/types/tool-type","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx","to":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:CreateSessionForm","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx","to":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:CreateSessionForm","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:CreateSessionForm","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:CreateSessionForm","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:CreateSessionForm","to":"symbol-name:setRepositories","kind":"calls","metadata":{"unresolvedName":"setRepositories"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:CreateSessionForm","to":"symbol-name:loadRepos","kind":"calls","metadata":{"unresolvedName":"loadRepos"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:CreateSessionForm","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:CreateSessionForm","to":"symbol-name:setCreateError","kind":"calls","metadata":{"unresolvedName":"setCreateError"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:CreateSessionForm","to":"symbol-name:setCreateStatus","kind":"calls","metadata":{"unresolvedName":"setCreateStatus"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:CreateSessionForm","to":"/workspace/apps/web/src/api/settings.ts:updateUserConfig","kind":"calls","metadata":{"unresolvedName":"updateUserConfig"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:CreateSessionForm","to":"symbol-name:setSelectedProject","kind":"calls","metadata":{"unresolvedName":"setSelectedProject"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:CreateSessionForm","to":"symbol-name:setSelectedRepo","kind":"calls","metadata":{"unresolvedName":"setSelectedRepo"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:CreateSessionForm","to":"symbol-name:setSelectedToolType","kind":"calls","metadata":{"unresolvedName":"setSelectedToolType"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:CreateSessionForm","to":"symbol-name:setDisplayName","kind":"calls","metadata":{"unresolvedName":"setDisplayName"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:CreateSessionForm","to":"symbol-name:onCreated","kind":"calls","metadata":{"unresolvedName":"onCreated"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:CreateSessionForm","to":"external:projects.map","kind":"calls","metadata":{"unresolvedName":"projects.map"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:CreateSessionForm","to":"external:repositories.map","kind":"calls","metadata":{"unresolvedName":"repositories.map"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:CreateSessionForm","to":"external:toolTypes.map","kind":"calls","metadata":{"unresolvedName":"toolTypes.map"}},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx","to":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx","to":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:","to":"symbol-name:setRepositories","kind":"calls","metadata":{"unresolvedName":"setRepositories"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:","to":"symbol-name:loadRepos","kind":"calls","metadata":{"unresolvedName":"loadRepos"}},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx","to":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:loadRepos","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx","to":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:loadRepos","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:loadRepos","to":"symbol-name:setRepositories","kind":"calls","metadata":{"unresolvedName":"setRepositories"}},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx","to":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:handleCreate","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx","to":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:handleCreate","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:handleCreate","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:handleCreate","to":"symbol-name:setCreateError","kind":"calls","metadata":{"unresolvedName":"setCreateError"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:handleCreate","to":"symbol-name:setCreateStatus","kind":"calls","metadata":{"unresolvedName":"setCreateStatus"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:handleCreate","to":"/workspace/apps/web/src/api/settings.ts:updateUserConfig","kind":"calls","metadata":{"unresolvedName":"updateUserConfig"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:handleCreate","to":"symbol-name:setSelectedProject","kind":"calls","metadata":{"unresolvedName":"setSelectedProject"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:handleCreate","to":"symbol-name:setSelectedRepo","kind":"calls","metadata":{"unresolvedName":"setSelectedRepo"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:handleCreate","to":"symbol-name:setSelectedToolType","kind":"calls","metadata":{"unresolvedName":"setSelectedToolType"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:handleCreate","to":"symbol-name:setDisplayName","kind":"calls","metadata":{"unresolvedName":"setDisplayName"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:handleCreate","to":"symbol-name:onCreated","kind":"calls","metadata":{"unresolvedName":"onCreated"}},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx","to":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx","to":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:","to":"symbol-name:setSelectedProject","kind":"calls","metadata":{"unresolvedName":"setSelectedProject"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:","to":"symbol-name:setSelectedRepo","kind":"calls","metadata":{"unresolvedName":"setSelectedRepo"}},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"file:/workspace/apps/web/src/types/tool-instance.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"file:/workspace/apps/web/src/types/tool-type.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"file:/workspace/apps/web/src/api/sessions.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"file:/workspace/apps/web/src/components/features/session/InstanceList.module.css","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"symbol-name:useNavigate","kind":"calls","metadata":{"unresolvedName":"useNavigate"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"symbol-name:setInstances","kind":"calls","metadata":{"unresolvedName":"setInstances"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"symbol-name:loadInstances","kind":"calls","metadata":{"unresolvedName":"loadInstances"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"external:instances.filter","kind":"calls","metadata":{"unresolvedName":"instances.filter"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"external:i.url?.startsWith","kind":"calls","metadata":{"unresolvedName":"i.url?.startsWith"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"symbol-name:setHealthStatus","kind":"calls","metadata":{"unresolvedName":"setHealthStatus"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"external:Date.now","kind":"calls","metadata":{"unresolvedName":"Date.now"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"symbol-name:checkHealth","kind":"calls","metadata":{"unresolvedName":"checkHealth"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"symbol-name:setInterval","kind":"calls","metadata":{"unresolvedName":"setInterval"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"symbol-name:clearInterval","kind":"calls","metadata":{"unresolvedName":"clearInterval"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"symbol-name:setShowCreate","kind":"calls","metadata":{"unresolvedName":"setShowCreate"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"symbol-name:setSelectedToolType","kind":"calls","metadata":{"unresolvedName":"setSelectedToolType"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"symbol-name:setDisplayName","kind":"calls","metadata":{"unresolvedName":"setDisplayName"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"symbol-name:setStopConfirmId","kind":"calls","metadata":{"unresolvedName":"setStopConfirmId"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"symbol-name:confirm","kind":"calls","metadata":{"unresolvedName":"confirm"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"external:prev.filter","kind":"calls","metadata":{"unresolvedName":"prev.filter"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"external:instance.url?.startsWith","kind":"calls","metadata":{"unresolvedName":"instance.url?.startsWith"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"external:instances.map","kind":"calls","metadata":{"unresolvedName":"instances.map"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:getStatusColor","kind":"calls","metadata":{"unresolvedName":"getStatusColor"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:isTunnelUnhealthy","kind":"calls","metadata":{"unresolvedName":"isTunnelUnhealthy"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"external:instance.tool_type_interfaces.includes","kind":"calls","metadata":{"unresolvedName":"instance.tool_type_interfaces.includes"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"external:instance.url.startsWith","kind":"calls","metadata":{"unresolvedName":"instance.url.startsWith"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleRecreateTunnel","kind":"calls","metadata":{"unresolvedName":"handleRecreateTunnel"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"symbol-name:navigate","kind":"calls","metadata":{"unresolvedName":"navigate"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleStart","kind":"calls","metadata":{"unresolvedName":"handleStart"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"symbol-name:handleStop","kind":"calls","metadata":{"unresolvedName":"handleStop"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleRestart","kind":"calls","metadata":{"unresolvedName":"handleRestart"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"symbol-name:handleDelete","kind":"calls","metadata":{"unresolvedName":"handleDelete"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"external:toolTypes.map","kind":"calls","metadata":{"unresolvedName":"toolTypes.map"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"symbol-name:handleCreate","kind":"calls","metadata":{"unresolvedName":"handleCreate"}},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:","to":"symbol-name:setInstances","kind":"calls","metadata":{"unresolvedName":"setInstances"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:","to":"symbol-name:loadInstances","kind":"calls","metadata":{"unresolvedName":"loadInstances"}},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:","to":"external:instances.filter","kind":"calls","metadata":{"unresolvedName":"instances.filter"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:","to":"external:i.url?.startsWith","kind":"calls","metadata":{"unresolvedName":"i.url?.startsWith"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:","to":"symbol-name:setHealthStatus","kind":"calls","metadata":{"unresolvedName":"setHealthStatus"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:","to":"external:Date.now","kind":"calls","metadata":{"unresolvedName":"Date.now"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:","to":"symbol-name:checkHealth","kind":"calls","metadata":{"unresolvedName":"checkHealth"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:","to":"symbol-name:setInterval","kind":"calls","metadata":{"unresolvedName":"setInterval"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:","to":"symbol-name:clearInterval","kind":"calls","metadata":{"unresolvedName":"clearInterval"}},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:checkHealth","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:checkHealth","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:checkHealth","to":"symbol-name:setHealthStatus","kind":"calls","metadata":{"unresolvedName":"setHealthStatus"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:checkHealth","to":"external:Date.now","kind":"calls","metadata":{"unresolvedName":"Date.now"}},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleCreate","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleCreate","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleCreate","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleCreate","to":"symbol-name:setShowCreate","kind":"calls","metadata":{"unresolvedName":"setShowCreate"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleCreate","to":"symbol-name:setSelectedToolType","kind":"calls","metadata":{"unresolvedName":"setSelectedToolType"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleCreate","to":"symbol-name:setDisplayName","kind":"calls","metadata":{"unresolvedName":"setDisplayName"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleCreate","to":"symbol-name:loadInstances","kind":"calls","metadata":{"unresolvedName":"loadInstances"}},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleStart","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleStart","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleStart","to":"symbol-name:loadInstances","kind":"calls","metadata":{"unresolvedName":"loadInstances"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleStart","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleStop","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleStop","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleStop","to":"symbol-name:setStopConfirmId","kind":"calls","metadata":{"unresolvedName":"setStopConfirmId"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleStop","to":"symbol-name:loadInstances","kind":"calls","metadata":{"unresolvedName":"loadInstances"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleStop","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleRestart","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleRestart","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleRestart","to":"symbol-name:loadInstances","kind":"calls","metadata":{"unresolvedName":"loadInstances"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleRestart","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleDelete","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleDelete","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleDelete","to":"symbol-name:confirm","kind":"calls","metadata":{"unresolvedName":"confirm"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleDelete","to":"symbol-name:setInstances","kind":"calls","metadata":{"unresolvedName":"setInstances"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleDelete","to":"external:prev.filter","kind":"calls","metadata":{"unresolvedName":"prev.filter"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleDelete","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleRecreateTunnel","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleRecreateTunnel","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleRecreateTunnel","to":"symbol-name:loadInstances","kind":"calls","metadata":{"unresolvedName":"loadInstances"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleRecreateTunnel","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:getStatusColor","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:getStatusColor","kind":"defines"},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:isTunnelUnhealthy","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/InstanceList.tsx","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:isTunnelUnhealthy","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:isTunnelUnhealthy","to":"external:instance.url?.startsWith","kind":"calls","metadata":{"unresolvedName":"instance.url?.startsWith"}},{"from":"file:/workspace/apps/web/src/components/features/session/SessionCard.test.tsx","to":"external:@testing-library/react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/SessionCard.test.tsx","to":"external:vitest","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/SessionCard.test.tsx","to":"file:/workspace/apps/web/src/components/features/session/SessionCard.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/SessionCard.test.tsx","to":"external:@/types/session","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/SessionCard.test.tsx","to":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/SessionCard.test.tsx","to":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","to":"external:expect(screen.getByText(\"running\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"running\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","to":"external:screen.getAllByText","kind":"calls","metadata":{"unresolvedName":"screen.getAllByText"}},{"from":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","to":"external:expect(screen.getByText(\"VS Code\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code\")).toBeInTheDocument"}},{"from":"file:/workspace/apps/web/src/components/features/session/SessionCard.test.tsx","to":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/SessionCard.test.tsx","to":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","to":"external:expect(screen.getByText(\"running\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"running\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","to":"external:screen.getAllByText","kind":"calls","metadata":{"unresolvedName":"screen.getAllByText"}},{"from":"file:/workspace/apps/web/src/components/features/session/SessionCard.test.tsx","to":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/SessionCard.test.tsx","to":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","to":"external:screen.getAllByText","kind":"calls","metadata":{"unresolvedName":"screen.getAllByText"}},{"from":"file:/workspace/apps/web/src/components/features/session/SessionCard.test.tsx","to":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/SessionCard.test.tsx","to":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","to":"external:expect(screen.getByText(\"VS Code\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/features/session/SessionCard.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/components/features/session/SessionCard.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/SessionCard.tsx","to":"external:@/components/ui/Icon","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/SessionCard.tsx","to":"external:@/types/session","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/SessionCard.tsx","to":"/workspace/apps/web/src/components/features/session/SessionCard.tsx:SessionCard","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/SessionCard.tsx","to":"/workspace/apps/web/src/components/features/session/SessionCard.tsx:SessionCard","kind":"defines"},{"from":"file:/workspace/apps/web/src/components/features/session/SessionList.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/SessionList.tsx","to":"external:@/api/sessions","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/SessionList.tsx","to":"external:@/types/session","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/SessionList.tsx","to":"file:/workspace/apps/web/src/components/features/session/SessionCard.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/session/SessionList.tsx","to":"/workspace/apps/web/src/components/features/session/SessionList.tsx:SessionList","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/SessionList.tsx","to":"/workspace/apps/web/src/components/features/session/SessionList.tsx:SessionList","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:SessionList","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:SessionList","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:SessionList","to":"external:sessions.filter","kind":"calls","metadata":{"unresolvedName":"sessions.filter"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:SessionList","to":"symbol-name:setTunnelHealth","kind":"calls","metadata":{"unresolvedName":"setTunnelHealth"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:SessionList","to":"symbol-name:checkHealth","kind":"calls","metadata":{"unresolvedName":"checkHealth"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:SessionList","to":"symbol-name:setInterval","kind":"calls","metadata":{"unresolvedName":"setInterval"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:SessionList","to":"symbol-name:clearInterval","kind":"calls","metadata":{"unresolvedName":"clearInterval"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:SessionList","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:SessionList","to":"symbol-name:setStopConfirmId","kind":"calls","metadata":{"unresolvedName":"setStopConfirmId"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:SessionList","to":"symbol-name:onSessionChange","kind":"calls","metadata":{"unresolvedName":"onSessionChange"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:SessionList","to":"symbol-name:setDeleteConfirmId","kind":"calls","metadata":{"unresolvedName":"setDeleteConfirmId"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:SessionList","to":"symbol-name:setRecreatingId","kind":"calls","metadata":{"unresolvedName":"setRecreatingId"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:SessionList","to":"external:window.open","kind":"calls","metadata":{"unresolvedName":"window.open"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:SessionList","to":"symbol-name:onOpen","kind":"calls","metadata":{"unresolvedName":"onOpen"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:SessionList","to":"external:sessions.map","kind":"calls","metadata":{"unresolvedName":"sessions.map"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:SessionList","to":"/workspace/apps/web/src/components/start-tool-fab.tsx:handleOpen","kind":"calls","metadata":{"unresolvedName":"handleOpen"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:SessionList","to":"symbol-name:handleStop","kind":"calls","metadata":{"unresolvedName":"handleStop"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:SessionList","to":"symbol-name:handleDelete","kind":"calls","metadata":{"unresolvedName":"handleDelete"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:SessionList","to":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleRecreateTunnel","kind":"calls","metadata":{"unresolvedName":"handleRecreateTunnel"}},{"from":"file:/workspace/apps/web/src/components/features/session/SessionList.tsx","to":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/SessionList.tsx","to":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","to":"external:sessions.filter","kind":"calls","metadata":{"unresolvedName":"sessions.filter"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","to":"symbol-name:setTunnelHealth","kind":"calls","metadata":{"unresolvedName":"setTunnelHealth"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","to":"symbol-name:checkHealth","kind":"calls","metadata":{"unresolvedName":"checkHealth"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","to":"symbol-name:setInterval","kind":"calls","metadata":{"unresolvedName":"setInterval"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","to":"symbol-name:clearInterval","kind":"calls","metadata":{"unresolvedName":"clearInterval"}},{"from":"file:/workspace/apps/web/src/components/features/session/SessionList.tsx","to":"/workspace/apps/web/src/components/features/session/SessionList.tsx:checkHealth","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/SessionList.tsx","to":"/workspace/apps/web/src/components/features/session/SessionList.tsx:checkHealth","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:checkHealth","to":"external:sessions.filter","kind":"calls","metadata":{"unresolvedName":"sessions.filter"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:checkHealth","to":"symbol-name:setTunnelHealth","kind":"calls","metadata":{"unresolvedName":"setTunnelHealth"}},{"from":"file:/workspace/apps/web/src/components/features/session/SessionList.tsx","to":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/SessionList.tsx","to":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","to":"symbol-name:setStopConfirmId","kind":"calls","metadata":{"unresolvedName":"setStopConfirmId"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","to":"symbol-name:onSessionChange","kind":"calls","metadata":{"unresolvedName":"onSessionChange"}},{"from":"file:/workspace/apps/web/src/components/features/session/SessionList.tsx","to":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/SessionList.tsx","to":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","to":"symbol-name:setDeleteConfirmId","kind":"calls","metadata":{"unresolvedName":"setDeleteConfirmId"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","to":"symbol-name:onSessionChange","kind":"calls","metadata":{"unresolvedName":"onSessionChange"}},{"from":"file:/workspace/apps/web/src/components/features/session/SessionList.tsx","to":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/SessionList.tsx","to":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","to":"symbol-name:setRecreatingId","kind":"calls","metadata":{"unresolvedName":"setRecreatingId"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","to":"symbol-name:onSessionChange","kind":"calls","metadata":{"unresolvedName":"onSessionChange"}},{"from":"file:/workspace/apps/web/src/components/features/session/SessionList.tsx","to":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/SessionList.tsx","to":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","to":"external:window.open","kind":"calls","metadata":{"unresolvedName":"window.open"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","to":"symbol-name:onOpen","kind":"calls","metadata":{"unresolvedName":"onOpen"}},{"from":"file:/workspace/apps/web/src/components/features/session/SessionList.tsx","to":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/SessionList.tsx","to":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/components/features/session/SessionList.tsx","to":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/SessionList.tsx","to":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/components/features/session/SessionList.tsx","to":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/session/SessionList.tsx","to":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/components/features/settings/SettingsTabLayout.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/settings/SettingsTabLayout.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/settings/SettingsTabLayout.tsx","to":"file:/workspace/apps/web/src/components/features/settings/SettingsTabLayout.module.css","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/settings/SettingsTabLayout.tsx","to":"/workspace/apps/web/src/components/features/settings/SettingsTabLayout.tsx:SettingsTabLayout","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/settings/SettingsTabLayout.tsx","to":"/workspace/apps/web/src/components/features/settings/SettingsTabLayout.tsx:SettingsTabLayout","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/settings/SettingsTabLayout.tsx:SettingsTabLayout","to":"symbol-name:useLocation","kind":"calls","metadata":{"unresolvedName":"useLocation"}},{"from":"/workspace/apps/web/src/components/features/settings/SettingsTabLayout.tsx:SettingsTabLayout","to":"external:tabs.map","kind":"calls","metadata":{"unresolvedName":"tabs.map"}},{"from":"/workspace/apps/web/src/components/features/settings/SettingsTabLayout.tsx:SettingsTabLayout","to":"external:location.pathname.includes","kind":"calls","metadata":{"unresolvedName":"location.pathname.includes"}},{"from":"file:/workspace/apps/web/src/components/features/tool-configs/ToolConfigForm.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/tool-configs/ToolConfigForm.tsx","to":"file:/workspace/apps/web/src/types/tool-config.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/tool-configs/ToolConfigForm.tsx","to":"/workspace/apps/web/src/components/features/tool-configs/ToolConfigForm.tsx:ToolConfigForm","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-configs/ToolConfigForm.tsx","to":"/workspace/apps/web/src/components/features/tool-configs/ToolConfigForm.tsx:ToolConfigForm","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-configs/ToolConfigForm.tsx:ToolConfigForm","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/features/tool-configs/ToolConfigForm.tsx:ToolConfigForm","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/components/features/tool-configs/ToolConfigForm.tsx:ToolConfigForm","to":"symbol-name:setSaveStatus","kind":"calls","metadata":{"unresolvedName":"setSaveStatus"}},{"from":"/workspace/apps/web/src/components/features/tool-configs/ToolConfigForm.tsx:ToolConfigForm","to":"symbol-name:onSubmit","kind":"calls","metadata":{"unresolvedName":"onSubmit"}},{"from":"/workspace/apps/web/src/components/features/tool-configs/ToolConfigForm.tsx:ToolConfigForm","to":"symbol-name:setFormData","kind":"calls","metadata":{"unresolvedName":"setFormData"}},{"from":"file:/workspace/apps/web/src/components/features/tool-configs/ToolConfigForm.tsx","to":"/workspace/apps/web/src/components/features/tool-configs/ToolConfigForm.tsx:handleSubmit","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-configs/ToolConfigForm.tsx","to":"/workspace/apps/web/src/components/features/tool-configs/ToolConfigForm.tsx:handleSubmit","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-configs/ToolConfigForm.tsx:handleSubmit","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/components/features/tool-configs/ToolConfigForm.tsx:handleSubmit","to":"symbol-name:setSaveStatus","kind":"calls","metadata":{"unresolvedName":"setSaveStatus"}},{"from":"/workspace/apps/web/src/components/features/tool-configs/ToolConfigForm.tsx:handleSubmit","to":"symbol-name:onSubmit","kind":"calls","metadata":{"unresolvedName":"onSubmit"}},{"from":"file:/workspace/apps/web/src/components/features/tool-configs/ToolConfigList.tsx","to":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/tool-configs/ToolConfigList.tsx","to":"file:/workspace/apps/web/src/types/tool-config.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/tool-configs/ToolConfigList.tsx","to":"/workspace/apps/web/src/components/features/tool-configs/ToolConfigList.tsx:ToolConfigList","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-configs/ToolConfigList.tsx","to":"/workspace/apps/web/src/components/features/tool-configs/ToolConfigList.tsx:ToolConfigList","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-configs/ToolConfigList.tsx:ToolConfigList","to":"external:configs.map","kind":"calls","metadata":{"unresolvedName":"configs.map"}},{"from":"/workspace/apps/web/src/components/features/tool-configs/ToolConfigList.tsx:ToolConfigList","to":"symbol-name:onEdit","kind":"calls","metadata":{"unresolvedName":"onEdit"}},{"from":"/workspace/apps/web/src/components/features/tool-configs/ToolConfigList.tsx:ToolConfigList","to":"symbol-name:onDelete","kind":"calls","metadata":{"unresolvedName":"onDelete"}},{"from":"file:/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx","to":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx","to":"file:/workspace/apps/web/src/types/tool-type.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx","to":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx","to":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","to":"external:toolType?.default_port?.toString","kind":"calls","metadata":{"unresolvedName":"toolType?.default_port?.toString"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","to":"external:toolType?.required_variables?.join","kind":"calls","metadata":{"unresolvedName":"toolType?.required_variables?.join"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","to":"symbol-name:setFormError","kind":"calls","metadata":{"unresolvedName":"setFormError"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","to":"external:formName.trim","kind":"calls","metadata":{"unresolvedName":"formName.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","to":"external:formDisplayName.trim","kind":"calls","metadata":{"unresolvedName":"formDisplayName.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","to":"external:formTemplate.trim","kind":"calls","metadata":{"unresolvedName":"formTemplate.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","to":"external:formPort.trim","kind":"calls","metadata":{"unresolvedName":"formPort.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","to":"symbol-name:isNaN","kind":"calls","metadata":{"unresolvedName":"isNaN"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","to":"symbol-name:Number","kind":"calls","metadata":{"unresolvedName":"Number"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","to":"external:formVariables\n\t\t\t.split(\",\")\n\t\t\t.map((v) => v.trim())\n\t\t\t.filter","kind":"calls","metadata":{"unresolvedName":"formVariables\n\t\t\t.split(\",\")\n\t\t\t.map((v) => v.trim())\n\t\t\t.filter"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","to":"external:formVariables\n\t\t\t.split(\",\")\n\t\t\t.map","kind":"calls","metadata":{"unresolvedName":"formVariables\n\t\t\t.split(\",\")\n\t\t\t.map"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","to":"external:formVariables\n\t\t\t.split","kind":"calls","metadata":{"unresolvedName":"formVariables\n\t\t\t.split"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","to":"external:v.trim","kind":"calls","metadata":{"unresolvedName":"v.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","to":"external:formDescription.trim","kind":"calls","metadata":{"unresolvedName":"formDescription.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","to":"external:formCategory.trim","kind":"calls","metadata":{"unresolvedName":"formCategory.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","to":"symbol-name:onSubmit","kind":"calls","metadata":{"unresolvedName":"onSubmit"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","to":"symbol-name:setFormInterfaces","kind":"calls","metadata":{"unresolvedName":"setFormInterfaces"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","to":"external:prev.includes","kind":"calls","metadata":{"unresolvedName":"prev.includes"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","to":"external:prev.filter","kind":"calls","metadata":{"unresolvedName":"prev.filter"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","to":"symbol-name:setFormName","kind":"calls","metadata":{"unresolvedName":"setFormName"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","to":"symbol-name:setFormDisplayName","kind":"calls","metadata":{"unresolvedName":"setFormDisplayName"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","to":"symbol-name:setFormDescription","kind":"calls","metadata":{"unresolvedName":"setFormDescription"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","to":"symbol-name:setFormCategory","kind":"calls","metadata":{"unresolvedName":"setFormCategory"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","to":"external:formInterfaces.includes","kind":"calls","metadata":{"unresolvedName":"formInterfaces.includes"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","to":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:toggleInterface","kind":"calls","metadata":{"unresolvedName":"toggleInterface"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","to":"symbol-name:setFormPort","kind":"calls","metadata":{"unresolvedName":"setFormPort"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","to":"symbol-name:setFormTemplate","kind":"calls","metadata":{"unresolvedName":"setFormTemplate"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:ToolTypeForm","to":"symbol-name:setFormVariables","kind":"calls","metadata":{"unresolvedName":"setFormVariables"}},{"from":"file:/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx","to":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:handleSubmit","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx","to":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:handleSubmit","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:handleSubmit","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:handleSubmit","to":"symbol-name:setFormError","kind":"calls","metadata":{"unresolvedName":"setFormError"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:handleSubmit","to":"external:formName.trim","kind":"calls","metadata":{"unresolvedName":"formName.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:handleSubmit","to":"external:formDisplayName.trim","kind":"calls","metadata":{"unresolvedName":"formDisplayName.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:handleSubmit","to":"external:formTemplate.trim","kind":"calls","metadata":{"unresolvedName":"formTemplate.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:handleSubmit","to":"external:formPort.trim","kind":"calls","metadata":{"unresolvedName":"formPort.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:handleSubmit","to":"symbol-name:isNaN","kind":"calls","metadata":{"unresolvedName":"isNaN"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:handleSubmit","to":"symbol-name:Number","kind":"calls","metadata":{"unresolvedName":"Number"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:handleSubmit","to":"external:formVariables\n\t\t\t.split(\",\")\n\t\t\t.map((v) => v.trim())\n\t\t\t.filter","kind":"calls","metadata":{"unresolvedName":"formVariables\n\t\t\t.split(\",\")\n\t\t\t.map((v) => v.trim())\n\t\t\t.filter"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:handleSubmit","to":"external:formVariables\n\t\t\t.split(\",\")\n\t\t\t.map","kind":"calls","metadata":{"unresolvedName":"formVariables\n\t\t\t.split(\",\")\n\t\t\t.map"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:handleSubmit","to":"external:formVariables\n\t\t\t.split","kind":"calls","metadata":{"unresolvedName":"formVariables\n\t\t\t.split"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:handleSubmit","to":"external:v.trim","kind":"calls","metadata":{"unresolvedName":"v.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:handleSubmit","to":"external:formDescription.trim","kind":"calls","metadata":{"unresolvedName":"formDescription.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:handleSubmit","to":"external:formCategory.trim","kind":"calls","metadata":{"unresolvedName":"formCategory.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:handleSubmit","to":"symbol-name:onSubmit","kind":"calls","metadata":{"unresolvedName":"onSubmit"}},{"from":"file:/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx","to":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:toggleInterface","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx","to":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:toggleInterface","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:toggleInterface","to":"symbol-name:setFormInterfaces","kind":"calls","metadata":{"unresolvedName":"setFormInterfaces"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:toggleInterface","to":"external:prev.includes","kind":"calls","metadata":{"unresolvedName":"prev.includes"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeForm.tsx:toggleInterface","to":"external:prev.filter","kind":"calls","metadata":{"unresolvedName":"prev.filter"}},{"from":"file:/workspace/apps/web/src/components/features/tool-types/ToolTypeList.tsx","to":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/tool-types/ToolTypeList.tsx","to":"file:/workspace/apps/web/src/types/tool-type.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/tool-types/ToolTypeList.tsx","to":"/workspace/apps/web/src/components/features/tool-types/ToolTypeList.tsx:ToolTypeList","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-types/ToolTypeList.tsx","to":"/workspace/apps/web/src/components/features/tool-types/ToolTypeList.tsx:ToolTypeList","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeList.tsx:ToolTypeList","to":"external:toolTypes.map","kind":"calls","metadata":{"unresolvedName":"toolTypes.map"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeList.tsx:ToolTypeList","to":"external:toolType.interfaces.join","kind":"calls","metadata":{"unresolvedName":"toolType.interfaces.join"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeList.tsx:ToolTypeList","to":"symbol-name:onEdit","kind":"calls","metadata":{"unresolvedName":"onEdit"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeList.tsx:ToolTypeList","to":"symbol-name:setDeleteConfirmId","kind":"calls","metadata":{"unresolvedName":"setDeleteConfirmId"}},{"from":"/workspace/apps/web/src/components/features/tool-types/ToolTypeList.tsx:ToolTypeList","to":"symbol-name:onDelete","kind":"calls","metadata":{"unresolvedName":"onDelete"}},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx","to":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx","to":"file:/workspace/apps/web/src/api/config-folders.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab","to":"/workspace/apps/web/src/api/config-folders.ts:listConfigFolders","kind":"calls","metadata":{"unresolvedName":"listConfigFolders"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab","to":"symbol-name:setFolders","kind":"calls","metadata":{"unresolvedName":"setFolders"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab","to":"symbol-name:loadFolders","kind":"calls","metadata":{"unresolvedName":"loadFolders"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab","to":"symbol-name:setFolderForm","kind":"calls","metadata":{"unresolvedName":"setFolderForm"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab","to":"symbol-name:setFolderError","kind":"calls","metadata":{"unresolvedName":"setFolderError"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab","to":"symbol-name:setShowFolderForm","kind":"calls","metadata":{"unresolvedName":"setShowFolderForm"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab","to":"symbol-name:setSelectedFolder","kind":"calls","metadata":{"unresolvedName":"setSelectedFolder"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab","to":"external:JSON.stringify","kind":"calls","metadata":{"unresolvedName":"JSON.stringify"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab","to":"external:folderForm.name.trim","kind":"calls","metadata":{"unresolvedName":"folderForm.name.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab","to":"external:folderForm.mount_path.trim","kind":"calls","metadata":{"unresolvedName":"folderForm.mount_path.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab","to":"external:folderForm.files_json.trim","kind":"calls","metadata":{"unresolvedName":"folderForm.files_json.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab","to":"external:JSON.parse","kind":"calls","metadata":{"unresolvedName":"JSON.parse"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab","to":"external:folderForm.description.trim","kind":"calls","metadata":{"unresolvedName":"folderForm.description.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab","to":"/workspace/apps/web/src/api/config-folders.ts:updateConfigFolder","kind":"calls","metadata":{"unresolvedName":"updateConfigFolder"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab","to":"/workspace/apps/web/src/api/config-folders.ts:createConfigFolder","kind":"calls","metadata":{"unresolvedName":"createConfigFolder"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab","to":"external:window.confirm","kind":"calls","metadata":{"unresolvedName":"window.confirm"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab","to":"/workspace/apps/web/src/api/config-folders.ts:deleteConfigFolder","kind":"calls","metadata":{"unresolvedName":"deleteConfigFolder"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab","to":"symbol-name:alert","kind":"calls","metadata":{"unresolvedName":"alert"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab","to":"external:folders.map","kind":"calls","metadata":{"unresolvedName":"folders.map"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab","to":"external:Object.keys","kind":"calls","metadata":{"unresolvedName":"Object.keys"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab","to":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:openEditFolder","kind":"calls","metadata":{"unresolvedName":"openEditFolder"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:ConfigFoldersTab","to":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:handleDeleteFolder","kind":"calls","metadata":{"unresolvedName":"handleDeleteFolder"}},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:","to":"/workspace/apps/web/src/api/config-folders.ts:listConfigFolders","kind":"calls","metadata":{"unresolvedName":"listConfigFolders"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:","to":"symbol-name:setFolders","kind":"calls","metadata":{"unresolvedName":"setFolders"}},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:","to":"symbol-name:loadFolders","kind":"calls","metadata":{"unresolvedName":"loadFolders"}},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:openCreateFolder","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:openCreateFolder","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:openCreateFolder","to":"symbol-name:setFolderForm","kind":"calls","metadata":{"unresolvedName":"setFolderForm"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:openCreateFolder","to":"symbol-name:setFolderError","kind":"calls","metadata":{"unresolvedName":"setFolderError"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:openCreateFolder","to":"symbol-name:setShowFolderForm","kind":"calls","metadata":{"unresolvedName":"setShowFolderForm"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:openCreateFolder","to":"symbol-name:setSelectedFolder","kind":"calls","metadata":{"unresolvedName":"setSelectedFolder"}},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:openEditFolder","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:openEditFolder","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:openEditFolder","to":"symbol-name:setFolderForm","kind":"calls","metadata":{"unresolvedName":"setFolderForm"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:openEditFolder","to":"external:JSON.stringify","kind":"calls","metadata":{"unresolvedName":"JSON.stringify"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:openEditFolder","to":"symbol-name:setFolderError","kind":"calls","metadata":{"unresolvedName":"setFolderError"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:openEditFolder","to":"symbol-name:setShowFolderForm","kind":"calls","metadata":{"unresolvedName":"setShowFolderForm"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:openEditFolder","to":"symbol-name:setSelectedFolder","kind":"calls","metadata":{"unresolvedName":"setSelectedFolder"}},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:handleFolderSubmit","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:handleFolderSubmit","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:handleFolderSubmit","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:handleFolderSubmit","to":"symbol-name:setFolderError","kind":"calls","metadata":{"unresolvedName":"setFolderError"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:handleFolderSubmit","to":"external:folderForm.name.trim","kind":"calls","metadata":{"unresolvedName":"folderForm.name.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:handleFolderSubmit","to":"external:folderForm.mount_path.trim","kind":"calls","metadata":{"unresolvedName":"folderForm.mount_path.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:handleFolderSubmit","to":"external:folderForm.files_json.trim","kind":"calls","metadata":{"unresolvedName":"folderForm.files_json.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:handleFolderSubmit","to":"external:JSON.parse","kind":"calls","metadata":{"unresolvedName":"JSON.parse"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:handleFolderSubmit","to":"external:folderForm.description.trim","kind":"calls","metadata":{"unresolvedName":"folderForm.description.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:handleFolderSubmit","to":"/workspace/apps/web/src/api/config-folders.ts:updateConfigFolder","kind":"calls","metadata":{"unresolvedName":"updateConfigFolder"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:handleFolderSubmit","to":"/workspace/apps/web/src/api/config-folders.ts:createConfigFolder","kind":"calls","metadata":{"unresolvedName":"createConfigFolder"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:handleFolderSubmit","to":"symbol-name:setShowFolderForm","kind":"calls","metadata":{"unresolvedName":"setShowFolderForm"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:handleFolderSubmit","to":"symbol-name:setSelectedFolder","kind":"calls","metadata":{"unresolvedName":"setSelectedFolder"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:handleFolderSubmit","to":"symbol-name:loadFolders","kind":"calls","metadata":{"unresolvedName":"loadFolders"}},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:handleDeleteFolder","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:handleDeleteFolder","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:handleDeleteFolder","to":"external:window.confirm","kind":"calls","metadata":{"unresolvedName":"window.confirm"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:handleDeleteFolder","to":"/workspace/apps/web/src/api/config-folders.ts:deleteConfigFolder","kind":"calls","metadata":{"unresolvedName":"deleteConfigFolder"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:handleDeleteFolder","to":"symbol-name:loadFolders","kind":"calls","metadata":{"unresolvedName":"loadFolders"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ConfigFoldersTab.tsx:handleDeleteFolder","to":"symbol-name:alert","kind":"calls","metadata":{"unresolvedName":"alert"}},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx","to":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx","to":"file:/workspace/apps/web/src/api/tool-configs.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx","to":"file:/workspace/apps/web/src/api/tool-types.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"external:Promise.all","kind":"calls","metadata":{"unresolvedName":"Promise.all"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"/workspace/apps/web/src/api/tool-configs.ts:listToolConfigs","kind":"calls","metadata":{"unresolvedName":"listToolConfigs"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"/workspace/apps/web/src/api/tool-types.ts:listToolTypes","kind":"calls","metadata":{"unresolvedName":"listToolTypes"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"symbol-name:setConfigs","kind":"calls","metadata":{"unresolvedName":"setConfigs"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"symbol-name:setToolTypes","kind":"calls","metadata":{"unresolvedName":"setToolTypes"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"symbol-name:loadData","kind":"calls","metadata":{"unresolvedName":"loadData"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"symbol-name:setConfigForm","kind":"calls","metadata":{"unresolvedName":"setConfigForm"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"symbol-name:setConfigError","kind":"calls","metadata":{"unresolvedName":"setConfigError"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"symbol-name:setShowConfigForm","kind":"calls","metadata":{"unresolvedName":"setShowConfigForm"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"symbol-name:setSelectedConfig","kind":"calls","metadata":{"unresolvedName":"setSelectedConfig"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"external:config.port_override?.toString","kind":"calls","metadata":{"unresolvedName":"config.port_override?.toString"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"external:JSON.stringify","kind":"calls","metadata":{"unresolvedName":"JSON.stringify"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"external:configForm.key.trim","kind":"calls","metadata":{"unresolvedName":"configForm.key.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"external:configForm.env_vars_json.trim","kind":"calls","metadata":{"unresolvedName":"configForm.env_vars_json.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"external:JSON.parse","kind":"calls","metadata":{"unresolvedName":"JSON.parse"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"external:configForm.volumes_json.trim","kind":"calls","metadata":{"unresolvedName":"configForm.volumes_json.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"symbol-name:Number","kind":"calls","metadata":{"unresolvedName":"Number"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"external:configForm.start_command.trim","kind":"calls","metadata":{"unresolvedName":"configForm.start_command.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"external:configForm.working_directory.trim","kind":"calls","metadata":{"unresolvedName":"configForm.working_directory.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"/workspace/apps/web/src/api/tool-configs.ts:updateToolConfig","kind":"calls","metadata":{"unresolvedName":"updateToolConfig"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"/workspace/apps/web/src/api/tool-configs.ts:createToolConfig","kind":"calls","metadata":{"unresolvedName":"createToolConfig"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"external:window.confirm","kind":"calls","metadata":{"unresolvedName":"window.confirm"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"/workspace/apps/web/src/api/tool-configs.ts:deleteToolConfig","kind":"calls","metadata":{"unresolvedName":"deleteToolConfig"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"symbol-name:alert","kind":"calls","metadata":{"unresolvedName":"alert"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"external:toolTypes.map","kind":"calls","metadata":{"unresolvedName":"toolTypes.map"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"external:configs.map","kind":"calls","metadata":{"unresolvedName":"configs.map"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:openEditConfig","kind":"calls","metadata":{"unresolvedName":"openEditConfig"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:ToolConfigsTab","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:handleDeleteConfig","kind":"calls","metadata":{"unresolvedName":"handleDeleteConfig"}},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:","to":"external:Promise.all","kind":"calls","metadata":{"unresolvedName":"Promise.all"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:","to":"/workspace/apps/web/src/api/tool-configs.ts:listToolConfigs","kind":"calls","metadata":{"unresolvedName":"listToolConfigs"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:","to":"/workspace/apps/web/src/api/tool-types.ts:listToolTypes","kind":"calls","metadata":{"unresolvedName":"listToolTypes"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:","to":"symbol-name:setConfigs","kind":"calls","metadata":{"unresolvedName":"setConfigs"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:","to":"symbol-name:setToolTypes","kind":"calls","metadata":{"unresolvedName":"setToolTypes"}},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:","to":"symbol-name:loadData","kind":"calls","metadata":{"unresolvedName":"loadData"}},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:openCreateConfig","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:openCreateConfig","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:openCreateConfig","to":"symbol-name:setConfigForm","kind":"calls","metadata":{"unresolvedName":"setConfigForm"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:openCreateConfig","to":"symbol-name:setConfigError","kind":"calls","metadata":{"unresolvedName":"setConfigError"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:openCreateConfig","to":"symbol-name:setShowConfigForm","kind":"calls","metadata":{"unresolvedName":"setShowConfigForm"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:openCreateConfig","to":"symbol-name:setSelectedConfig","kind":"calls","metadata":{"unresolvedName":"setSelectedConfig"}},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:openEditConfig","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:openEditConfig","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:openEditConfig","to":"symbol-name:setConfigForm","kind":"calls","metadata":{"unresolvedName":"setConfigForm"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:openEditConfig","to":"external:config.port_override?.toString","kind":"calls","metadata":{"unresolvedName":"config.port_override?.toString"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:openEditConfig","to":"external:JSON.stringify","kind":"calls","metadata":{"unresolvedName":"JSON.stringify"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:openEditConfig","to":"symbol-name:setConfigError","kind":"calls","metadata":{"unresolvedName":"setConfigError"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:openEditConfig","to":"symbol-name:setShowConfigForm","kind":"calls","metadata":{"unresolvedName":"setShowConfigForm"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:openEditConfig","to":"symbol-name:setSelectedConfig","kind":"calls","metadata":{"unresolvedName":"setSelectedConfig"}},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:handleConfigSubmit","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:handleConfigSubmit","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:handleConfigSubmit","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:handleConfigSubmit","to":"symbol-name:setConfigError","kind":"calls","metadata":{"unresolvedName":"setConfigError"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:handleConfigSubmit","to":"external:configForm.key.trim","kind":"calls","metadata":{"unresolvedName":"configForm.key.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:handleConfigSubmit","to":"external:configForm.env_vars_json.trim","kind":"calls","metadata":{"unresolvedName":"configForm.env_vars_json.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:handleConfigSubmit","to":"external:JSON.parse","kind":"calls","metadata":{"unresolvedName":"JSON.parse"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:handleConfigSubmit","to":"external:configForm.volumes_json.trim","kind":"calls","metadata":{"unresolvedName":"configForm.volumes_json.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:handleConfigSubmit","to":"symbol-name:Number","kind":"calls","metadata":{"unresolvedName":"Number"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:handleConfigSubmit","to":"external:configForm.start_command.trim","kind":"calls","metadata":{"unresolvedName":"configForm.start_command.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:handleConfigSubmit","to":"external:configForm.working_directory.trim","kind":"calls","metadata":{"unresolvedName":"configForm.working_directory.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:handleConfigSubmit","to":"/workspace/apps/web/src/api/tool-configs.ts:updateToolConfig","kind":"calls","metadata":{"unresolvedName":"updateToolConfig"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:handleConfigSubmit","to":"/workspace/apps/web/src/api/tool-configs.ts:createToolConfig","kind":"calls","metadata":{"unresolvedName":"createToolConfig"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:handleConfigSubmit","to":"symbol-name:setShowConfigForm","kind":"calls","metadata":{"unresolvedName":"setShowConfigForm"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:handleConfigSubmit","to":"symbol-name:setSelectedConfig","kind":"calls","metadata":{"unresolvedName":"setSelectedConfig"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:handleConfigSubmit","to":"symbol-name:loadData","kind":"calls","metadata":{"unresolvedName":"loadData"}},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:handleDeleteConfig","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:handleDeleteConfig","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:handleDeleteConfig","to":"external:window.confirm","kind":"calls","metadata":{"unresolvedName":"window.confirm"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:handleDeleteConfig","to":"/workspace/apps/web/src/api/tool-configs.ts:deleteToolConfig","kind":"calls","metadata":{"unresolvedName":"deleteToolConfig"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:handleDeleteConfig","to":"symbol-name:loadData","kind":"calls","metadata":{"unresolvedName":"loadData"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolConfigsTab.tsx:handleDeleteConfig","to":"symbol-name:alert","kind":"calls","metadata":{"unresolvedName":"alert"}},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx","to":"external:@testing-library/react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx","to":"external:vitest","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx","to":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx","to":"file:/workspace/apps/web/src/api/tool-types.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"external:expect(screen.getByText(/loading tool types/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/loading tool types/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"external:mockedList.mockResolvedValueOnce","kind":"calls","metadata":{"unresolvedName":"mockedList.mockResolvedValueOnce"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"external:expect(screen.getByText(\"Tool Types\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Tool Types\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"external:mockedList.mockRejectedValueOnce","kind":"calls","metadata":{"unresolvedName":"mockedList.mockRejectedValueOnce"}},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"external:expect(screen.getByText(/loading tool types/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/loading tool types/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"external:mockedList.mockResolvedValueOnce","kind":"calls","metadata":{"unresolvedName":"mockedList.mockResolvedValueOnce"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"external:expect(screen.getByText(\"Tool Types\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Tool Types\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument"}},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"external:expect(screen.getByText(\"Tool Types\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Tool Types\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"external:mockedList.mockRejectedValueOnce","kind":"calls","metadata":{"unresolvedName":"mockedList.mockRejectedValueOnce"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","to":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","to":"file:/workspace/apps/web/src/api/tool-types.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"/workspace/apps/web/src/api/tool-types.ts:listToolTypes","kind":"calls","metadata":{"unresolvedName":"listToolTypes"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"symbol-name:setToolTypes","kind":"calls","metadata":{"unresolvedName":"setToolTypes"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:loadToolTypes","kind":"calls","metadata":{"unresolvedName":"loadToolTypes"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"symbol-name:setToolTypeForm","kind":"calls","metadata":{"unresolvedName":"setToolTypeForm"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"symbol-name:setToolTypeError","kind":"calls","metadata":{"unresolvedName":"setToolTypeError"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"symbol-name:setShowToolTypeForm","kind":"calls","metadata":{"unresolvedName":"setShowToolTypeForm"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"symbol-name:setSelectedToolType","kind":"calls","metadata":{"unresolvedName":"setSelectedToolType"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"external:toolType.default_port?.toString","kind":"calls","metadata":{"unresolvedName":"toolType.default_port?.toString"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"external:toolType.readiness_probe?.timeout?.toString","kind":"calls","metadata":{"unresolvedName":"toolType.readiness_probe?.timeout?.toString"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"external:toolType.readiness_probe?.interval?.toString","kind":"calls","metadata":{"unresolvedName":"toolType.readiness_probe?.interval?.toString"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"external:toolType.required_variables?.join","kind":"calls","metadata":{"unresolvedName":"toolType.required_variables?.join"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"external:toolTypeForm.name.trim","kind":"calls","metadata":{"unresolvedName":"toolTypeForm.name.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"external:toolTypeForm.display_name.trim","kind":"calls","metadata":{"unresolvedName":"toolTypeForm.display_name.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"external:toolTypeForm.default_port.trim","kind":"calls","metadata":{"unresolvedName":"toolTypeForm.default_port.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"symbol-name:isNaN","kind":"calls","metadata":{"unresolvedName":"isNaN"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"symbol-name:Number","kind":"calls","metadata":{"unresolvedName":"Number"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"external:template.trim","kind":"calls","metadata":{"unresolvedName":"template.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"external:toolTypeForm.required_variables\n\t\t\t.split(\",\")\n\t\t\t.map","kind":"calls","metadata":{"unresolvedName":"toolTypeForm.required_variables\n\t\t\t.split(\",\")\n\t\t\t.map"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"external:toolTypeForm.required_variables\n\t\t\t.split","kind":"calls","metadata":{"unresolvedName":"toolTypeForm.required_variables\n\t\t\t.split"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"external:v.trim","kind":"calls","metadata":{"unresolvedName":"v.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"external:toolTypeForm.readiness_command.trim","kind":"calls","metadata":{"unresolvedName":"toolTypeForm.readiness_command.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"symbol-name:parseInt","kind":"calls","metadata":{"unresolvedName":"parseInt"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"external:toolTypeForm.description.trim","kind":"calls","metadata":{"unresolvedName":"toolTypeForm.description.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"external:toolTypeForm.category.trim","kind":"calls","metadata":{"unresolvedName":"toolTypeForm.category.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"/workspace/apps/web/src/api/tool-types.ts:updateToolType","kind":"calls","metadata":{"unresolvedName":"updateToolType"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"/workspace/apps/web/src/api/tool-types.ts:createToolType","kind":"calls","metadata":{"unresolvedName":"createToolType"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"external:window.confirm","kind":"calls","metadata":{"unresolvedName":"window.confirm"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"/workspace/apps/web/src/api/tool-types.ts:deleteToolType","kind":"calls","metadata":{"unresolvedName":"deleteToolType"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"symbol-name:alert","kind":"calls","metadata":{"unresolvedName":"alert"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"external:[\"web\", \"terminal\"].map","kind":"calls","metadata":{"unresolvedName":"[\"web\", \"terminal\"].map"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"external:toolTypeForm.interfaces.includes","kind":"calls","metadata":{"unresolvedName":"toolTypeForm.interfaces.includes"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"external:toolTypeForm.interfaces.filter","kind":"calls","metadata":{"unresolvedName":"toolTypeForm.interfaces.filter"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"external:toolTypes.map","kind":"calls","metadata":{"unresolvedName":"toolTypes.map"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"external:toolType.interfaces.join","kind":"calls","metadata":{"unresolvedName":"toolType.interfaces.join"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:openEditToolType","kind":"calls","metadata":{"unresolvedName":"openEditToolType"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:ToolTypesTab","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleDeleteToolType","kind":"calls","metadata":{"unresolvedName":"handleDeleteToolType"}},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:","to":"/workspace/apps/web/src/api/tool-types.ts:listToolTypes","kind":"calls","metadata":{"unresolvedName":"listToolTypes"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:","to":"symbol-name:setToolTypes","kind":"calls","metadata":{"unresolvedName":"setToolTypes"}},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:loadToolTypes","kind":"calls","metadata":{"unresolvedName":"loadToolTypes"}},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:openCreateToolType","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:openCreateToolType","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:openCreateToolType","to":"symbol-name:setToolTypeForm","kind":"calls","metadata":{"unresolvedName":"setToolTypeForm"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:openCreateToolType","to":"symbol-name:setToolTypeError","kind":"calls","metadata":{"unresolvedName":"setToolTypeError"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:openCreateToolType","to":"symbol-name:setShowToolTypeForm","kind":"calls","metadata":{"unresolvedName":"setShowToolTypeForm"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:openCreateToolType","to":"symbol-name:setSelectedToolType","kind":"calls","metadata":{"unresolvedName":"setSelectedToolType"}},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:openEditToolType","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:openEditToolType","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:openEditToolType","to":"symbol-name:setToolTypeForm","kind":"calls","metadata":{"unresolvedName":"setToolTypeForm"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:openEditToolType","to":"external:toolType.default_port?.toString","kind":"calls","metadata":{"unresolvedName":"toolType.default_port?.toString"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:openEditToolType","to":"external:toolType.readiness_probe?.timeout?.toString","kind":"calls","metadata":{"unresolvedName":"toolType.readiness_probe?.timeout?.toString"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:openEditToolType","to":"external:toolType.readiness_probe?.interval?.toString","kind":"calls","metadata":{"unresolvedName":"toolType.readiness_probe?.interval?.toString"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:openEditToolType","to":"external:toolType.required_variables?.join","kind":"calls","metadata":{"unresolvedName":"toolType.required_variables?.join"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:openEditToolType","to":"symbol-name:setToolTypeError","kind":"calls","metadata":{"unresolvedName":"setToolTypeError"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:openEditToolType","to":"symbol-name:setShowToolTypeForm","kind":"calls","metadata":{"unresolvedName":"setShowToolTypeForm"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:openEditToolType","to":"symbol-name:setSelectedToolType","kind":"calls","metadata":{"unresolvedName":"setSelectedToolType"}},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleToolTypeSubmit","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleToolTypeSubmit","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleToolTypeSubmit","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleToolTypeSubmit","to":"symbol-name:setToolTypeError","kind":"calls","metadata":{"unresolvedName":"setToolTypeError"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleToolTypeSubmit","to":"external:toolTypeForm.name.trim","kind":"calls","metadata":{"unresolvedName":"toolTypeForm.name.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleToolTypeSubmit","to":"external:toolTypeForm.display_name.trim","kind":"calls","metadata":{"unresolvedName":"toolTypeForm.display_name.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleToolTypeSubmit","to":"external:toolTypeForm.default_port.trim","kind":"calls","metadata":{"unresolvedName":"toolTypeForm.default_port.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleToolTypeSubmit","to":"symbol-name:isNaN","kind":"calls","metadata":{"unresolvedName":"isNaN"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleToolTypeSubmit","to":"symbol-name:Number","kind":"calls","metadata":{"unresolvedName":"Number"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleToolTypeSubmit","to":"external:template.trim","kind":"calls","metadata":{"unresolvedName":"template.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleToolTypeSubmit","to":"external:toolTypeForm.required_variables\n\t\t\t.split(\",\")\n\t\t\t.map","kind":"calls","metadata":{"unresolvedName":"toolTypeForm.required_variables\n\t\t\t.split(\",\")\n\t\t\t.map"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleToolTypeSubmit","to":"external:toolTypeForm.required_variables\n\t\t\t.split","kind":"calls","metadata":{"unresolvedName":"toolTypeForm.required_variables\n\t\t\t.split"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleToolTypeSubmit","to":"external:v.trim","kind":"calls","metadata":{"unresolvedName":"v.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleToolTypeSubmit","to":"external:toolTypeForm.readiness_command.trim","kind":"calls","metadata":{"unresolvedName":"toolTypeForm.readiness_command.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleToolTypeSubmit","to":"symbol-name:parseInt","kind":"calls","metadata":{"unresolvedName":"parseInt"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleToolTypeSubmit","to":"external:toolTypeForm.description.trim","kind":"calls","metadata":{"unresolvedName":"toolTypeForm.description.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleToolTypeSubmit","to":"external:toolTypeForm.category.trim","kind":"calls","metadata":{"unresolvedName":"toolTypeForm.category.trim"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleToolTypeSubmit","to":"/workspace/apps/web/src/api/tool-types.ts:updateToolType","kind":"calls","metadata":{"unresolvedName":"updateToolType"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleToolTypeSubmit","to":"/workspace/apps/web/src/api/tool-types.ts:createToolType","kind":"calls","metadata":{"unresolvedName":"createToolType"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleToolTypeSubmit","to":"symbol-name:setShowToolTypeForm","kind":"calls","metadata":{"unresolvedName":"setShowToolTypeForm"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleToolTypeSubmit","to":"symbol-name:setSelectedToolType","kind":"calls","metadata":{"unresolvedName":"setSelectedToolType"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleToolTypeSubmit","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:loadToolTypes","kind":"calls","metadata":{"unresolvedName":"loadToolTypes"}},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleDeleteToolType","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleDeleteToolType","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleDeleteToolType","to":"external:window.confirm","kind":"calls","metadata":{"unresolvedName":"window.confirm"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleDeleteToolType","to":"/workspace/apps/web/src/api/tool-types.ts:deleteToolType","kind":"calls","metadata":{"unresolvedName":"deleteToolType"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleDeleteToolType","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:loadToolTypes","kind":"calls","metadata":{"unresolvedName":"loadToolTypes"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:handleDeleteToolType","to":"symbol-name:alert","kind":"calls","metadata":{"unresolvedName":"alert"}},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:","to":"symbol-name:setToolTypeForm","kind":"calls","metadata":{"unresolvedName":"setToolTypeForm"}},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:","to":"external:toolTypeForm.interfaces.filter","kind":"calls","metadata":{"unresolvedName":"toolTypeForm.interfaces.filter"}},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx","to":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/tool-workshop/ToolTypesTab.tsx:","to":"symbol-name:setToolTypeForm","kind":"calls","metadata":{"unresolvedName":"setToolTypeForm"}},{"from":"file:/workspace/apps/web/src/components/features/workspace/WorkspaceHeader.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/workspace/WorkspaceHeader.tsx","to":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/workspace/WorkspaceHeader.tsx","to":"/workspace/apps/web/src/components/features/workspace/WorkspaceHeader.tsx:WorkspaceHeader","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/workspace/WorkspaceHeader.tsx","to":"/workspace/apps/web/src/components/features/workspace/WorkspaceHeader.tsx:WorkspaceHeader","kind":"defines"},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"external:@/components/ui/Icon","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"file:/workspace/apps/web/src/api/config_profiles.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"file:/workspace/apps/web/src/api/config_profiles.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:normalizeMount","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:normalizeMount","kind":"defines"},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:normalizeMounts","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:normalizeMounts","kind":"defines"},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:normalizeMounts","to":"external:mounts.map","kind":"calls","metadata":{"unresolvedName":"mounts.map"}},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountEditor","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountEditor","kind":"defines"},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountEditor","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountEditor","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:normalizeMounts","kind":"calls","metadata":{"unresolvedName":"normalizeMounts"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountEditor","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountEditor","to":"symbol-name:setNormalizedMounts","kind":"calls","metadata":{"unresolvedName":"setNormalizedMounts"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountEditor","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:normalizeMount","kind":"calls","metadata":{"unresolvedName":"normalizeMount"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountEditor","to":"symbol-name:onChange","kind":"calls","metadata":{"unresolvedName":"onChange"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountEditor","to":"symbol-name:setIsAdding","kind":"calls","metadata":{"unresolvedName":"setIsAdding"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountEditor","to":"symbol-name:setEditingIndex","kind":"calls","metadata":{"unresolvedName":"setEditingIndex"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountEditor","to":"external:normalizedMounts.filter","kind":"calls","metadata":{"unresolvedName":"normalizedMounts.filter"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountEditor","to":"external:normalizedMounts.map","kind":"calls","metadata":{"unresolvedName":"normalizedMounts.map"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountEditor","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleUpdate","kind":"calls","metadata":{"unresolvedName":"handleUpdate"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountEditor","to":"external:mount.mappings?.map","kind":"calls","metadata":{"unresolvedName":"mount.mappings?.map"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountEditor","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleRemove","kind":"calls","metadata":{"unresolvedName":"handleRemove"}},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:","to":"symbol-name:setNormalizedMounts","kind":"calls","metadata":{"unresolvedName":"setNormalizedMounts"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:normalizeMounts","kind":"calls","metadata":{"unresolvedName":"normalizeMounts"}},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleAdd","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleAdd","kind":"defines"},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleAdd","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:normalizeMount","kind":"calls","metadata":{"unresolvedName":"normalizeMount"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleAdd","to":"symbol-name:setNormalizedMounts","kind":"calls","metadata":{"unresolvedName":"setNormalizedMounts"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleAdd","to":"symbol-name:onChange","kind":"calls","metadata":{"unresolvedName":"onChange"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleAdd","to":"symbol-name:setIsAdding","kind":"calls","metadata":{"unresolvedName":"setIsAdding"}},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleUpdate","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleUpdate","kind":"defines"},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleUpdate","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:normalizeMount","kind":"calls","metadata":{"unresolvedName":"normalizeMount"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleUpdate","to":"symbol-name:setNormalizedMounts","kind":"calls","metadata":{"unresolvedName":"setNormalizedMounts"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleUpdate","to":"symbol-name:onChange","kind":"calls","metadata":{"unresolvedName":"onChange"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleUpdate","to":"symbol-name:setEditingIndex","kind":"calls","metadata":{"unresolvedName":"setEditingIndex"}},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleRemove","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleRemove","kind":"defines"},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleRemove","to":"external:normalizedMounts.filter","kind":"calls","metadata":{"unresolvedName":"normalizedMounts.filter"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleRemove","to":"symbol-name:setNormalizedMounts","kind":"calls","metadata":{"unresolvedName":"setNormalizedMounts"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleRemove","to":"symbol-name:onChange","kind":"calls","metadata":{"unresolvedName":"onChange"}},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountForm","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountForm","kind":"defines"},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountForm","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountForm","to":"external:remoteUrl.trim","kind":"calls","metadata":{"unresolvedName":"remoteUrl.trim"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountForm","to":"symbol-name:setErrors","kind":"calls","metadata":{"unresolvedName":"setErrors"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountForm","to":"symbol-name:setValidation","kind":"calls","metadata":{"unresolvedName":"setValidation"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountForm","to":"/workspace/apps/web/src/api/config_profiles.ts:validateGitUrl","kind":"calls","metadata":{"unresolvedName":"validateGitUrl"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountForm","to":"symbol-name:setBranch","kind":"calls","metadata":{"unresolvedName":"setBranch"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountForm","to":"symbol-name:setRemoteUrl","kind":"calls","metadata":{"unresolvedName":"setRemoteUrl"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountForm","to":"external:remoteUrl.startsWith","kind":"calls","metadata":{"unresolvedName":"remoteUrl.startsWith"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountForm","to":"external:mappings.forEach","kind":"calls","metadata":{"unresolvedName":"mappings.forEach"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountForm","to":"external:m.target_path.trim","kind":"calls","metadata":{"unresolvedName":"m.target_path.trim"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountForm","to":"external:m.source_path.includes","kind":"calls","metadata":{"unresolvedName":"m.source_path.includes"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountForm","to":"external:m.target_path.includes","kind":"calls","metadata":{"unresolvedName":"m.target_path.includes"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountForm","to":"external:Object.keys","kind":"calls","metadata":{"unresolvedName":"Object.keys"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountForm","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:validate","kind":"calls","metadata":{"unresolvedName":"validate"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountForm","to":"symbol-name:onSave","kind":"calls","metadata":{"unresolvedName":"onSave"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountForm","to":"external:branch.trim","kind":"calls","metadata":{"unresolvedName":"branch.trim"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountForm","to":"external:mappings.map","kind":"calls","metadata":{"unresolvedName":"mappings.map"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountForm","to":"external:m.source_path.trim","kind":"calls","metadata":{"unresolvedName":"m.source_path.trim"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountForm","to":"symbol-name:setMappings","kind":"calls","metadata":{"unresolvedName":"setMappings"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountForm","to":"external:prev.filter","kind":"calls","metadata":{"unresolvedName":"prev.filter"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountForm","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:updateMapping","kind":"calls","metadata":{"unresolvedName":"updateMapping"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:GitMountForm","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:removeMapping","kind":"calls","metadata":{"unresolvedName":"removeMapping"}},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleCheckUrl","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleCheckUrl","kind":"defines"},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleCheckUrl","to":"external:remoteUrl.trim","kind":"calls","metadata":{"unresolvedName":"remoteUrl.trim"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleCheckUrl","to":"symbol-name:setErrors","kind":"calls","metadata":{"unresolvedName":"setErrors"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleCheckUrl","to":"symbol-name:setValidation","kind":"calls","metadata":{"unresolvedName":"setValidation"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleCheckUrl","to":"/workspace/apps/web/src/api/config_profiles.ts:validateGitUrl","kind":"calls","metadata":{"unresolvedName":"validateGitUrl"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleCheckUrl","to":"symbol-name:setBranch","kind":"calls","metadata":{"unresolvedName":"setBranch"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleCheckUrl","to":"symbol-name:setRemoteUrl","kind":"calls","metadata":{"unresolvedName":"setRemoteUrl"}},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:applySuggestion","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:applySuggestion","kind":"defines"},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:applySuggestion","to":"symbol-name:setRemoteUrl","kind":"calls","metadata":{"unresolvedName":"setRemoteUrl"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:applySuggestion","to":"symbol-name:setValidation","kind":"calls","metadata":{"unresolvedName":"setValidation"}},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:validate","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:validate","kind":"defines"},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:validate","to":"external:remoteUrl.trim","kind":"calls","metadata":{"unresolvedName":"remoteUrl.trim"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:validate","to":"external:remoteUrl.startsWith","kind":"calls","metadata":{"unresolvedName":"remoteUrl.startsWith"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:validate","to":"external:mappings.forEach","kind":"calls","metadata":{"unresolvedName":"mappings.forEach"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:validate","to":"external:m.target_path.trim","kind":"calls","metadata":{"unresolvedName":"m.target_path.trim"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:validate","to":"external:m.source_path.includes","kind":"calls","metadata":{"unresolvedName":"m.source_path.includes"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:validate","to":"external:m.target_path.includes","kind":"calls","metadata":{"unresolvedName":"m.target_path.includes"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:validate","to":"symbol-name:setErrors","kind":"calls","metadata":{"unresolvedName":"setErrors"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:validate","to":"external:Object.keys","kind":"calls","metadata":{"unresolvedName":"Object.keys"}},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:","to":"external:m.target_path.trim","kind":"calls","metadata":{"unresolvedName":"m.target_path.trim"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:","to":"external:m.source_path.includes","kind":"calls","metadata":{"unresolvedName":"m.source_path.includes"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:","to":"external:m.target_path.includes","kind":"calls","metadata":{"unresolvedName":"m.target_path.includes"}},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleSubmit","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleSubmit","kind":"defines"},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleSubmit","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:validate","kind":"calls","metadata":{"unresolvedName":"validate"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleSubmit","to":"symbol-name:onSave","kind":"calls","metadata":{"unresolvedName":"onSave"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleSubmit","to":"external:remoteUrl.trim","kind":"calls","metadata":{"unresolvedName":"remoteUrl.trim"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleSubmit","to":"external:branch.trim","kind":"calls","metadata":{"unresolvedName":"branch.trim"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleSubmit","to":"external:mappings.map","kind":"calls","metadata":{"unresolvedName":"mappings.map"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleSubmit","to":"external:m.source_path.trim","kind":"calls","metadata":{"unresolvedName":"m.source_path.trim"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:handleSubmit","to":"external:m.target_path.trim","kind":"calls","metadata":{"unresolvedName":"m.target_path.trim"}},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:addMapping","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:addMapping","kind":"defines"},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:addMapping","to":"symbol-name:setMappings","kind":"calls","metadata":{"unresolvedName":"setMappings"}},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:updateMapping","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:updateMapping","kind":"defines"},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:updateMapping","to":"symbol-name:setMappings","kind":"calls","metadata":{"unresolvedName":"setMappings"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:updateMapping","to":"symbol-name:setErrors","kind":"calls","metadata":{"unresolvedName":"setErrors"}},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:removeMapping","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:removeMapping","kind":"defines"},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:removeMapping","to":"symbol-name:setMappings","kind":"calls","metadata":{"unresolvedName":"setMappings"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:removeMapping","to":"external:prev.filter","kind":"calls","metadata":{"unresolvedName":"prev.filter"}},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:","to":"symbol-name:setRemoteUrl","kind":"calls","metadata":{"unresolvedName":"setRemoteUrl"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:","to":"symbol-name:setValidation","kind":"calls","metadata":{"unresolvedName":"setValidation"}},{"from":"/workspace/apps/web/src/components/git-mount-editor.tsx:","to":"symbol-name:setErrors","kind":"calls","metadata":{"unresolvedName":"setErrors"}},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","to":"/workspace/apps/web/src/components/git-mount-editor.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/components/layout/AppShell.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/layout/AppShell.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/layout/AppShell.tsx","to":"file:/workspace/apps/web/src/api/sessions.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/layout/AppShell.tsx","to":"file:/workspace/apps/web/src/types/session.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/layout/AppShell.tsx","to":"file:/workspace/apps/web/src/hooks/use-theme.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/layout/AppShell.tsx","to":"file:/workspace/apps/web/src/state/auth.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/layout/AppShell.tsx","to":"file:/workspace/apps/web/src/state/sessions.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/layout/AppShell.tsx","to":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/layout/AppShell.tsx","to":"file:/workspace/apps/web/src/utils/icons.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/layout/AppShell.tsx","to":"file:/workspace/apps/web/src/components/layout/AppShell.module.css","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/layout/AppShell.tsx","to":"/workspace/apps/web/src/components/layout/AppShell.tsx:SessionItem","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/layout/AppShell.tsx","to":"/workspace/apps/web/src/components/layout/AppShell.tsx:SessionItem","kind":"defines"},{"from":"file:/workspace/apps/web/src/components/layout/AppShell.tsx","to":"/workspace/apps/web/src/components/layout/AppShell.tsx:AppShell","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/layout/AppShell.tsx","to":"/workspace/apps/web/src/components/layout/AppShell.tsx:AppShell","kind":"defines"},{"from":"/workspace/apps/web/src/components/layout/AppShell.tsx:AppShell","to":"/workspace/apps/web/src/hooks/use-theme.ts:useTheme","kind":"calls","metadata":{"unresolvedName":"useTheme"}},{"from":"/workspace/apps/web/src/components/layout/AppShell.tsx:AppShell","to":"/workspace/apps/web/src/state/auth.tsx:useAuth","kind":"calls","metadata":{"unresolvedName":"useAuth"}},{"from":"/workspace/apps/web/src/components/layout/AppShell.tsx:AppShell","to":"/workspace/apps/web/src/state/sessions.tsx:useSessions","kind":"calls","metadata":{"unresolvedName":"useSessions"}},{"from":"/workspace/apps/web/src/components/layout/AppShell.tsx:AppShell","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/components/layout/AppShell.tsx:AppShell","to":"symbol-name:setAllSessions","kind":"calls","metadata":{"unresolvedName":"setAllSessions"}},{"from":"/workspace/apps/web/src/components/layout/AppShell.tsx:AppShell","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/components/layout/AppShell.tsx:AppShell","to":"symbol-name:loadSessions","kind":"calls","metadata":{"unresolvedName":"loadSessions"}},{"from":"/workspace/apps/web/src/components/layout/AppShell.tsx:AppShell","to":"symbol-name:setInterval","kind":"calls","metadata":{"unresolvedName":"setInterval"}},{"from":"/workspace/apps/web/src/components/layout/AppShell.tsx:AppShell","to":"symbol-name:clearInterval","kind":"calls","metadata":{"unresolvedName":"clearInterval"}},{"from":"/workspace/apps/web/src/components/layout/AppShell.tsx:AppShell","to":"symbol-name:logout","kind":"calls","metadata":{"unresolvedName":"logout"}},{"from":"/workspace/apps/web/src/components/layout/AppShell.tsx:AppShell","to":"external:NAV_ITEMS.map","kind":"calls","metadata":{"unresolvedName":"NAV_ITEMS.map"}},{"from":"/workspace/apps/web/src/components/layout/AppShell.tsx:AppShell","to":"external:sessions.filter","kind":"calls","metadata":{"unresolvedName":"sessions.filter"}},{"from":"/workspace/apps/web/src/components/layout/AppShell.tsx:AppShell","to":"external:ACTIVE_STATUSES.includes","kind":"calls","metadata":{"unresolvedName":"ACTIVE_STATUSES.includes"}},{"from":"/workspace/apps/web/src/components/layout/AppShell.tsx:AppShell","to":"external:sessions\n\t\t\t\t\t\t\t\t.filter","kind":"calls","metadata":{"unresolvedName":"sessions\n\t\t\t\t\t\t\t\t.filter"}},{"from":"file:/workspace/apps/web/src/components/layout/AppShell.tsx","to":"/workspace/apps/web/src/components/layout/AppShell.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/layout/AppShell.tsx","to":"/workspace/apps/web/src/components/layout/AppShell.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/layout/AppShell.tsx:","to":"symbol-name:setAllSessions","kind":"calls","metadata":{"unresolvedName":"setAllSessions"}},{"from":"file:/workspace/apps/web/src/components/layout/AppShell.tsx","to":"/workspace/apps/web/src/components/layout/AppShell.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/layout/AppShell.tsx","to":"/workspace/apps/web/src/components/layout/AppShell.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/layout/AppShell.tsx:","to":"symbol-name:loadSessions","kind":"calls","metadata":{"unresolvedName":"loadSessions"}},{"from":"/workspace/apps/web/src/components/layout/AppShell.tsx:","to":"symbol-name:setInterval","kind":"calls","metadata":{"unresolvedName":"setInterval"}},{"from":"/workspace/apps/web/src/components/layout/AppShell.tsx:","to":"symbol-name:clearInterval","kind":"calls","metadata":{"unresolvedName":"clearInterval"}},{"from":"file:/workspace/apps/web/src/components/layout/AppShell.tsx","to":"/workspace/apps/web/src/components/layout/AppShell.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/layout/AppShell.tsx","to":"/workspace/apps/web/src/components/layout/AppShell.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/layout/AppShell.tsx:","to":"symbol-name:loadSessions","kind":"calls","metadata":{"unresolvedName":"loadSessions"}},{"from":"file:/workspace/apps/web/src/components/layout/AppShell.tsx","to":"/workspace/apps/web/src/components/layout/AppShell.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/layout/AppShell.tsx","to":"/workspace/apps/web/src/components/layout/AppShell.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/layout/AppShell.tsx:","to":"symbol-name:logout","kind":"calls","metadata":{"unresolvedName":"logout"}},{"from":"file:/workspace/apps/web/src/components/layout/AppShell.tsx","to":"/workspace/apps/web/src/components/layout/AppShell.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/layout/AppShell.tsx","to":"/workspace/apps/web/src/components/layout/AppShell.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/layout/AppShell.tsx:","to":"external:sessions.filter","kind":"calls","metadata":{"unresolvedName":"sessions.filter"}},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"external:@/components/ui/Icon","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"file:/workspace/apps/web/src/utils/errors.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"file:/workspace/apps/web/src/api/tool_definitions.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","kind":"defines"},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:setBaseImage","kind":"calls","metadata":{"unresolvedName":"setBaseImage"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:setBaseDefinitionId","kind":"calls","metadata":{"unresolvedName":"setBaseDefinitionId"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:setAptPackages","kind":"calls","metadata":{"unresolvedName":"setAptPackages"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"external:((pkgs.apt as string[]) || []).map","kind":"calls","metadata":{"unresolvedName":"((pkgs.apt as string[]) || []).map"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:setNpmPackages","kind":"calls","metadata":{"unresolvedName":"setNpmPackages"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"external:((pkgs.npm_global as string[]) || []).map","kind":"calls","metadata":{"unresolvedName":"((pkgs.npm_global as string[]) || []).map"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:setPipPackages","kind":"calls","metadata":{"unresolvedName":"setPipPackages"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"external:((pkgs.pip as string[]) || []).map","kind":"calls","metadata":{"unresolvedName":"((pkgs.pip as string[]) || []).map"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:setNodeVersion","kind":"calls","metadata":{"unresolvedName":"setNodeVersion"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:setUserName","kind":"calls","metadata":{"unresolvedName":"setUserName"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:setUserUid","kind":"calls","metadata":{"unresolvedName":"setUserUid"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:String","kind":"calls","metadata":{"unresolvedName":"String"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:setUserGid","kind":"calls","metadata":{"unresolvedName":"setUserGid"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:setEnvVars","kind":"calls","metadata":{"unresolvedName":"setEnvVars"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"external:Object.entries(env).map","kind":"calls","metadata":{"unresolvedName":"Object.entries(env).map"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"external:Object.entries","kind":"calls","metadata":{"unresolvedName":"Object.entries"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:setBuildScripts","kind":"calls","metadata":{"unresolvedName":"setBuildScripts"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:setStartupScripts","kind":"calls","metadata":{"unresolvedName":"setStartupScripts"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:setMounts","kind":"calls","metadata":{"unresolvedName":"setMounts"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:setCommand","kind":"calls","metadata":{"unresolvedName":"setCommand"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:setWorkingDir","kind":"calls","metadata":{"unresolvedName":"setWorkingDir"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:setStdinOpen","kind":"calls","metadata":{"unresolvedName":"setStdinOpen"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:setTty","kind":"calls","metadata":{"unresolvedName":"setTty"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"external:aptPackages.map((p) => p.name).filter","kind":"calls","metadata":{"unresolvedName":"aptPackages.map((p) => p.name).filter"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"external:aptPackages.map","kind":"calls","metadata":{"unresolvedName":"aptPackages.map"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"external:npmPackages.map((p) => p.name).filter","kind":"calls","metadata":{"unresolvedName":"npmPackages.map((p) => p.name).filter"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"external:npmPackages.map","kind":"calls","metadata":{"unresolvedName":"npmPackages.map"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"external:pipPackages.map((p) => p.name).filter","kind":"calls","metadata":{"unresolvedName":"pipPackages.map((p) => p.name).filter"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"external:pipPackages.map","kind":"calls","metadata":{"unresolvedName":"pipPackages.map"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"external:envVars.forEach","kind":"calls","metadata":{"unresolvedName":"envVars.forEach"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"external:buildScripts.filter","kind":"calls","metadata":{"unresolvedName":"buildScripts.filter"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"external:startupScripts.filter","kind":"calls","metadata":{"unresolvedName":"startupScripts.filter"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"external:mounts.filter","kind":"calls","metadata":{"unresolvedName":"mounts.filter"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:parseInt","kind":"calls","metadata":{"unresolvedName":"parseInt"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"external:command.filter","kind":"calls","metadata":{"unresolvedName":"command.filter"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:useRef","kind":"calls","metadata":{"unresolvedName":"useRef"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:buildManifest","kind":"calls","metadata":{"unresolvedName":"buildManifest"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"external:JSON.stringify","kind":"calls","metadata":{"unresolvedName":"JSON.stringify"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"external:onChangeRef.current","kind":"calls","metadata":{"unresolvedName":"onChangeRef.current"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:setPreviewError","kind":"calls","metadata":{"unresolvedName":"setPreviewError"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:setPreviewLoading","kind":"calls","metadata":{"unresolvedName":"setPreviewLoading"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"/workspace/apps/web/src/api/tool_definitions.ts:compileToolDefinition","kind":"calls","metadata":{"unresolvedName":"compileToolDefinition"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:setPreview","kind":"calls","metadata":{"unresolvedName":"setPreview"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"/workspace/apps/web/src/utils/errors.ts:extractErrorMessage","kind":"calls","metadata":{"unresolvedName":"extractErrorMessage"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"external:aptPackages.filter","kind":"calls","metadata":{"unresolvedName":"aptPackages.filter"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"external:npmPackages.filter","kind":"calls","metadata":{"unresolvedName":"npmPackages.filter"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"external:pipPackages.filter","kind":"calls","metadata":{"unresolvedName":"pipPackages.filter"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"external:envVars.filter","kind":"calls","metadata":{"unresolvedName":"envVars.filter"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"external:baseDefinitions.map","kind":"calls","metadata":{"unresolvedName":"baseDefinitions.map"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"/workspace/apps/web/src/components/manifest-editor.tsx:updateAptPackage","kind":"calls","metadata":{"unresolvedName":"updateAptPackage"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:removeAptPackage","kind":"calls","metadata":{"unresolvedName":"removeAptPackage"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"/workspace/apps/web/src/components/manifest-editor.tsx:updateNpmPackage","kind":"calls","metadata":{"unresolvedName":"updateNpmPackage"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:removeNpmPackage","kind":"calls","metadata":{"unresolvedName":"removeNpmPackage"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"/workspace/apps/web/src/components/manifest-editor.tsx:updatePipPackage","kind":"calls","metadata":{"unresolvedName":"updatePipPackage"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:removePipPackage","kind":"calls","metadata":{"unresolvedName":"removePipPackage"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"external:envVars.map","kind":"calls","metadata":{"unresolvedName":"envVars.map"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:updateEnvVar","kind":"calls","metadata":{"unresolvedName":"updateEnvVar"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"/workspace/apps/web/src/pages/config-profiles.tsx:removeEnvVar","kind":"calls","metadata":{"unresolvedName":"removeEnvVar"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"external:buildScripts.map","kind":"calls","metadata":{"unresolvedName":"buildScripts.map"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"/workspace/apps/web/src/components/manifest-editor.tsx:updateBuildScript","kind":"calls","metadata":{"unresolvedName":"updateBuildScript"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:removeBuildScript","kind":"calls","metadata":{"unresolvedName":"removeBuildScript"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"external:startupScripts.map","kind":"calls","metadata":{"unresolvedName":"startupScripts.map"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"/workspace/apps/web/src/components/manifest-editor.tsx:updateStartupScript","kind":"calls","metadata":{"unresolvedName":"updateStartupScript"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:removeStartupScript","kind":"calls","metadata":{"unresolvedName":"removeStartupScript"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"external:mounts.map","kind":"calls","metadata":{"unresolvedName":"mounts.map"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"symbol-name:updateMount","kind":"calls","metadata":{"unresolvedName":"updateMount"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"/workspace/apps/web/src/pages/config-profiles.tsx:removeMount","kind":"calls","metadata":{"unresolvedName":"removeMount"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"external:command.join","kind":"calls","metadata":{"unresolvedName":"command.join"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:ManifestEditor","to":"external:e.target.value.split","kind":"calls","metadata":{"unresolvedName":"e.target.value.split"}},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"/workspace/apps/web/src/components/manifest-editor.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"/workspace/apps/web/src/components/manifest-editor.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"symbol-name:setBaseImage","kind":"calls","metadata":{"unresolvedName":"setBaseImage"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"symbol-name:setBaseDefinitionId","kind":"calls","metadata":{"unresolvedName":"setBaseDefinitionId"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"symbol-name:setAptPackages","kind":"calls","metadata":{"unresolvedName":"setAptPackages"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"external:((pkgs.apt as string[]) || []).map","kind":"calls","metadata":{"unresolvedName":"((pkgs.apt as string[]) || []).map"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"symbol-name:setNpmPackages","kind":"calls","metadata":{"unresolvedName":"setNpmPackages"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"external:((pkgs.npm_global as string[]) || []).map","kind":"calls","metadata":{"unresolvedName":"((pkgs.npm_global as string[]) || []).map"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"symbol-name:setPipPackages","kind":"calls","metadata":{"unresolvedName":"setPipPackages"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"external:((pkgs.pip as string[]) || []).map","kind":"calls","metadata":{"unresolvedName":"((pkgs.pip as string[]) || []).map"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"symbol-name:setNodeVersion","kind":"calls","metadata":{"unresolvedName":"setNodeVersion"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"symbol-name:setUserName","kind":"calls","metadata":{"unresolvedName":"setUserName"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"symbol-name:setUserUid","kind":"calls","metadata":{"unresolvedName":"setUserUid"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"symbol-name:String","kind":"calls","metadata":{"unresolvedName":"String"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"symbol-name:setUserGid","kind":"calls","metadata":{"unresolvedName":"setUserGid"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"symbol-name:setEnvVars","kind":"calls","metadata":{"unresolvedName":"setEnvVars"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"external:Object.entries(env).map","kind":"calls","metadata":{"unresolvedName":"Object.entries(env).map"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"external:Object.entries","kind":"calls","metadata":{"unresolvedName":"Object.entries"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"symbol-name:setBuildScripts","kind":"calls","metadata":{"unresolvedName":"setBuildScripts"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"symbol-name:setStartupScripts","kind":"calls","metadata":{"unresolvedName":"setStartupScripts"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"symbol-name:setMounts","kind":"calls","metadata":{"unresolvedName":"setMounts"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"symbol-name:setCommand","kind":"calls","metadata":{"unresolvedName":"setCommand"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"symbol-name:setWorkingDir","kind":"calls","metadata":{"unresolvedName":"setWorkingDir"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"symbol-name:setStdinOpen","kind":"calls","metadata":{"unresolvedName":"setStdinOpen"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"symbol-name:setTty","kind":"calls","metadata":{"unresolvedName":"setTty"}},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"/workspace/apps/web/src/components/manifest-editor.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"/workspace/apps/web/src/components/manifest-editor.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"external:aptPackages.map((p) => p.name).filter","kind":"calls","metadata":{"unresolvedName":"aptPackages.map((p) => p.name).filter"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"external:aptPackages.map","kind":"calls","metadata":{"unresolvedName":"aptPackages.map"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"external:npmPackages.map((p) => p.name).filter","kind":"calls","metadata":{"unresolvedName":"npmPackages.map((p) => p.name).filter"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"external:npmPackages.map","kind":"calls","metadata":{"unresolvedName":"npmPackages.map"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"external:pipPackages.map((p) => p.name).filter","kind":"calls","metadata":{"unresolvedName":"pipPackages.map((p) => p.name).filter"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"external:pipPackages.map","kind":"calls","metadata":{"unresolvedName":"pipPackages.map"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"external:envVars.forEach","kind":"calls","metadata":{"unresolvedName":"envVars.forEach"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"external:buildScripts.filter","kind":"calls","metadata":{"unresolvedName":"buildScripts.filter"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"external:startupScripts.filter","kind":"calls","metadata":{"unresolvedName":"startupScripts.filter"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"external:mounts.filter","kind":"calls","metadata":{"unresolvedName":"mounts.filter"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"symbol-name:parseInt","kind":"calls","metadata":{"unresolvedName":"parseInt"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"external:command.filter","kind":"calls","metadata":{"unresolvedName":"command.filter"}},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"/workspace/apps/web/src/components/manifest-editor.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"/workspace/apps/web/src/components/manifest-editor.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"/workspace/apps/web/src/components/manifest-editor.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"/workspace/apps/web/src/components/manifest-editor.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"symbol-name:buildManifest","kind":"calls","metadata":{"unresolvedName":"buildManifest"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"external:JSON.stringify","kind":"calls","metadata":{"unresolvedName":"JSON.stringify"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"external:onChangeRef.current","kind":"calls","metadata":{"unresolvedName":"onChangeRef.current"}},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"/workspace/apps/web/src/components/manifest-editor.tsx:handlePreview","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"/workspace/apps/web/src/components/manifest-editor.tsx:handlePreview","kind":"defines"},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:handlePreview","to":"symbol-name:setPreviewError","kind":"calls","metadata":{"unresolvedName":"setPreviewError"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:handlePreview","to":"symbol-name:setPreviewLoading","kind":"calls","metadata":{"unresolvedName":"setPreviewLoading"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:handlePreview","to":"/workspace/apps/web/src/api/tool_definitions.ts:compileToolDefinition","kind":"calls","metadata":{"unresolvedName":"compileToolDefinition"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:handlePreview","to":"symbol-name:setPreview","kind":"calls","metadata":{"unresolvedName":"setPreview"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:handlePreview","to":"/workspace/apps/web/src/utils/errors.ts:extractErrorMessage","kind":"calls","metadata":{"unresolvedName":"extractErrorMessage"}},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"/workspace/apps/web/src/components/manifest-editor.tsx:updateAptPackage","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"/workspace/apps/web/src/components/manifest-editor.tsx:updateAptPackage","kind":"defines"},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:updateAptPackage","to":"symbol-name:setAptPackages","kind":"calls","metadata":{"unresolvedName":"setAptPackages"}},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"/workspace/apps/web/src/components/manifest-editor.tsx:updateNpmPackage","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"/workspace/apps/web/src/components/manifest-editor.tsx:updateNpmPackage","kind":"defines"},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:updateNpmPackage","to":"symbol-name:setNpmPackages","kind":"calls","metadata":{"unresolvedName":"setNpmPackages"}},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"/workspace/apps/web/src/components/manifest-editor.tsx:updatePipPackage","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"/workspace/apps/web/src/components/manifest-editor.tsx:updatePipPackage","kind":"defines"},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:updatePipPackage","to":"symbol-name:setPipPackages","kind":"calls","metadata":{"unresolvedName":"setPipPackages"}},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"/workspace/apps/web/src/components/manifest-editor.tsx:updateEnvVar","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"/workspace/apps/web/src/components/manifest-editor.tsx:updateEnvVar","kind":"defines"},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:updateEnvVar","to":"symbol-name:setEnvVars","kind":"calls","metadata":{"unresolvedName":"setEnvVars"}},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"/workspace/apps/web/src/components/manifest-editor.tsx:updateBuildScript","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"/workspace/apps/web/src/components/manifest-editor.tsx:updateBuildScript","kind":"defines"},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:updateBuildScript","to":"symbol-name:setBuildScripts","kind":"calls","metadata":{"unresolvedName":"setBuildScripts"}},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"/workspace/apps/web/src/components/manifest-editor.tsx:updateStartupScript","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"/workspace/apps/web/src/components/manifest-editor.tsx:updateStartupScript","kind":"defines"},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:updateStartupScript","to":"symbol-name:setStartupScripts","kind":"calls","metadata":{"unresolvedName":"setStartupScripts"}},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"/workspace/apps/web/src/components/manifest-editor.tsx:updateMount","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"/workspace/apps/web/src/components/manifest-editor.tsx:updateMount","kind":"defines"},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:updateMount","to":"symbol-name:setMounts","kind":"calls","metadata":{"unresolvedName":"setMounts"}},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"/workspace/apps/web/src/components/manifest-editor.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"/workspace/apps/web/src/components/manifest-editor.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"symbol-name:setBaseDefinitionId","kind":"calls","metadata":{"unresolvedName":"setBaseDefinitionId"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"symbol-name:setBaseImage","kind":"calls","metadata":{"unresolvedName":"setBaseImage"}},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"/workspace/apps/web/src/components/manifest-editor.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/manifest-editor.tsx","to":"/workspace/apps/web/src/components/manifest-editor.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"symbol-name:setBaseImage","kind":"calls","metadata":{"unresolvedName":"setBaseImage"}},{"from":"/workspace/apps/web/src/components/manifest-editor.tsx:","to":"symbol-name:setBaseDefinitionId","kind":"calls","metadata":{"unresolvedName":"setBaseDefinitionId"}},{"from":"file:/workspace/apps/web/src/components/mobile-action-sheet.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/mobile-action-sheet.tsx","to":"external:@/components/ui/Icon","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/mobile-action-sheet.tsx","to":"external:@/components/ui/Icon","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/mobile-action-sheet.tsx","to":"/workspace/apps/web/src/components/mobile-action-sheet.tsx:MobileActionSheet","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/mobile-action-sheet.tsx","to":"/workspace/apps/web/src/components/mobile-action-sheet.tsx:MobileActionSheet","kind":"defines"},{"from":"/workspace/apps/web/src/components/mobile-action-sheet.tsx:MobileActionSheet","to":"symbol-name:useRef","kind":"calls","metadata":{"unresolvedName":"useRef"}},{"from":"/workspace/apps/web/src/components/mobile-action-sheet.tsx:MobileActionSheet","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/components/mobile-action-sheet.tsx:MobileActionSheet","to":"symbol-name:onClose","kind":"calls","metadata":{"unresolvedName":"onClose"}},{"from":"/workspace/apps/web/src/components/mobile-action-sheet.tsx:MobileActionSheet","to":"external:document.addEventListener","kind":"calls","metadata":{"unresolvedName":"document.addEventListener"}},{"from":"/workspace/apps/web/src/components/mobile-action-sheet.tsx:MobileActionSheet","to":"external:document.removeEventListener","kind":"calls","metadata":{"unresolvedName":"document.removeEventListener"}},{"from":"/workspace/apps/web/src/components/mobile-action-sheet.tsx:MobileActionSheet","to":"external:e.stopPropagation","kind":"calls","metadata":{"unresolvedName":"e.stopPropagation"}},{"from":"/workspace/apps/web/src/components/mobile-action-sheet.tsx:MobileActionSheet","to":"external:actions.map","kind":"calls","metadata":{"unresolvedName":"actions.map"}},{"from":"/workspace/apps/web/src/components/mobile-action-sheet.tsx:MobileActionSheet","to":"external:action.onClick","kind":"calls","metadata":{"unresolvedName":"action.onClick"}},{"from":"file:/workspace/apps/web/src/components/mobile-action-sheet.tsx","to":"/workspace/apps/web/src/components/mobile-action-sheet.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/mobile-action-sheet.tsx","to":"/workspace/apps/web/src/components/mobile-action-sheet.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/components/mobile-action-sheet.tsx","to":"/workspace/apps/web/src/components/mobile-action-sheet.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/mobile-action-sheet.tsx","to":"/workspace/apps/web/src/components/mobile-action-sheet.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/components/mobile-action-sheet.tsx","to":"/workspace/apps/web/src/components/mobile-action-sheet.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/mobile-action-sheet.tsx","to":"/workspace/apps/web/src/components/mobile-action-sheet.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/mobile-action-sheet.tsx:","to":"symbol-name:onClose","kind":"calls","metadata":{"unresolvedName":"onClose"}},{"from":"/workspace/apps/web/src/components/mobile-action-sheet.tsx:","to":"external:document.addEventListener","kind":"calls","metadata":{"unresolvedName":"document.addEventListener"}},{"from":"/workspace/apps/web/src/components/mobile-action-sheet.tsx:","to":"external:document.removeEventListener","kind":"calls","metadata":{"unresolvedName":"document.removeEventListener"}},{"from":"file:/workspace/apps/web/src/components/mobile-action-sheet.tsx","to":"/workspace/apps/web/src/components/mobile-action-sheet.tsx:handleEscape","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/mobile-action-sheet.tsx","to":"/workspace/apps/web/src/components/mobile-action-sheet.tsx:handleEscape","kind":"defines"},{"from":"/workspace/apps/web/src/components/mobile-action-sheet.tsx:handleEscape","to":"symbol-name:onClose","kind":"calls","metadata":{"unresolvedName":"onClose"}},{"from":"file:/workspace/apps/web/src/components/mobile-action-sheet.tsx","to":"/workspace/apps/web/src/components/mobile-action-sheet.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/mobile-action-sheet.tsx","to":"/workspace/apps/web/src/components/mobile-action-sheet.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/mobile-action-sheet.tsx:","to":"external:action.onClick","kind":"calls","metadata":{"unresolvedName":"action.onClick"}},{"from":"/workspace/apps/web/src/components/mobile-action-sheet.tsx:","to":"symbol-name:onClose","kind":"calls","metadata":{"unresolvedName":"onClose"}},{"from":"file:/workspace/apps/web/src/components/mobile-detail-view.tsx","to":"external:@/components/ui/Icon","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/mobile-detail-view.tsx","to":"/workspace/apps/web/src/components/mobile-detail-view.tsx:MobileDetailView","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/mobile-detail-view.tsx","to":"/workspace/apps/web/src/components/mobile-detail-view.tsx:MobileDetailView","kind":"defines"},{"from":"/workspace/apps/web/src/components/mobile-detail-view.tsx:MobileDetailView","to":"external:JSON.stringify","kind":"calls","metadata":{"unresolvedName":"JSON.stringify"}},{"from":"/workspace/apps/web/src/components/mobile-detail-view.tsx:MobileDetailView","to":"symbol-name:String","kind":"calls","metadata":{"unresolvedName":"String"}},{"from":"/workspace/apps/web/src/components/mobile-detail-view.tsx:MobileDetailView","to":"external:fields.map","kind":"calls","metadata":{"unresolvedName":"fields.map"}},{"from":"/workspace/apps/web/src/components/mobile-detail-view.tsx:MobileDetailView","to":"/workspace/apps/web/src/components/mobile-detail-view.tsx:renderValue","kind":"calls","metadata":{"unresolvedName":"renderValue"}},{"from":"file:/workspace/apps/web/src/components/mobile-detail-view.tsx","to":"/workspace/apps/web/src/components/mobile-detail-view.tsx:renderValue","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/mobile-detail-view.tsx","to":"/workspace/apps/web/src/components/mobile-detail-view.tsx:renderValue","kind":"defines"},{"from":"/workspace/apps/web/src/components/mobile-detail-view.tsx:renderValue","to":"external:JSON.stringify","kind":"calls","metadata":{"unresolvedName":"JSON.stringify"}},{"from":"/workspace/apps/web/src/components/mobile-detail-view.tsx:renderValue","to":"symbol-name:String","kind":"calls","metadata":{"unresolvedName":"String"}},{"from":"file:/workspace/apps/web/src/components/mobile-edit-view.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/mobile-edit-view.tsx","to":"/workspace/apps/web/src/components/mobile-edit-view.tsx:MobileEditView","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/mobile-edit-view.tsx","to":"/workspace/apps/web/src/components/mobile-edit-view.tsx:MobileEditView","kind":"defines"},{"from":"/workspace/apps/web/src/components/mobile-edit-view.tsx:MobileEditView","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/mobile-edit-view.tsx:MobileEditView","to":"external:fields?.forEach","kind":"calls","metadata":{"unresolvedName":"fields?.forEach"}},{"from":"/workspace/apps/web/src/components/mobile-edit-view.tsx:MobileEditView","to":"symbol-name:setFormData","kind":"calls","metadata":{"unresolvedName":"setFormData"}},{"from":"/workspace/apps/web/src/components/mobile-edit-view.tsx:MobileEditView","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/components/mobile-edit-view.tsx:MobileEditView","to":"symbol-name:onSave","kind":"calls","metadata":{"unresolvedName":"onSave"}},{"from":"/workspace/apps/web/src/components/mobile-edit-view.tsx:MobileEditView","to":"external:fields?.map","kind":"calls","metadata":{"unresolvedName":"fields?.map"}},{"from":"/workspace/apps/web/src/components/mobile-edit-view.tsx:MobileEditView","to":"symbol-name:String","kind":"calls","metadata":{"unresolvedName":"String"}},{"from":"/workspace/apps/web/src/components/mobile-edit-view.tsx:MobileEditView","to":"symbol-name:handleChange","kind":"calls","metadata":{"unresolvedName":"handleChange"}},{"from":"/workspace/apps/web/src/components/mobile-edit-view.tsx:MobileEditView","to":"external:field.options?.map","kind":"calls","metadata":{"unresolvedName":"field.options?.map"}},{"from":"/workspace/apps/web/src/components/mobile-edit-view.tsx:MobileEditView","to":"symbol-name:Boolean","kind":"calls","metadata":{"unresolvedName":"Boolean"}},{"from":"/workspace/apps/web/src/components/mobile-edit-view.tsx:MobileEditView","to":"symbol-name:Number","kind":"calls","metadata":{"unresolvedName":"Number"}},{"from":"file:/workspace/apps/web/src/components/mobile-edit-view.tsx","to":"/workspace/apps/web/src/components/mobile-edit-view.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/mobile-edit-view.tsx","to":"/workspace/apps/web/src/components/mobile-edit-view.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/mobile-edit-view.tsx:","to":"external:fields?.forEach","kind":"calls","metadata":{"unresolvedName":"fields?.forEach"}},{"from":"file:/workspace/apps/web/src/components/mobile-edit-view.tsx","to":"/workspace/apps/web/src/components/mobile-edit-view.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/mobile-edit-view.tsx","to":"/workspace/apps/web/src/components/mobile-edit-view.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/components/mobile-edit-view.tsx","to":"/workspace/apps/web/src/components/mobile-edit-view.tsx:handleChange","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/mobile-edit-view.tsx","to":"/workspace/apps/web/src/components/mobile-edit-view.tsx:handleChange","kind":"defines"},{"from":"/workspace/apps/web/src/components/mobile-edit-view.tsx:handleChange","to":"symbol-name:setFormData","kind":"calls","metadata":{"unresolvedName":"setFormData"}},{"from":"file:/workspace/apps/web/src/components/mobile-edit-view.tsx","to":"/workspace/apps/web/src/components/mobile-edit-view.tsx:handleSubmit","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/mobile-edit-view.tsx","to":"/workspace/apps/web/src/components/mobile-edit-view.tsx:handleSubmit","kind":"defines"},{"from":"/workspace/apps/web/src/components/mobile-edit-view.tsx:handleSubmit","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/components/mobile-edit-view.tsx:handleSubmit","to":"symbol-name:onSave","kind":"calls","metadata":{"unresolvedName":"onSave"}},{"from":"file:/workspace/apps/web/src/components/mobile-fab.tsx","to":"external:@/components/ui/Icon","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/mobile-fab.tsx","to":"/workspace/apps/web/src/components/mobile-fab.tsx:MobileFAB","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/mobile-fab.tsx","to":"/workspace/apps/web/src/components/mobile-fab.tsx:MobileFAB","kind":"defines"},{"from":"file:/workspace/apps/web/src/components/mobile-list-view.tsx","to":"external:@/components/ui/Icon","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/mobile-list-view.tsx","to":"file:/workspace/apps/web/src/utils/icons.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/mobile-list-view.tsx","to":"/workspace/apps/web/src/components/mobile-list-view.tsx:MobileListView","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/mobile-list-view.tsx","to":"/workspace/apps/web/src/components/mobile-list-view.tsx:MobileListView","kind":"defines"},{"from":"/workspace/apps/web/src/components/mobile-list-view.tsx:MobileListView","to":"symbol-name:onSearch","kind":"calls","metadata":{"unresolvedName":"onSearch"}},{"from":"/workspace/apps/web/src/components/mobile-list-view.tsx:MobileListView","to":"external:items.map","kind":"calls","metadata":{"unresolvedName":"items.map"}},{"from":"/workspace/apps/web/src/components/mobile-list-view.tsx:MobileListView","to":"symbol-name:onItemClick","kind":"calls","metadata":{"unresolvedName":"onItemClick"}},{"from":"file:/workspace/apps/web/src/components/mobile-nav.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/mobile-nav.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/mobile-nav.tsx","to":"external:@/components/ui/Icon","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/mobile-nav.tsx","to":"file:/workspace/apps/web/src/components/tools-bottom-sheet.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/mobile-nav.tsx","to":"file:/workspace/apps/web/src/utils/icons.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/mobile-nav.tsx","to":"/workspace/apps/web/src/components/mobile-nav.tsx:MobileNav","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/mobile-nav.tsx","to":"/workspace/apps/web/src/components/mobile-nav.tsx:MobileNav","kind":"defines"},{"from":"/workspace/apps/web/src/components/mobile-nav.tsx:MobileNav","to":"symbol-name:useLocation","kind":"calls","metadata":{"unresolvedName":"useLocation"}},{"from":"/workspace/apps/web/src/components/mobile-nav.tsx:MobileNav","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/mobile-nav.tsx:MobileNav","to":"symbol-name:setToolsSheetOpen","kind":"calls","metadata":{"unresolvedName":"setToolsSheetOpen"}},{"from":"/workspace/apps/web/src/components/mobile-nav.tsx:MobileNav","to":"external:MOBILE_NAV_ITEMS.map","kind":"calls","metadata":{"unresolvedName":"MOBILE_NAV_ITEMS.map"}},{"from":"/workspace/apps/web/src/components/mobile-nav.tsx:MobileNav","to":"/workspace/apps/web/src/components/mobile-nav.tsx:handleNavClick","kind":"calls","metadata":{"unresolvedName":"handleNavClick"}},{"from":"file:/workspace/apps/web/src/components/mobile-nav.tsx","to":"/workspace/apps/web/src/components/mobile-nav.tsx:handleNavClick","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/mobile-nav.tsx","to":"/workspace/apps/web/src/components/mobile-nav.tsx:handleNavClick","kind":"defines"},{"from":"/workspace/apps/web/src/components/mobile-nav.tsx:handleNavClick","to":"symbol-name:setToolsSheetOpen","kind":"calls","metadata":{"unresolvedName":"setToolsSheetOpen"}},{"from":"file:/workspace/apps/web/src/components/mobile-nav.tsx","to":"/workspace/apps/web/src/components/mobile-nav.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/mobile-nav.tsx","to":"/workspace/apps/web/src/components/mobile-nav.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/mobile-nav.tsx:","to":"/workspace/apps/web/src/components/mobile-nav.tsx:handleNavClick","kind":"calls","metadata":{"unresolvedName":"handleNavClick"}},{"from":"file:/workspace/apps/web/src/components/mobile-page-header.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/mobile-page-header.tsx","to":"file:/workspace/apps/web/src/hooks/use-mobile-viewport.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/mobile-page-header.tsx","to":"external:@/components/ui/Icon","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/mobile-page-header.tsx","to":"/workspace/apps/web/src/components/mobile-page-header.tsx:MobilePageHeader","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/mobile-page-header.tsx","to":"/workspace/apps/web/src/components/mobile-page-header.tsx:MobilePageHeader","kind":"defines"},{"from":"/workspace/apps/web/src/components/mobile-page-header.tsx:MobilePageHeader","to":"symbol-name:useNavigate","kind":"calls","metadata":{"unresolvedName":"useNavigate"}},{"from":"/workspace/apps/web/src/components/mobile-page-header.tsx:MobilePageHeader","to":"/workspace/apps/web/src/hooks/use-mobile-viewport.ts:useMobileViewport","kind":"calls","metadata":{"unresolvedName":"useMobileViewport"}},{"from":"/workspace/apps/web/src/components/mobile-page-header.tsx:MobilePageHeader","to":"symbol-name:navigate","kind":"calls","metadata":{"unresolvedName":"navigate"}},{"from":"file:/workspace/apps/web/src/components/mobile-terminal-header.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/mobile-terminal-header.tsx","to":"external:@/components/ui/Icon","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/mobile-terminal-header.tsx","to":"/workspace/apps/web/src/components/mobile-terminal-header.tsx:MobileTerminalHeader","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/mobile-terminal-header.tsx","to":"/workspace/apps/web/src/components/mobile-terminal-header.tsx:MobileTerminalHeader","kind":"defines"},{"from":"/workspace/apps/web/src/components/mobile-terminal-header.tsx:MobileTerminalHeader","to":"symbol-name:onFontSizeChange","kind":"calls","metadata":{"unresolvedName":"onFontSizeChange"}},{"from":"file:/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx","to":"external:@/components/features/terminal/TerminalComponent","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx","to":"file:/workspace/apps/web/src/components/mobile-terminal-header.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx","to":"file:/workspace/apps/web/src/components/special-keys-strip.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx","to":"file:/workspace/apps/web/src/components/special-keys-panel.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx","to":"file:/workspace/apps/web/src/hooks/use-mobile-viewport.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx","to":"file:/workspace/apps/web/src/hooks/use-virtual-keyboard.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx","to":"file:/workspace/apps/web/src/hooks/use-auto-hide.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx","to":"file:/workspace/apps/web/src/hooks/use-special-keys.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx","to":"/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx:MobileTerminalWrapper","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx","to":"/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx:MobileTerminalWrapper","kind":"defines"},{"from":"/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx:MobileTerminalWrapper","to":"/workspace/apps/web/src/hooks/use-mobile-viewport.ts:useMobileViewport","kind":"calls","metadata":{"unresolvedName":"useMobileViewport"}},{"from":"/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx:MobileTerminalWrapper","to":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:useVirtualKeyboard","kind":"calls","metadata":{"unresolvedName":"useVirtualKeyboard"}},{"from":"/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx:MobileTerminalWrapper","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx:MobileTerminalWrapper","to":"/workspace/apps/web/src/hooks/use-auto-hide.ts:useAutoHide","kind":"calls","metadata":{"unresolvedName":"useAutoHide"}},{"from":"/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx:MobileTerminalWrapper","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx:MobileTerminalWrapper","to":"external:headerAutoHide.toggle","kind":"calls","metadata":{"unresolvedName":"headerAutoHide.toggle"}},{"from":"/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx:MobileTerminalWrapper","to":"symbol-name:setTerminalRef","kind":"calls","metadata":{"unresolvedName":"setTerminalRef"}},{"from":"/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx:MobileTerminalWrapper","to":"external:terminalRef?.sendData","kind":"calls","metadata":{"unresolvedName":"terminalRef?.sendData"}},{"from":"/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx:MobileTerminalWrapper","to":"external:terminalRef?.changeFontSize","kind":"calls","metadata":{"unresolvedName":"terminalRef?.changeFontSize"}},{"from":"/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx:MobileTerminalWrapper","to":"symbol-name:setShowPanel","kind":"calls","metadata":{"unresolvedName":"setShowPanel"}},{"from":"/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx:MobileTerminalWrapper","to":"external:terminalRef?.focusInput","kind":"calls","metadata":{"unresolvedName":"terminalRef?.focusInput"}},{"from":"file:/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx","to":"/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx","to":"/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx:","to":"external:headerAutoHide.toggle","kind":"calls","metadata":{"unresolvedName":"headerAutoHide.toggle"}},{"from":"file:/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx","to":"/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx","to":"/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx:","to":"symbol-name:setTerminalRef","kind":"calls","metadata":{"unresolvedName":"setTerminalRef"}},{"from":"file:/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx","to":"/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx","to":"/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/mobile-terminal-wrapper.tsx:","to":"external:terminalRef?.sendData","kind":"calls","metadata":{"unresolvedName":"terminalRef?.sendData"}},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"external:vitest","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"external:@testing-library/react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"file:/workspace/apps/web/src/components/notification-center.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"file:/workspace/apps/web/src/state/notifications.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"file:/workspace/apps/web/src/api/notifications.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"/workspace/apps/web/src/components/notification-center.test.tsx:wrapper","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"/workspace/apps/web/src/components/notification-center.test.tsx:wrapper","kind":"defines"},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"/workspace/apps/web/src/components/notification-center.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"/workspace/apps/web/src/components/notification-center.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:beforeEach","kind":"calls","metadata":{"unresolvedName":"beforeEach"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:vi.useFakeTimers","kind":"calls","metadata":{"unresolvedName":"vi.useFakeTimers"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:mockedGetNotifications.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetNotifications.mockResolvedValue"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:mockedGetUnreadCount.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetUnreadCount.mockResolvedValue"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:afterEach","kind":"calls","metadata":{"unresolvedName":"afterEach"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:vi.useRealTimers","kind":"calls","metadata":{"unresolvedName":"vi.useRealTimers"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:vi.clearAllMocks","kind":"calls","metadata":{"unresolvedName":"vi.clearAllMocks"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:cleanup","kind":"calls","metadata":{"unresolvedName":"cleanup"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:vi.advanceTimersByTimeAsync","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTimeAsync"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:expect(screen.getByText(\"3\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"3\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:expect(screen.queryByText(\"0\")).not.toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.queryByText(\"0\")).not.toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:screen.queryByText","kind":"calls","metadata":{"unresolvedName":"screen.queryByText"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:expect(screen.getByRole(\"dialog\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByRole(\"dialog\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:fireEvent.mouseDown","kind":"calls","metadata":{"unresolvedName":"fireEvent.mouseDown"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:screen.getByTestId","kind":"calls","metadata":{"unresolvedName":"screen.getByTestId"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:expect(screen.queryByRole(\"dialog\")).not.toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.queryByRole(\"dialog\")).not.toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:screen.queryByRole","kind":"calls","metadata":{"unresolvedName":"screen.queryByRole"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:fireEvent.keyDown","kind":"calls","metadata":{"unresolvedName":"fireEvent.keyDown"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:expect(screen.getByText(\"No notifications\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"No notifications\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:makeNotification","kind":"calls","metadata":{"unresolvedName":"makeNotification"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:expect(screen.getByText(\"Notification 1\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Notification 1\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:expect(screen.getByText(\"Notification 2\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Notification 2\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:import","kind":"calls","metadata":{"unresolvedName":"import"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:expect(vi.mocked(mockMarkAll)).toHaveBeenCalled","kind":"calls","metadata":{"unresolvedName":"expect(vi.mocked(mockMarkAll)).toHaveBeenCalled"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:vi.mocked","kind":"calls","metadata":{"unresolvedName":"vi.mocked"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:expect(vi.mocked(mockClearAll)).toHaveBeenCalled","kind":"calls","metadata":{"unresolvedName":"expect(vi.mocked(mockClearAll)).toHaveBeenCalled"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:expect(mockedGetNotifications).toHaveBeenCalled","kind":"calls","metadata":{"unresolvedName":"expect(mockedGetNotifications).toHaveBeenCalled"}},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"/workspace/apps/web/src/components/notification-center.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"/workspace/apps/web/src/components/notification-center.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:vi.useFakeTimers","kind":"calls","metadata":{"unresolvedName":"vi.useFakeTimers"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:mockedGetNotifications.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetNotifications.mockResolvedValue"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:mockedGetUnreadCount.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetUnreadCount.mockResolvedValue"}},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"/workspace/apps/web/src/components/notification-center.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"/workspace/apps/web/src/components/notification-center.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:vi.useRealTimers","kind":"calls","metadata":{"unresolvedName":"vi.useRealTimers"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:vi.clearAllMocks","kind":"calls","metadata":{"unresolvedName":"vi.clearAllMocks"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:cleanup","kind":"calls","metadata":{"unresolvedName":"cleanup"}},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"/workspace/apps/web/src/components/notification-center.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"/workspace/apps/web/src/components/notification-center.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"/workspace/apps/web/src/components/notification-center.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"/workspace/apps/web/src/components/notification-center.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:mockedGetUnreadCount.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetUnreadCount.mockResolvedValue"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:vi.advanceTimersByTimeAsync","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTimeAsync"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:expect(screen.getByText(\"3\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"3\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"/workspace/apps/web/src/components/notification-center.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"/workspace/apps/web/src/components/notification-center.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:expect(screen.queryByText(\"0\")).not.toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.queryByText(\"0\")).not.toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:screen.queryByText","kind":"calls","metadata":{"unresolvedName":"screen.queryByText"}},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"/workspace/apps/web/src/components/notification-center.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"/workspace/apps/web/src/components/notification-center.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:expect(screen.getByRole(\"dialog\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByRole(\"dialog\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"/workspace/apps/web/src/components/notification-center.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"/workspace/apps/web/src/components/notification-center.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:expect(screen.getByRole(\"dialog\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByRole(\"dialog\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:fireEvent.mouseDown","kind":"calls","metadata":{"unresolvedName":"fireEvent.mouseDown"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:screen.getByTestId","kind":"calls","metadata":{"unresolvedName":"screen.getByTestId"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:expect(screen.queryByRole(\"dialog\")).not.toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.queryByRole(\"dialog\")).not.toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:screen.queryByRole","kind":"calls","metadata":{"unresolvedName":"screen.queryByRole"}},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"/workspace/apps/web/src/components/notification-center.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"/workspace/apps/web/src/components/notification-center.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:expect(screen.getByRole(\"dialog\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByRole(\"dialog\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:fireEvent.keyDown","kind":"calls","metadata":{"unresolvedName":"fireEvent.keyDown"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:expect(screen.queryByRole(\"dialog\")).not.toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.queryByRole(\"dialog\")).not.toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:screen.queryByRole","kind":"calls","metadata":{"unresolvedName":"screen.queryByRole"}},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"/workspace/apps/web/src/components/notification-center.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"/workspace/apps/web/src/components/notification-center.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:expect(screen.getByText(\"No notifications\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"No notifications\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"/workspace/apps/web/src/components/notification-center.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"/workspace/apps/web/src/components/notification-center.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:mockedGetNotifications.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetNotifications.mockResolvedValue"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:makeNotification","kind":"calls","metadata":{"unresolvedName":"makeNotification"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:vi.advanceTimersByTimeAsync","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTimeAsync"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:expect(screen.getByText(\"Notification 1\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Notification 1\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:expect(screen.getByText(\"Notification 2\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Notification 2\")).toBeInTheDocument"}},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"/workspace/apps/web/src/components/notification-center.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"/workspace/apps/web/src/components/notification-center.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:mockedGetNotifications.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetNotifications.mockResolvedValue"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:makeNotification","kind":"calls","metadata":{"unresolvedName":"makeNotification"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:vi.advanceTimersByTimeAsync","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTimeAsync"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:import","kind":"calls","metadata":{"unresolvedName":"import"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:expect(vi.mocked(mockMarkAll)).toHaveBeenCalled","kind":"calls","metadata":{"unresolvedName":"expect(vi.mocked(mockMarkAll)).toHaveBeenCalled"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:vi.mocked","kind":"calls","metadata":{"unresolvedName":"vi.mocked"}},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"/workspace/apps/web/src/components/notification-center.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"/workspace/apps/web/src/components/notification-center.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:mockedGetNotifications.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetNotifications.mockResolvedValue"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:makeNotification","kind":"calls","metadata":{"unresolvedName":"makeNotification"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:vi.advanceTimersByTimeAsync","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTimeAsync"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:import","kind":"calls","metadata":{"unresolvedName":"import"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:expect(vi.mocked(mockClearAll)).toHaveBeenCalled","kind":"calls","metadata":{"unresolvedName":"expect(vi.mocked(mockClearAll)).toHaveBeenCalled"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:vi.mocked","kind":"calls","metadata":{"unresolvedName":"vi.mocked"}},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"/workspace/apps/web/src/components/notification-center.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-center.test.tsx","to":"/workspace/apps/web/src/components/notification-center.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:vi.advanceTimersByTimeAsync","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTimeAsync"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"external:expect(mockedGetNotifications).toHaveBeenCalled","kind":"calls","metadata":{"unresolvedName":"expect(mockedGetNotifications).toHaveBeenCalled"}},{"from":"/workspace/apps/web/src/components/notification-center.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/components/notification-center.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/notification-center.tsx","to":"file:/workspace/apps/web/src/hooks/use-notifications.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/notification-center.tsx","to":"file:/workspace/apps/web/src/components/notification-item.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/notification-center.tsx","to":"external:@/components/ui/Icon","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/notification-center.tsx","to":"/workspace/apps/web/src/components/notification-center.tsx:NotificationCenter","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-center.tsx","to":"/workspace/apps/web/src/components/notification-center.tsx:NotificationCenter","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-center.tsx:NotificationCenter","to":"/workspace/apps/web/src/hooks/use-notifications.ts:useNotifications","kind":"calls","metadata":{"unresolvedName":"useNotifications"}},{"from":"/workspace/apps/web/src/components/notification-center.tsx:NotificationCenter","to":"symbol-name:useRef","kind":"calls","metadata":{"unresolvedName":"useRef"}},{"from":"/workspace/apps/web/src/components/notification-center.tsx:NotificationCenter","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/components/notification-center.tsx:NotificationCenter","to":"external:dropdownRef.current.contains","kind":"calls","metadata":{"unresolvedName":"dropdownRef.current.contains"}},{"from":"/workspace/apps/web/src/components/notification-center.tsx:NotificationCenter","to":"symbol-name:setIsDropdownOpen","kind":"calls","metadata":{"unresolvedName":"setIsDropdownOpen"}},{"from":"/workspace/apps/web/src/components/notification-center.tsx:NotificationCenter","to":"external:document.addEventListener","kind":"calls","metadata":{"unresolvedName":"document.addEventListener"}},{"from":"/workspace/apps/web/src/components/notification-center.tsx:NotificationCenter","to":"external:document.removeEventListener","kind":"calls","metadata":{"unresolvedName":"document.removeEventListener"}},{"from":"/workspace/apps/web/src/components/notification-center.tsx:NotificationCenter","to":"symbol-name:refreshList","kind":"calls","metadata":{"unresolvedName":"refreshList"}},{"from":"/workspace/apps/web/src/components/notification-center.tsx:NotificationCenter","to":"symbol-name:String","kind":"calls","metadata":{"unresolvedName":"String"}},{"from":"/workspace/apps/web/src/components/notification-center.tsx:NotificationCenter","to":"external:notifications.map","kind":"calls","metadata":{"unresolvedName":"notifications.map"}},{"from":"/workspace/apps/web/src/components/notification-center.tsx:NotificationCenter","to":"symbol-name:markAllRead","kind":"calls","metadata":{"unresolvedName":"markAllRead"}},{"from":"/workspace/apps/web/src/components/notification-center.tsx:NotificationCenter","to":"symbol-name:clearAll","kind":"calls","metadata":{"unresolvedName":"clearAll"}},{"from":"file:/workspace/apps/web/src/components/notification-center.tsx","to":"/workspace/apps/web/src/components/notification-center.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-center.tsx","to":"/workspace/apps/web/src/components/notification-center.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-center.tsx:","to":"external:dropdownRef.current.contains","kind":"calls","metadata":{"unresolvedName":"dropdownRef.current.contains"}},{"from":"/workspace/apps/web/src/components/notification-center.tsx:","to":"symbol-name:setIsDropdownOpen","kind":"calls","metadata":{"unresolvedName":"setIsDropdownOpen"}},{"from":"/workspace/apps/web/src/components/notification-center.tsx:","to":"external:document.addEventListener","kind":"calls","metadata":{"unresolvedName":"document.addEventListener"}},{"from":"/workspace/apps/web/src/components/notification-center.tsx:","to":"external:document.removeEventListener","kind":"calls","metadata":{"unresolvedName":"document.removeEventListener"}},{"from":"file:/workspace/apps/web/src/components/notification-center.tsx","to":"/workspace/apps/web/src/components/notification-center.tsx:handleMouseDown","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-center.tsx","to":"/workspace/apps/web/src/components/notification-center.tsx:handleMouseDown","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-center.tsx:handleMouseDown","to":"external:dropdownRef.current.contains","kind":"calls","metadata":{"unresolvedName":"dropdownRef.current.contains"}},{"from":"/workspace/apps/web/src/components/notification-center.tsx:handleMouseDown","to":"symbol-name:setIsDropdownOpen","kind":"calls","metadata":{"unresolvedName":"setIsDropdownOpen"}},{"from":"file:/workspace/apps/web/src/components/notification-center.tsx","to":"/workspace/apps/web/src/components/notification-center.tsx:handleKeyDown","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-center.tsx","to":"/workspace/apps/web/src/components/notification-center.tsx:handleKeyDown","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-center.tsx:handleKeyDown","to":"symbol-name:setIsDropdownOpen","kind":"calls","metadata":{"unresolvedName":"setIsDropdownOpen"}},{"from":"file:/workspace/apps/web/src/components/notification-center.tsx","to":"/workspace/apps/web/src/components/notification-center.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-center.tsx","to":"/workspace/apps/web/src/components/notification-center.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-center.tsx:","to":"external:document.removeEventListener","kind":"calls","metadata":{"unresolvedName":"document.removeEventListener"}},{"from":"file:/workspace/apps/web/src/components/notification-center.tsx","to":"/workspace/apps/web/src/components/notification-center.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-center.tsx","to":"/workspace/apps/web/src/components/notification-center.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-center.tsx:","to":"symbol-name:refreshList","kind":"calls","metadata":{"unresolvedName":"refreshList"}},{"from":"file:/workspace/apps/web/src/components/notification-center.tsx","to":"/workspace/apps/web/src/components/notification-center.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-center.tsx","to":"/workspace/apps/web/src/components/notification-center.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-center.tsx:","to":"symbol-name:markAllRead","kind":"calls","metadata":{"unresolvedName":"markAllRead"}},{"from":"file:/workspace/apps/web/src/components/notification-center.tsx","to":"/workspace/apps/web/src/components/notification-center.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-center.tsx","to":"/workspace/apps/web/src/components/notification-center.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-center.tsx:","to":"symbol-name:clearAll","kind":"calls","metadata":{"unresolvedName":"clearAll"}},{"from":"file:/workspace/apps/web/src/components/notification-item.test.tsx","to":"external:vitest","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/notification-item.test.tsx","to":"external:@testing-library/react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/notification-item.test.tsx","to":"file:/workspace/apps/web/src/components/notification-item.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/notification-item.test.tsx","to":"/workspace/apps/web/src/components/notification-item.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-item.test.tsx","to":"/workspace/apps/web/src/components/notification-item.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"symbol-name:cleanup","kind":"calls","metadata":{"unresolvedName":"cleanup"}},{"from":"file:/workspace/apps/web/src/components/notification-item.test.tsx","to":"/workspace/apps/web/src/components/notification-item.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-item.test.tsx","to":"/workspace/apps/web/src/components/notification-item.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"symbol-name:makeNotification","kind":"calls","metadata":{"unresolvedName":"makeNotification"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:expect(screen.getByText(\"Container started\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Container started\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:expect(screen.getByText(/ago|just now/)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/ago|just now/)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:expect(row.className).toContain","kind":"calls","metadata":{"unresolvedName":"expect(row.className).toContain"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:expect(onMarkRead).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(onMarkRead).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:expect(onDismiss).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(onDismiss).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:expect(screen.getByRole(\"img\", { hidden: true })).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByRole(\"img\", { hidden: true })).toBeInTheDocument"}},{"from":"file:/workspace/apps/web/src/components/notification-item.test.tsx","to":"/workspace/apps/web/src/components/notification-item.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-item.test.tsx","to":"/workspace/apps/web/src/components/notification-item.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"symbol-name:makeNotification","kind":"calls","metadata":{"unresolvedName":"makeNotification"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:expect(screen.getByText(\"Container started\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Container started\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:expect(screen.getByText(/ago|just now/)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/ago|just now/)).toBeInTheDocument"}},{"from":"file:/workspace/apps/web/src/components/notification-item.test.tsx","to":"/workspace/apps/web/src/components/notification-item.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-item.test.tsx","to":"/workspace/apps/web/src/components/notification-item.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"symbol-name:makeNotification","kind":"calls","metadata":{"unresolvedName":"makeNotification"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:expect(row.className).toContain","kind":"calls","metadata":{"unresolvedName":"expect(row.className).toContain"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/components/notification-item.test.tsx","to":"/workspace/apps/web/src/components/notification-item.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-item.test.tsx","to":"/workspace/apps/web/src/components/notification-item.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"symbol-name:makeNotification","kind":"calls","metadata":{"unresolvedName":"makeNotification"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:expect(row.className).toContain","kind":"calls","metadata":{"unresolvedName":"expect(row.className).toContain"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/components/notification-item.test.tsx","to":"/workspace/apps/web/src/components/notification-item.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-item.test.tsx","to":"/workspace/apps/web/src/components/notification-item.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"symbol-name:makeNotification","kind":"calls","metadata":{"unresolvedName":"makeNotification"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:expect(onMarkRead).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(onMarkRead).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/components/notification-item.test.tsx","to":"/workspace/apps/web/src/components/notification-item.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-item.test.tsx","to":"/workspace/apps/web/src/components/notification-item.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"symbol-name:makeNotification","kind":"calls","metadata":{"unresolvedName":"makeNotification"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:expect(onDismiss).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(onDismiss).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/components/notification-item.test.tsx","to":"/workspace/apps/web/src/components/notification-item.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-item.test.tsx","to":"/workspace/apps/web/src/components/notification-item.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"symbol-name:makeNotification","kind":"calls","metadata":{"unresolvedName":"makeNotification"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:expect(screen.getByRole(\"img\", { hidden: true })).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByRole(\"img\", { hidden: true })).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/notification-item.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"file:/workspace/apps/web/src/components/notification-item.tsx","to":"external:@/components/ui/Icon","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/notification-item.tsx","to":"file:/workspace/apps/web/src/utils/time.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/notification-item.tsx","to":"file:/workspace/apps/web/src/api/notifications.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/notification-item.tsx","to":"file:/workspace/apps/web/src/utils/icons.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/notification-item.tsx","to":"/workspace/apps/web/src/components/notification-item.tsx:NotificationItem","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/notification-item.tsx","to":"/workspace/apps/web/src/components/notification-item.tsx:NotificationItem","kind":"defines"},{"from":"/workspace/apps/web/src/components/notification-item.tsx:NotificationItem","to":"/workspace/apps/web/src/utils/time.ts:formatRelativeTime","kind":"calls","metadata":{"unresolvedName":"formatRelativeTime"}},{"from":"/workspace/apps/web/src/components/notification-item.tsx:NotificationItem","to":"symbol-name:onMarkRead","kind":"calls","metadata":{"unresolvedName":"onMarkRead"}},{"from":"/workspace/apps/web/src/components/notification-item.tsx:NotificationItem","to":"symbol-name:onDismiss","kind":"calls","metadata":{"unresolvedName":"onDismiss"}},{"from":"file:/workspace/apps/web/src/components/session-card.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/session-card.tsx","to":"file:/workspace/apps/web/src/api/sessions.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/session-card.tsx","to":"external:@/components/ui/Icon","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/session-card.tsx","to":"file:/workspace/apps/web/src/hooks/use-mobile-viewport.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/session-card.tsx","to":"file:/workspace/apps/web/src/components/mobile-action-sheet.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/session-card.tsx","to":"external:@/components/ui/Icon","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/session-card.tsx","to":"/workspace/apps/web/src/components/session-card.tsx:SessionCard","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/session-card.tsx","to":"/workspace/apps/web/src/components/session-card.tsx:SessionCard","kind":"defines"},{"from":"/workspace/apps/web/src/components/session-card.tsx:SessionCard","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/session-card.tsx:SessionCard","to":"/workspace/apps/web/src/hooks/use-mobile-viewport.ts:useMobileViewport","kind":"calls","metadata":{"unresolvedName":"useMobileViewport"}},{"from":"/workspace/apps/web/src/components/session-card.tsx:SessionCard","to":"external:session.tool_type_interfaces?.includes","kind":"calls","metadata":{"unresolvedName":"session.tool_type_interfaces?.includes"}},{"from":"/workspace/apps/web/src/components/session-card.tsx:SessionCard","to":"symbol-name:setShowStopConfirm","kind":"calls","metadata":{"unresolvedName":"setShowStopConfirm"}},{"from":"/workspace/apps/web/src/components/session-card.tsx:SessionCard","to":"symbol-name:onStop","kind":"calls","metadata":{"unresolvedName":"onStop"}},{"from":"/workspace/apps/web/src/components/session-card.tsx:SessionCard","to":"symbol-name:setShowDeleteConfirm","kind":"calls","metadata":{"unresolvedName":"setShowDeleteConfirm"}},{"from":"/workspace/apps/web/src/components/session-card.tsx:SessionCard","to":"symbol-name:onDelete","kind":"calls","metadata":{"unresolvedName":"onDelete"}},{"from":"/workspace/apps/web/src/components/session-card.tsx:SessionCard","to":"external:new Date(session.created_at).toLocaleString","kind":"calls","metadata":{"unresolvedName":"new Date(session.created_at).toLocaleString"}},{"from":"/workspace/apps/web/src/components/session-card.tsx:SessionCard","to":"symbol-name:onOpen","kind":"calls","metadata":{"unresolvedName":"onOpen"}},{"from":"/workspace/apps/web/src/components/session-card.tsx:SessionCard","to":"symbol-name:setShowActionSheet","kind":"calls","metadata":{"unresolvedName":"setShowActionSheet"}},{"from":"/workspace/apps/web/src/components/session-card.tsx:SessionCard","to":"symbol-name:onStart","kind":"calls","metadata":{"unresolvedName":"onStart"}},{"from":"/workspace/apps/web/src/components/session-card.tsx:SessionCard","to":"symbol-name:onRecreateTunnel","kind":"calls","metadata":{"unresolvedName":"onRecreateTunnel"}},{"from":"file:/workspace/apps/web/src/components/session-card.tsx","to":"/workspace/apps/web/src/components/session-card.tsx:handleStop","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/session-card.tsx","to":"/workspace/apps/web/src/components/session-card.tsx:handleStop","kind":"defines"},{"from":"/workspace/apps/web/src/components/session-card.tsx:handleStop","to":"symbol-name:setShowStopConfirm","kind":"calls","metadata":{"unresolvedName":"setShowStopConfirm"}},{"from":"/workspace/apps/web/src/components/session-card.tsx:handleStop","to":"symbol-name:onStop","kind":"calls","metadata":{"unresolvedName":"onStop"}},{"from":"file:/workspace/apps/web/src/components/session-card.tsx","to":"/workspace/apps/web/src/components/session-card.tsx:handleDelete","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/session-card.tsx","to":"/workspace/apps/web/src/components/session-card.tsx:handleDelete","kind":"defines"},{"from":"/workspace/apps/web/src/components/session-card.tsx:handleDelete","to":"symbol-name:setShowDeleteConfirm","kind":"calls","metadata":{"unresolvedName":"setShowDeleteConfirm"}},{"from":"/workspace/apps/web/src/components/session-card.tsx:handleDelete","to":"symbol-name:onDelete","kind":"calls","metadata":{"unresolvedName":"onDelete"}},{"from":"file:/workspace/apps/web/src/components/session-list.tsx","to":"file:/workspace/apps/web/src/api/sessions.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/session-list.tsx","to":"file:/workspace/apps/web/src/components/session-card.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/session-list.tsx","to":"file:/workspace/apps/web/src/api/sessions.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/session-list.tsx","to":"/workspace/apps/web/src/components/session-list.tsx:SessionList","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/session-list.tsx","to":"/workspace/apps/web/src/components/session-list.tsx:SessionList","kind":"defines"},{"from":"/workspace/apps/web/src/components/session-list.tsx:SessionList","to":"external:sessions.filter","kind":"calls","metadata":{"unresolvedName":"sessions.filter"}},{"from":"/workspace/apps/web/src/components/session-list.tsx:SessionList","to":"external:activeStatuses.includes","kind":"calls","metadata":{"unresolvedName":"activeStatuses.includes"}},{"from":"/workspace/apps/web/src/components/session-list.tsx:SessionList","to":"external:sessions\n .filter((s) => recentStatuses.includes(s.status))\n .slice","kind":"calls","metadata":{"unresolvedName":"sessions\n .filter((s) => recentStatuses.includes(s.status))\n .slice"}},{"from":"/workspace/apps/web/src/components/session-list.tsx:SessionList","to":"external:sessions\n .filter","kind":"calls","metadata":{"unresolvedName":"sessions\n .filter"}},{"from":"/workspace/apps/web/src/components/session-list.tsx:SessionList","to":"external:recentStatuses.includes","kind":"calls","metadata":{"unresolvedName":"recentStatuses.includes"}},{"from":"/workspace/apps/web/src/components/session-list.tsx:SessionList","to":"external:sessions.map","kind":"calls","metadata":{"unresolvedName":"sessions.map"}},{"from":"/workspace/apps/web/src/components/session-list.tsx:SessionList","to":"external:activeSessions.map","kind":"calls","metadata":{"unresolvedName":"activeSessions.map"}},{"from":"/workspace/apps/web/src/components/session-list.tsx:SessionList","to":"external:recentSessions.map","kind":"calls","metadata":{"unresolvedName":"recentSessions.map"}},{"from":"file:/workspace/apps/web/src/components/special-keys-panel.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/special-keys-panel.tsx","to":"file:/workspace/apps/web/src/hooks/use-special-keys.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/special-keys-panel.tsx","to":"/workspace/apps/web/src/components/special-keys-panel.tsx:SpecialKeysPanel","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/special-keys-panel.tsx","to":"/workspace/apps/web/src/components/special-keys-panel.tsx:SpecialKeysPanel","kind":"defines"},{"from":"/workspace/apps/web/src/components/special-keys-panel.tsx:SpecialKeysPanel","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/components/special-keys-panel.tsx:SpecialKeysPanel","to":"/workspace/apps/web/src/hooks/use-special-keys.ts:getSequenceWithModifier","kind":"calls","metadata":{"unresolvedName":"getSequenceWithModifier"}},{"from":"/workspace/apps/web/src/components/special-keys-panel.tsx:SpecialKeysPanel","to":"symbol-name:onSend","kind":"calls","metadata":{"unresolvedName":"onSend"}},{"from":"/workspace/apps/web/src/components/special-keys-panel.tsx:SpecialKeysPanel","to":"symbol-name:onModifierChange","kind":"calls","metadata":{"unresolvedName":"onModifierChange"}},{"from":"/workspace/apps/web/src/components/special-keys-panel.tsx:SpecialKeysPanel","to":"symbol-name:onClose","kind":"calls","metadata":{"unresolvedName":"onClose"}},{"from":"/workspace/apps/web/src/components/special-keys-panel.tsx:SpecialKeysPanel","to":"symbol-name:requestAnimationFrame","kind":"calls","metadata":{"unresolvedName":"requestAnimationFrame"}},{"from":"/workspace/apps/web/src/components/special-keys-panel.tsx:SpecialKeysPanel","to":"symbol-name:onKeepFocus","kind":"calls","metadata":{"unresolvedName":"onKeepFocus"}},{"from":"/workspace/apps/web/src/components/special-keys-panel.tsx:SpecialKeysPanel","to":"external:e.stopPropagation","kind":"calls","metadata":{"unresolvedName":"e.stopPropagation"}},{"from":"/workspace/apps/web/src/components/special-keys-panel.tsx:SpecialKeysPanel","to":"external:EXPANDED_KEYS.map","kind":"calls","metadata":{"unresolvedName":"EXPANDED_KEYS.map"}},{"from":"/workspace/apps/web/src/components/special-keys-panel.tsx:SpecialKeysPanel","to":"symbol-name:handlePointerDown","kind":"calls","metadata":{"unresolvedName":"handlePointerDown"}},{"from":"/workspace/apps/web/src/components/special-keys-panel.tsx:SpecialKeysPanel","to":"external:F_KEYS.map","kind":"calls","metadata":{"unresolvedName":"F_KEYS.map"}},{"from":"file:/workspace/apps/web/src/components/special-keys-panel.tsx","to":"/workspace/apps/web/src/components/special-keys-panel.tsx:handlePointerDown","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/special-keys-panel.tsx","to":"/workspace/apps/web/src/components/special-keys-panel.tsx:handlePointerDown","kind":"defines"},{"from":"/workspace/apps/web/src/components/special-keys-panel.tsx:handlePointerDown","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/components/special-keys-panel.tsx:handlePointerDown","to":"/workspace/apps/web/src/hooks/use-special-keys.ts:getSequenceWithModifier","kind":"calls","metadata":{"unresolvedName":"getSequenceWithModifier"}},{"from":"/workspace/apps/web/src/components/special-keys-panel.tsx:handlePointerDown","to":"symbol-name:onSend","kind":"calls","metadata":{"unresolvedName":"onSend"}},{"from":"/workspace/apps/web/src/components/special-keys-panel.tsx:handlePointerDown","to":"symbol-name:onModifierChange","kind":"calls","metadata":{"unresolvedName":"onModifierChange"}},{"from":"/workspace/apps/web/src/components/special-keys-panel.tsx:handlePointerDown","to":"symbol-name:onClose","kind":"calls","metadata":{"unresolvedName":"onClose"}},{"from":"/workspace/apps/web/src/components/special-keys-panel.tsx:handlePointerDown","to":"symbol-name:requestAnimationFrame","kind":"calls","metadata":{"unresolvedName":"requestAnimationFrame"}},{"from":"/workspace/apps/web/src/components/special-keys-panel.tsx:handlePointerDown","to":"symbol-name:onKeepFocus","kind":"calls","metadata":{"unresolvedName":"onKeepFocus"}},{"from":"file:/workspace/apps/web/src/components/special-keys-panel.tsx","to":"/workspace/apps/web/src/components/special-keys-panel.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/special-keys-panel.tsx","to":"/workspace/apps/web/src/components/special-keys-panel.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/special-keys-panel.tsx:","to":"symbol-name:onKeepFocus","kind":"calls","metadata":{"unresolvedName":"onKeepFocus"}},{"from":"file:/workspace/apps/web/src/components/special-keys-panel.tsx","to":"/workspace/apps/web/src/components/special-keys-panel.tsx:handleOverlayPointerDown","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/special-keys-panel.tsx","to":"/workspace/apps/web/src/components/special-keys-panel.tsx:handleOverlayPointerDown","kind":"defines"},{"from":"/workspace/apps/web/src/components/special-keys-panel.tsx:handleOverlayPointerDown","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/components/special-keys-panel.tsx:handleOverlayPointerDown","to":"symbol-name:onModifierChange","kind":"calls","metadata":{"unresolvedName":"onModifierChange"}},{"from":"/workspace/apps/web/src/components/special-keys-panel.tsx:handleOverlayPointerDown","to":"symbol-name:onClose","kind":"calls","metadata":{"unresolvedName":"onClose"}},{"from":"/workspace/apps/web/src/components/special-keys-panel.tsx:handleOverlayPointerDown","to":"symbol-name:requestAnimationFrame","kind":"calls","metadata":{"unresolvedName":"requestAnimationFrame"}},{"from":"/workspace/apps/web/src/components/special-keys-panel.tsx:handleOverlayPointerDown","to":"symbol-name:onKeepFocus","kind":"calls","metadata":{"unresolvedName":"onKeepFocus"}},{"from":"file:/workspace/apps/web/src/components/special-keys-panel.tsx","to":"/workspace/apps/web/src/components/special-keys-panel.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/special-keys-panel.tsx","to":"/workspace/apps/web/src/components/special-keys-panel.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/special-keys-panel.tsx:","to":"symbol-name:onKeepFocus","kind":"calls","metadata":{"unresolvedName":"onKeepFocus"}},{"from":"file:/workspace/apps/web/src/components/special-keys-strip.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/special-keys-strip.tsx","to":"file:/workspace/apps/web/src/hooks/use-special-keys.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/special-keys-strip.tsx","to":"/workspace/apps/web/src/components/special-keys-strip.tsx:SpecialKeysStrip","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/special-keys-strip.tsx","to":"/workspace/apps/web/src/components/special-keys-strip.tsx:SpecialKeysStrip","kind":"defines"},{"from":"/workspace/apps/web/src/components/special-keys-strip.tsx:SpecialKeysStrip","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/components/special-keys-strip.tsx:SpecialKeysStrip","to":"symbol-name:onModifierChange","kind":"calls","metadata":{"unresolvedName":"onModifierChange"}},{"from":"/workspace/apps/web/src/components/special-keys-strip.tsx:SpecialKeysStrip","to":"symbol-name:requestAnimationFrame","kind":"calls","metadata":{"unresolvedName":"requestAnimationFrame"}},{"from":"/workspace/apps/web/src/components/special-keys-strip.tsx:SpecialKeysStrip","to":"symbol-name:onKeepFocus","kind":"calls","metadata":{"unresolvedName":"onKeepFocus"}},{"from":"/workspace/apps/web/src/components/special-keys-strip.tsx:SpecialKeysStrip","to":"/workspace/apps/web/src/hooks/use-special-keys.ts:getSequenceWithModifier","kind":"calls","metadata":{"unresolvedName":"getSequenceWithModifier"}},{"from":"/workspace/apps/web/src/components/special-keys-strip.tsx:SpecialKeysStrip","to":"symbol-name:onSend","kind":"calls","metadata":{"unresolvedName":"onSend"}},{"from":"/workspace/apps/web/src/components/special-keys-strip.tsx:SpecialKeysStrip","to":"symbol-name:onMoreClick","kind":"calls","metadata":{"unresolvedName":"onMoreClick"}},{"from":"/workspace/apps/web/src/components/special-keys-strip.tsx:SpecialKeysStrip","to":"external:PRIMARY_KEYS.map","kind":"calls","metadata":{"unresolvedName":"PRIMARY_KEYS.map"}},{"from":"/workspace/apps/web/src/components/special-keys-strip.tsx:SpecialKeysStrip","to":"symbol-name:handlePointerDown","kind":"calls","metadata":{"unresolvedName":"handlePointerDown"}},{"from":"file:/workspace/apps/web/src/components/special-keys-strip.tsx","to":"/workspace/apps/web/src/components/special-keys-strip.tsx:handlePointerDown","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/special-keys-strip.tsx","to":"/workspace/apps/web/src/components/special-keys-strip.tsx:handlePointerDown","kind":"defines"},{"from":"/workspace/apps/web/src/components/special-keys-strip.tsx:handlePointerDown","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/components/special-keys-strip.tsx:handlePointerDown","to":"symbol-name:onModifierChange","kind":"calls","metadata":{"unresolvedName":"onModifierChange"}},{"from":"/workspace/apps/web/src/components/special-keys-strip.tsx:handlePointerDown","to":"symbol-name:requestAnimationFrame","kind":"calls","metadata":{"unresolvedName":"requestAnimationFrame"}},{"from":"/workspace/apps/web/src/components/special-keys-strip.tsx:handlePointerDown","to":"symbol-name:onKeepFocus","kind":"calls","metadata":{"unresolvedName":"onKeepFocus"}},{"from":"/workspace/apps/web/src/components/special-keys-strip.tsx:handlePointerDown","to":"/workspace/apps/web/src/hooks/use-special-keys.ts:getSequenceWithModifier","kind":"calls","metadata":{"unresolvedName":"getSequenceWithModifier"}},{"from":"/workspace/apps/web/src/components/special-keys-strip.tsx:handlePointerDown","to":"symbol-name:onSend","kind":"calls","metadata":{"unresolvedName":"onSend"}},{"from":"file:/workspace/apps/web/src/components/special-keys-strip.tsx","to":"/workspace/apps/web/src/components/special-keys-strip.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/special-keys-strip.tsx","to":"/workspace/apps/web/src/components/special-keys-strip.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/special-keys-strip.tsx:","to":"symbol-name:onKeepFocus","kind":"calls","metadata":{"unresolvedName":"onKeepFocus"}},{"from":"file:/workspace/apps/web/src/components/special-keys-strip.tsx","to":"/workspace/apps/web/src/components/special-keys-strip.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/special-keys-strip.tsx","to":"/workspace/apps/web/src/components/special-keys-strip.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/special-keys-strip.tsx:","to":"symbol-name:onKeepFocus","kind":"calls","metadata":{"unresolvedName":"onKeepFocus"}},{"from":"file:/workspace/apps/web/src/components/special-keys-strip.tsx","to":"/workspace/apps/web/src/components/special-keys-strip.tsx:handleMorePointerDown","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/special-keys-strip.tsx","to":"/workspace/apps/web/src/components/special-keys-strip.tsx:handleMorePointerDown","kind":"defines"},{"from":"/workspace/apps/web/src/components/special-keys-strip.tsx:handleMorePointerDown","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/components/special-keys-strip.tsx:handleMorePointerDown","to":"symbol-name:onMoreClick","kind":"calls","metadata":{"unresolvedName":"onMoreClick"}},{"from":"/workspace/apps/web/src/components/special-keys-strip.tsx:handleMorePointerDown","to":"symbol-name:requestAnimationFrame","kind":"calls","metadata":{"unresolvedName":"requestAnimationFrame"}},{"from":"/workspace/apps/web/src/components/special-keys-strip.tsx:handleMorePointerDown","to":"symbol-name:onKeepFocus","kind":"calls","metadata":{"unresolvedName":"onKeepFocus"}},{"from":"file:/workspace/apps/web/src/components/special-keys-strip.tsx","to":"/workspace/apps/web/src/components/special-keys-strip.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/special-keys-strip.tsx","to":"/workspace/apps/web/src/components/special-keys-strip.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/special-keys-strip.tsx:","to":"symbol-name:onKeepFocus","kind":"calls","metadata":{"unresolvedName":"onKeepFocus"}},{"from":"file:/workspace/apps/web/src/components/start-tool-fab.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/start-tool-fab.tsx","to":"external:@/components/ui/Icon","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/start-tool-fab.tsx","to":"file:/workspace/apps/web/src/components/tool-starter.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/start-tool-fab.tsx","to":"file:/workspace/apps/web/src/types/workspace.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/start-tool-fab.tsx","to":"file:/workspace/apps/web/src/api/workspaces.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/start-tool-fab.tsx","to":"/workspace/apps/web/src/components/start-tool-fab.tsx:StartToolFAB","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/start-tool-fab.tsx","to":"/workspace/apps/web/src/components/start-tool-fab.tsx:StartToolFAB","kind":"defines"},{"from":"/workspace/apps/web/src/components/start-tool-fab.tsx:StartToolFAB","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/start-tool-fab.tsx:StartToolFAB","to":"symbol-name:setOpen","kind":"calls","metadata":{"unresolvedName":"setOpen"}},{"from":"/workspace/apps/web/src/components/start-tool-fab.tsx:StartToolFAB","to":"symbol-name:setWorkspacesLoading","kind":"calls","metadata":{"unresolvedName":"setWorkspacesLoading"}},{"from":"/workspace/apps/web/src/components/start-tool-fab.tsx:StartToolFAB","to":"/workspace/apps/web/src/api/workspaces.ts:listAllWorkspaces","kind":"calls","metadata":{"unresolvedName":"listAllWorkspaces"}},{"from":"/workspace/apps/web/src/components/start-tool-fab.tsx:StartToolFAB","to":"symbol-name:setWorkspaces","kind":"calls","metadata":{"unresolvedName":"setWorkspaces"}},{"from":"/workspace/apps/web/src/components/start-tool-fab.tsx:StartToolFAB","to":"symbol-name:setSelectedWorkspace","kind":"calls","metadata":{"unresolvedName":"setSelectedWorkspace"}},{"from":"/workspace/apps/web/src/components/start-tool-fab.tsx:StartToolFAB","to":"external:e.stopPropagation","kind":"calls","metadata":{"unresolvedName":"e.stopPropagation"}},{"from":"/workspace/apps/web/src/components/start-tool-fab.tsx:StartToolFAB","to":"external:workspaces.find","kind":"calls","metadata":{"unresolvedName":"workspaces.find"}},{"from":"/workspace/apps/web/src/components/start-tool-fab.tsx:StartToolFAB","to":"external:workspaces.map","kind":"calls","metadata":{"unresolvedName":"workspaces.map"}},{"from":"file:/workspace/apps/web/src/components/start-tool-fab.tsx","to":"/workspace/apps/web/src/components/start-tool-fab.tsx:handleOpen","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/start-tool-fab.tsx","to":"/workspace/apps/web/src/components/start-tool-fab.tsx:handleOpen","kind":"defines"},{"from":"/workspace/apps/web/src/components/start-tool-fab.tsx:handleOpen","to":"symbol-name:setOpen","kind":"calls","metadata":{"unresolvedName":"setOpen"}},{"from":"/workspace/apps/web/src/components/start-tool-fab.tsx:handleOpen","to":"symbol-name:setWorkspacesLoading","kind":"calls","metadata":{"unresolvedName":"setWorkspacesLoading"}},{"from":"/workspace/apps/web/src/components/start-tool-fab.tsx:handleOpen","to":"/workspace/apps/web/src/api/workspaces.ts:listAllWorkspaces","kind":"calls","metadata":{"unresolvedName":"listAllWorkspaces"}},{"from":"/workspace/apps/web/src/components/start-tool-fab.tsx:handleOpen","to":"symbol-name:setWorkspaces","kind":"calls","metadata":{"unresolvedName":"setWorkspaces"}},{"from":"file:/workspace/apps/web/src/components/start-tool-fab.tsx","to":"/workspace/apps/web/src/components/start-tool-fab.tsx:handleClose","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/start-tool-fab.tsx","to":"/workspace/apps/web/src/components/start-tool-fab.tsx:handleClose","kind":"defines"},{"from":"/workspace/apps/web/src/components/start-tool-fab.tsx:handleClose","to":"symbol-name:setOpen","kind":"calls","metadata":{"unresolvedName":"setOpen"}},{"from":"/workspace/apps/web/src/components/start-tool-fab.tsx:handleClose","to":"symbol-name:setSelectedWorkspace","kind":"calls","metadata":{"unresolvedName":"setSelectedWorkspace"}},{"from":"file:/workspace/apps/web/src/components/start-tool-fab.tsx","to":"/workspace/apps/web/src/components/start-tool-fab.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/start-tool-fab.tsx","to":"/workspace/apps/web/src/components/start-tool-fab.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/start-tool-fab.tsx:","to":"external:workspaces.find","kind":"calls","metadata":{"unresolvedName":"workspaces.find"}},{"from":"/workspace/apps/web/src/components/start-tool-fab.tsx:","to":"symbol-name:setSelectedWorkspace","kind":"calls","metadata":{"unresolvedName":"setSelectedWorkspace"}},{"from":"file:/workspace/apps/web/src/components/start-tool-modal.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/start-tool-modal.tsx","to":"external:@/components/ui/Icon","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/start-tool-modal.tsx","to":"external:@/api/tool-types","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/start-tool-modal.tsx","to":"file:/workspace/apps/web/src/hooks/use-async-data.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/start-tool-modal.tsx","to":"file:/workspace/apps/web/src/types/workspace.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/start-tool-modal.tsx","to":"/workspace/apps/web/src/components/start-tool-modal.tsx:StartToolModal","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/start-tool-modal.tsx","to":"/workspace/apps/web/src/components/start-tool-modal.tsx:StartToolModal","kind":"defines"},{"from":"/workspace/apps/web/src/components/start-tool-modal.tsx:StartToolModal","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/start-tool-modal.tsx:StartToolModal","to":"/workspace/apps/web/src/hooks/use-async-data.ts:useAsyncData","kind":"calls","metadata":{"unresolvedName":"useAsyncData"}},{"from":"/workspace/apps/web/src/components/start-tool-modal.tsx:StartToolModal","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/components/start-tool-modal.tsx:StartToolModal","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/components/start-tool-modal.tsx:StartToolModal","to":"symbol-name:setSubmitting","kind":"calls","metadata":{"unresolvedName":"setSubmitting"}},{"from":"/workspace/apps/web/src/components/start-tool-modal.tsx:StartToolModal","to":"symbol-name:onStart","kind":"calls","metadata":{"unresolvedName":"onStart"}},{"from":"/workspace/apps/web/src/components/start-tool-modal.tsx:StartToolModal","to":"symbol-name:onClose","kind":"calls","metadata":{"unresolvedName":"onClose"}},{"from":"/workspace/apps/web/src/components/start-tool-modal.tsx:StartToolModal","to":"external:e.stopPropagation","kind":"calls","metadata":{"unresolvedName":"e.stopPropagation"}},{"from":"/workspace/apps/web/src/components/start-tool-modal.tsx:StartToolModal","to":"symbol-name:setToolTypeId","kind":"calls","metadata":{"unresolvedName":"setToolTypeId"}},{"from":"/workspace/apps/web/src/components/start-tool-modal.tsx:StartToolModal","to":"external:toolTypes?.map","kind":"calls","metadata":{"unresolvedName":"toolTypes?.map"}},{"from":"/workspace/apps/web/src/components/start-tool-modal.tsx:StartToolModal","to":"symbol-name:setConfigProfileId","kind":"calls","metadata":{"unresolvedName":"setConfigProfileId"}},{"from":"file:/workspace/apps/web/src/components/start-tool-modal.tsx","to":"/workspace/apps/web/src/components/start-tool-modal.tsx:handleSubmit","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/start-tool-modal.tsx","to":"/workspace/apps/web/src/components/start-tool-modal.tsx:handleSubmit","kind":"defines"},{"from":"/workspace/apps/web/src/components/start-tool-modal.tsx:handleSubmit","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/components/start-tool-modal.tsx:handleSubmit","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/components/start-tool-modal.tsx:handleSubmit","to":"symbol-name:setSubmitting","kind":"calls","metadata":{"unresolvedName":"setSubmitting"}},{"from":"/workspace/apps/web/src/components/start-tool-modal.tsx:handleSubmit","to":"symbol-name:onStart","kind":"calls","metadata":{"unresolvedName":"onStart"}},{"from":"/workspace/apps/web/src/components/start-tool-modal.tsx:handleSubmit","to":"symbol-name:onClose","kind":"calls","metadata":{"unresolvedName":"onClose"}},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.test.tsx","to":"external:@testing-library/react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.test.tsx","to":"external:vitest","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.test.tsx","to":"file:/workspace/apps/web/src/components/terminal-session-tabs.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.test.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.test.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"symbol-name:cleanup","kind":"calls","metadata":{"unresolvedName":"cleanup"}},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.test.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.test.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(screen.getByText(\"Session 1\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Session 1\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(screen.getByText(\"Session 2\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Session 2\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(screen.getByText(\"Session 3\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Session 3\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(screen.getByRole(\"button\", { name: /new session/i })).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByRole(\"button\", { name: /new session/i })).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:screen.getAllByText","kind":"calls","metadata":{"unresolvedName":"screen.getAllByText"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(onSelect).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(onSelect).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:screen.getByLabelText","kind":"calls","metadata":{"unresolvedName":"screen.getByLabelText"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(screen.getByText(\"Close?\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Close?\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(onClose).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(onClose).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:fireEvent.doubleClick","kind":"calls","metadata":{"unresolvedName":"fireEvent.doubleClick"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(input).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(input).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:fireEvent.change","kind":"calls","metadata":{"unresolvedName":"fireEvent.change"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:fireEvent.keyDown","kind":"calls","metadata":{"unresolvedName":"fireEvent.keyDown"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(onRename).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(onRename).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(onRename).not.toHaveBeenCalled","kind":"calls","metadata":{"unresolvedName":"expect(onRename).not.toHaveBeenCalled"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:Array.from","kind":"calls","metadata":{"unresolvedName":"Array.from"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(newButton).toBeDisabled","kind":"calls","metadata":{"unresolvedName":"expect(newButton).toBeDisabled"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:screen.getAllByRole","kind":"calls","metadata":{"unresolvedName":"screen.getAllByRole"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(tabs).toHaveLength","kind":"calls","metadata":{"unresolvedName":"expect(tabs).toHaveLength"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(tabs[0].querySelector(\".connected\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(tabs[0].querySelector(\".connected\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:tabs[0].querySelector","kind":"calls","metadata":{"unresolvedName":"tabs[0].querySelector"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(tabs[1].querySelector(\".connecting\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(tabs[1].querySelector(\".connecting\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:tabs[1].querySelector","kind":"calls","metadata":{"unresolvedName":"tabs[1].querySelector"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(tabs[2].querySelector(\".disconnected\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(tabs[2].querySelector(\".disconnected\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:tabs[2].querySelector","kind":"calls","metadata":{"unresolvedName":"tabs[2].querySelector"}},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.test.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.test.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(screen.getByText(\"Session 1\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Session 1\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(screen.getByText(\"Session 2\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Session 2\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(screen.getByText(\"Session 3\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Session 3\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(screen.getByRole(\"button\", { name: /new session/i })).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByRole(\"button\", { name: /new session/i })).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.test.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.test.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:screen.getAllByText","kind":"calls","metadata":{"unresolvedName":"screen.getAllByText"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(onSelect).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(onSelect).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.test.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.test.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:screen.getByLabelText","kind":"calls","metadata":{"unresolvedName":"screen.getByLabelText"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(screen.getByText(\"Close?\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Close?\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(onClose).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(onClose).toHaveBeenCalledWith"}},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.test.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.test.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:fireEvent.doubleClick","kind":"calls","metadata":{"unresolvedName":"fireEvent.doubleClick"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:screen.getAllByText","kind":"calls","metadata":{"unresolvedName":"screen.getAllByText"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:screen.getByLabelText","kind":"calls","metadata":{"unresolvedName":"screen.getByLabelText"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(input).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(input).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:fireEvent.change","kind":"calls","metadata":{"unresolvedName":"fireEvent.change"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:fireEvent.keyDown","kind":"calls","metadata":{"unresolvedName":"fireEvent.keyDown"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(onRename).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(onRename).toHaveBeenCalledWith"}},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.test.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.test.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:fireEvent.doubleClick","kind":"calls","metadata":{"unresolvedName":"fireEvent.doubleClick"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:screen.getAllByText","kind":"calls","metadata":{"unresolvedName":"screen.getAllByText"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:screen.getByLabelText","kind":"calls","metadata":{"unresolvedName":"screen.getByLabelText"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:fireEvent.change","kind":"calls","metadata":{"unresolvedName":"fireEvent.change"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:fireEvent.keyDown","kind":"calls","metadata":{"unresolvedName":"fireEvent.keyDown"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(onRename).not.toHaveBeenCalled","kind":"calls","metadata":{"unresolvedName":"expect(onRename).not.toHaveBeenCalled"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(screen.getByText(\"Session 1\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Session 1\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.test.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.test.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:Array.from","kind":"calls","metadata":{"unresolvedName":"Array.from"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(newButton).toBeDisabled","kind":"calls","metadata":{"unresolvedName":"expect(newButton).toBeDisabled"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.test.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.test.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:screen.getAllByRole","kind":"calls","metadata":{"unresolvedName":"screen.getAllByRole"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(tabs).toHaveLength","kind":"calls","metadata":{"unresolvedName":"expect(tabs).toHaveLength"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(tabs[0].querySelector(\".connected\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(tabs[0].querySelector(\".connected\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:tabs[0].querySelector","kind":"calls","metadata":{"unresolvedName":"tabs[0].querySelector"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(tabs[1].querySelector(\".connecting\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(tabs[1].querySelector(\".connecting\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:tabs[1].querySelector","kind":"calls","metadata":{"unresolvedName":"tabs[1].querySelector"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:expect(tabs[2].querySelector(\".disconnected\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(tabs[2].querySelector(\".disconnected\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.test.tsx:","to":"external:tabs[2].querySelector","kind":"calls","metadata":{"unresolvedName":"tabs[2].querySelector"}},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:TerminalSessionTabs","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:TerminalSessionTabs","kind":"defines"},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:TerminalSessionTabs","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:TerminalSessionTabs","to":"symbol-name:useRef","kind":"calls","metadata":{"unresolvedName":"useRef"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:TerminalSessionTabs","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:TerminalSessionTabs","to":"symbol-name:setRenamingId","kind":"calls","metadata":{"unresolvedName":"setRenamingId"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:TerminalSessionTabs","to":"symbol-name:setRenameValue","kind":"calls","metadata":{"unresolvedName":"setRenameValue"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:TerminalSessionTabs","to":"symbol-name:requestAnimationFrame","kind":"calls","metadata":{"unresolvedName":"requestAnimationFrame"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:TerminalSessionTabs","to":"external:renameInputRef.current?.focus","kind":"calls","metadata":{"unresolvedName":"renameInputRef.current?.focus"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:TerminalSessionTabs","to":"external:renameInputRef.current?.select","kind":"calls","metadata":{"unresolvedName":"renameInputRef.current?.select"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:TerminalSessionTabs","to":"external:renameValue.trim","kind":"calls","metadata":{"unresolvedName":"renameValue.trim"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:TerminalSessionTabs","to":"symbol-name:onRename","kind":"calls","metadata":{"unresolvedName":"onRename"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:TerminalSessionTabs","to":"symbol-name:commitRename","kind":"calls","metadata":{"unresolvedName":"commitRename"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:TerminalSessionTabs","to":"symbol-name:cancelRename","kind":"calls","metadata":{"unresolvedName":"cancelRename"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:TerminalSessionTabs","to":"external:e.stopPropagation","kind":"calls","metadata":{"unresolvedName":"e.stopPropagation"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:TerminalSessionTabs","to":"symbol-name:setConfirmCloseId","kind":"calls","metadata":{"unresolvedName":"setConfirmCloseId"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:TerminalSessionTabs","to":"symbol-name:onClose","kind":"calls","metadata":{"unresolvedName":"onClose"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:TerminalSessionTabs","to":"symbol-name:setTimeout","kind":"calls","metadata":{"unresolvedName":"setTimeout"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:TerminalSessionTabs","to":"external:sessions.map","kind":"calls","metadata":{"unresolvedName":"sessions.map"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:TerminalSessionTabs","to":"symbol-name:onSelect","kind":"calls","metadata":{"unresolvedName":"onSelect"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:TerminalSessionTabs","to":"symbol-name:handleDoubleClick","kind":"calls","metadata":{"unresolvedName":"handleDoubleClick"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:TerminalSessionTabs","to":"symbol-name:handleCloseClick","kind":"calls","metadata":{"unresolvedName":"handleCloseClick"}},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","to":"symbol-name:setRenamingId","kind":"calls","metadata":{"unresolvedName":"setRenamingId"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","to":"symbol-name:setRenameValue","kind":"calls","metadata":{"unresolvedName":"setRenameValue"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","to":"symbol-name:requestAnimationFrame","kind":"calls","metadata":{"unresolvedName":"requestAnimationFrame"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","to":"external:renameInputRef.current?.focus","kind":"calls","metadata":{"unresolvedName":"renameInputRef.current?.focus"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","to":"external:renameInputRef.current?.select","kind":"calls","metadata":{"unresolvedName":"renameInputRef.current?.select"}},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","to":"external:renameInputRef.current?.focus","kind":"calls","metadata":{"unresolvedName":"renameInputRef.current?.focus"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","to":"external:renameInputRef.current?.select","kind":"calls","metadata":{"unresolvedName":"renameInputRef.current?.select"}},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","to":"external:renameValue.trim","kind":"calls","metadata":{"unresolvedName":"renameValue.trim"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","to":"symbol-name:onRename","kind":"calls","metadata":{"unresolvedName":"onRename"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","to":"symbol-name:setRenamingId","kind":"calls","metadata":{"unresolvedName":"setRenamingId"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","to":"symbol-name:setRenameValue","kind":"calls","metadata":{"unresolvedName":"setRenameValue"}},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","to":"symbol-name:setRenamingId","kind":"calls","metadata":{"unresolvedName":"setRenamingId"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","to":"symbol-name:setRenameValue","kind":"calls","metadata":{"unresolvedName":"setRenameValue"}},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","to":"symbol-name:commitRename","kind":"calls","metadata":{"unresolvedName":"commitRename"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","to":"symbol-name:cancelRename","kind":"calls","metadata":{"unresolvedName":"cancelRename"}},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","to":"external:e.stopPropagation","kind":"calls","metadata":{"unresolvedName":"e.stopPropagation"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","to":"symbol-name:setConfirmCloseId","kind":"calls","metadata":{"unresolvedName":"setConfirmCloseId"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","to":"symbol-name:onClose","kind":"calls","metadata":{"unresolvedName":"onClose"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","to":"symbol-name:setTimeout","kind":"calls","metadata":{"unresolvedName":"setTimeout"}},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","to":"symbol-name:setConfirmCloseId","kind":"calls","metadata":{"unresolvedName":"setConfirmCloseId"}},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","to":"symbol-name:onSelect","kind":"calls","metadata":{"unresolvedName":"onSelect"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","to":"symbol-name:handleDoubleClick","kind":"calls","metadata":{"unresolvedName":"handleDoubleClick"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","to":"symbol-name:setRenameValue","kind":"calls","metadata":{"unresolvedName":"setRenameValue"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","to":"external:e.stopPropagation","kind":"calls","metadata":{"unresolvedName":"e.stopPropagation"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","to":"symbol-name:setConfirmCloseId","kind":"calls","metadata":{"unresolvedName":"setConfirmCloseId"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","to":"symbol-name:onClose","kind":"calls","metadata":{"unresolvedName":"onClose"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","to":"symbol-name:handleCloseClick","kind":"calls","metadata":{"unresolvedName":"handleCloseClick"}},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/terminal-session-tabs.tsx","to":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","to":"external:e.stopPropagation","kind":"calls","metadata":{"unresolvedName":"e.stopPropagation"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","to":"symbol-name:setConfirmCloseId","kind":"calls","metadata":{"unresolvedName":"setConfirmCloseId"}},{"from":"/workspace/apps/web/src/components/terminal-session-tabs.tsx:","to":"symbol-name:onClose","kind":"calls","metadata":{"unresolvedName":"onClose"}},{"from":"file:/workspace/apps/web/src/components/toast-rules.test.ts","to":"external:vitest","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/toast-rules.test.ts","to":"file:/workspace/apps/web/src/components/toast-rules.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/toast-rules.test.ts","to":"file:/workspace/apps/web/src/types/events.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/toast-rules.test.ts","to":"/workspace/apps/web/src/components/toast-rules.test.ts:makeEvent","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/toast-rules.test.ts","to":"/workspace/apps/web/src/components/toast-rules.test.ts:makeEvent","kind":"defines"},{"from":"file:/workspace/apps/web/src/components/toast-rules.test.ts","to":"/workspace/apps/web/src/components/toast-rules.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/toast-rules.test.ts","to":"/workspace/apps/web/src/components/toast-rules.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"external:expect(mapEventToCategory(makeEvent(\"instance.started\"))).toBe","kind":"calls","metadata":{"unresolvedName":"expect(mapEventToCategory(makeEvent(\"instance.started\"))).toBe"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"/workspace/apps/web/src/components/toast-rules.ts:mapEventToCategory","kind":"calls","metadata":{"unresolvedName":"mapEventToCategory"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"symbol-name:makeEvent","kind":"calls","metadata":{"unresolvedName":"makeEvent"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"external:expect(mapEventToCategory(makeEvent(\"instance.error\"))).toBe","kind":"calls","metadata":{"unresolvedName":"expect(mapEventToCategory(makeEvent(\"instance.error\"))).toBe"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"external:expect(mapEventToCategory(makeEvent(\"health.error\"))).toBe","kind":"calls","metadata":{"unresolvedName":"expect(mapEventToCategory(makeEvent(\"health.error\"))).toBe"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"external:expect(mapEventToCategory(makeEvent(\"system.announcement\"))).toBe","kind":"calls","metadata":{"unresolvedName":"expect(mapEventToCategory(makeEvent(\"system.announcement\"))).toBe"}},{"from":"file:/workspace/apps/web/src/components/toast-rules.test.ts","to":"/workspace/apps/web/src/components/toast-rules.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/toast-rules.test.ts","to":"/workspace/apps/web/src/components/toast-rules.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"external:expect(mapEventToCategory(makeEvent(\"instance.started\"))).toBe","kind":"calls","metadata":{"unresolvedName":"expect(mapEventToCategory(makeEvent(\"instance.started\"))).toBe"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"/workspace/apps/web/src/components/toast-rules.ts:mapEventToCategory","kind":"calls","metadata":{"unresolvedName":"mapEventToCategory"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"symbol-name:makeEvent","kind":"calls","metadata":{"unresolvedName":"makeEvent"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"external:expect(mapEventToCategory(makeEvent(\"instance.error\"))).toBe","kind":"calls","metadata":{"unresolvedName":"expect(mapEventToCategory(makeEvent(\"instance.error\"))).toBe"}},{"from":"file:/workspace/apps/web/src/components/toast-rules.test.ts","to":"/workspace/apps/web/src/components/toast-rules.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/toast-rules.test.ts","to":"/workspace/apps/web/src/components/toast-rules.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"external:expect(mapEventToCategory(makeEvent(\"health.error\"))).toBe","kind":"calls","metadata":{"unresolvedName":"expect(mapEventToCategory(makeEvent(\"health.error\"))).toBe"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"/workspace/apps/web/src/components/toast-rules.ts:mapEventToCategory","kind":"calls","metadata":{"unresolvedName":"mapEventToCategory"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"symbol-name:makeEvent","kind":"calls","metadata":{"unresolvedName":"makeEvent"}},{"from":"file:/workspace/apps/web/src/components/toast-rules.test.ts","to":"/workspace/apps/web/src/components/toast-rules.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/toast-rules.test.ts","to":"/workspace/apps/web/src/components/toast-rules.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"external:expect(mapEventToCategory(makeEvent(\"system.announcement\"))).toBe","kind":"calls","metadata":{"unresolvedName":"expect(mapEventToCategory(makeEvent(\"system.announcement\"))).toBe"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"/workspace/apps/web/src/components/toast-rules.ts:mapEventToCategory","kind":"calls","metadata":{"unresolvedName":"mapEventToCategory"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"symbol-name:makeEvent","kind":"calls","metadata":{"unresolvedName":"makeEvent"}},{"from":"file:/workspace/apps/web/src/components/toast-rules.test.ts","to":"/workspace/apps/web/src/components/toast-rules.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/toast-rules.test.ts","to":"/workspace/apps/web/src/components/toast-rules.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"external:expect(mapEventToSeverity(makeEvent(\"instance.error\"))).toBe","kind":"calls","metadata":{"unresolvedName":"expect(mapEventToSeverity(makeEvent(\"instance.error\"))).toBe"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"/workspace/apps/web/src/components/toast-rules.ts:mapEventToSeverity","kind":"calls","metadata":{"unresolvedName":"mapEventToSeverity"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"symbol-name:makeEvent","kind":"calls","metadata":{"unresolvedName":"makeEvent"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"external:expect(mapEventToSeverity(makeEvent(\"health.error\"))).toBe","kind":"calls","metadata":{"unresolvedName":"expect(mapEventToSeverity(makeEvent(\"health.error\"))).toBe"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"external:expect(mapEventToSeverity(makeEvent(\"instance.created\"))).toBe","kind":"calls","metadata":{"unresolvedName":"expect(mapEventToSeverity(makeEvent(\"instance.created\"))).toBe"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"external:expect(mapEventToSeverity(makeEvent(\"instance.started\"))).toBe","kind":"calls","metadata":{"unresolvedName":"expect(mapEventToSeverity(makeEvent(\"instance.started\"))).toBe"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"external:expect(mapEventToSeverity(makeEvent(\"instance.stopped\"))).toBe","kind":"calls","metadata":{"unresolvedName":"expect(mapEventToSeverity(makeEvent(\"instance.stopped\"))).toBe"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"external:expect(mapEventToSeverity(makeEvent(\"instance.restarted\"))).toBe","kind":"calls","metadata":{"unresolvedName":"expect(mapEventToSeverity(makeEvent(\"instance.restarted\"))).toBe"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"external:expect(mapEventToSeverity(makeEvent(\"instance.deleted\"))).toBe","kind":"calls","metadata":{"unresolvedName":"expect(mapEventToSeverity(makeEvent(\"instance.deleted\"))).toBe"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"external:expect(mapEventToSeverity(makeEvent(\"unknown.event\"))).toBe","kind":"calls","metadata":{"unresolvedName":"expect(mapEventToSeverity(makeEvent(\"unknown.event\"))).toBe"}},{"from":"file:/workspace/apps/web/src/components/toast-rules.test.ts","to":"/workspace/apps/web/src/components/toast-rules.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/toast-rules.test.ts","to":"/workspace/apps/web/src/components/toast-rules.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"external:expect(mapEventToSeverity(makeEvent(\"instance.error\"))).toBe","kind":"calls","metadata":{"unresolvedName":"expect(mapEventToSeverity(makeEvent(\"instance.error\"))).toBe"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"/workspace/apps/web/src/components/toast-rules.ts:mapEventToSeverity","kind":"calls","metadata":{"unresolvedName":"mapEventToSeverity"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"symbol-name:makeEvent","kind":"calls","metadata":{"unresolvedName":"makeEvent"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"external:expect(mapEventToSeverity(makeEvent(\"health.error\"))).toBe","kind":"calls","metadata":{"unresolvedName":"expect(mapEventToSeverity(makeEvent(\"health.error\"))).toBe"}},{"from":"file:/workspace/apps/web/src/components/toast-rules.test.ts","to":"/workspace/apps/web/src/components/toast-rules.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/toast-rules.test.ts","to":"/workspace/apps/web/src/components/toast-rules.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"/workspace/apps/web/src/components/toast-rules.ts:mapEventToSeverity","kind":"calls","metadata":{"unresolvedName":"mapEventToSeverity"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"symbol-name:makeEvent","kind":"calls","metadata":{"unresolvedName":"makeEvent"}},{"from":"file:/workspace/apps/web/src/components/toast-rules.test.ts","to":"/workspace/apps/web/src/components/toast-rules.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/toast-rules.test.ts","to":"/workspace/apps/web/src/components/toast-rules.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"/workspace/apps/web/src/components/toast-rules.ts:mapEventToSeverity","kind":"calls","metadata":{"unresolvedName":"mapEventToSeverity"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"symbol-name:makeEvent","kind":"calls","metadata":{"unresolvedName":"makeEvent"}},{"from":"file:/workspace/apps/web/src/components/toast-rules.test.ts","to":"/workspace/apps/web/src/components/toast-rules.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/toast-rules.test.ts","to":"/workspace/apps/web/src/components/toast-rules.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"external:expect(mapEventToSeverity(makeEvent(\"instance.created\"))).toBe","kind":"calls","metadata":{"unresolvedName":"expect(mapEventToSeverity(makeEvent(\"instance.created\"))).toBe"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"/workspace/apps/web/src/components/toast-rules.ts:mapEventToSeverity","kind":"calls","metadata":{"unresolvedName":"mapEventToSeverity"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"symbol-name:makeEvent","kind":"calls","metadata":{"unresolvedName":"makeEvent"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"external:expect(mapEventToSeverity(makeEvent(\"instance.started\"))).toBe","kind":"calls","metadata":{"unresolvedName":"expect(mapEventToSeverity(makeEvent(\"instance.started\"))).toBe"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"external:expect(mapEventToSeverity(makeEvent(\"instance.stopped\"))).toBe","kind":"calls","metadata":{"unresolvedName":"expect(mapEventToSeverity(makeEvent(\"instance.stopped\"))).toBe"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"external:expect(mapEventToSeverity(makeEvent(\"instance.restarted\"))).toBe","kind":"calls","metadata":{"unresolvedName":"expect(mapEventToSeverity(makeEvent(\"instance.restarted\"))).toBe"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"external:expect(mapEventToSeverity(makeEvent(\"instance.deleted\"))).toBe","kind":"calls","metadata":{"unresolvedName":"expect(mapEventToSeverity(makeEvent(\"instance.deleted\"))).toBe"}},{"from":"file:/workspace/apps/web/src/components/toast-rules.test.ts","to":"/workspace/apps/web/src/components/toast-rules.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/toast-rules.test.ts","to":"/workspace/apps/web/src/components/toast-rules.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"external:expect(mapEventToSeverity(makeEvent(\"unknown.event\"))).toBe","kind":"calls","metadata":{"unresolvedName":"expect(mapEventToSeverity(makeEvent(\"unknown.event\"))).toBe"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"/workspace/apps/web/src/components/toast-rules.ts:mapEventToSeverity","kind":"calls","metadata":{"unresolvedName":"mapEventToSeverity"}},{"from":"/workspace/apps/web/src/components/toast-rules.test.ts:","to":"symbol-name:makeEvent","kind":"calls","metadata":{"unresolvedName":"makeEvent"}},{"from":"file:/workspace/apps/web/src/components/toast-rules.ts","to":"file:/workspace/apps/web/src/state/toast.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/toast-rules.ts","to":"file:/workspace/apps/web/src/types/events.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/toast-rules.ts","to":"/workspace/apps/web/src/components/toast-rules.ts:makeDedupKey","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/toast-rules.ts","to":"/workspace/apps/web/src/components/toast-rules.ts:makeDedupKey","kind":"defines"},{"from":"file:/workspace/apps/web/src/components/toast-rules.ts","to":"/workspace/apps/web/src/components/toast-rules.ts:shouldShowToast","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/toast-rules.ts","to":"/workspace/apps/web/src/components/toast-rules.ts:shouldShowToast","kind":"defines"},{"from":"/workspace/apps/web/src/components/toast-rules.ts:shouldShowToast","to":"/workspace/apps/web/src/components/toast-rules.ts:makeDedupKey","kind":"calls","metadata":{"unresolvedName":"makeDedupKey"}},{"from":"/workspace/apps/web/src/components/toast-rules.ts:shouldShowToast","to":"external:Date.now","kind":"calls","metadata":{"unresolvedName":"Date.now"}},{"from":"/workspace/apps/web/src/components/toast-rules.ts:shouldShowToast","to":"external:lastToastTime.get","kind":"calls","metadata":{"unresolvedName":"lastToastTime.get"}},{"from":"/workspace/apps/web/src/components/toast-rules.ts:shouldShowToast","to":"external:lastToastTime.set","kind":"calls","metadata":{"unresolvedName":"lastToastTime.set"}},{"from":"file:/workspace/apps/web/src/components/toast-rules.ts","to":"/workspace/apps/web/src/components/toast-rules.ts:mapEventToCategory","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/toast-rules.ts","to":"/workspace/apps/web/src/components/toast-rules.ts:mapEventToCategory","kind":"defines"},{"from":"/workspace/apps/web/src/components/toast-rules.ts:mapEventToCategory","to":"external:event.event.startsWith","kind":"calls","metadata":{"unresolvedName":"event.event.startsWith"}},{"from":"file:/workspace/apps/web/src/components/toast-rules.ts","to":"/workspace/apps/web/src/components/toast-rules.ts:mapEventToSeverity","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/toast-rules.ts","to":"/workspace/apps/web/src/components/toast-rules.ts:mapEventToSeverity","kind":"defines"},{"from":"file:/workspace/apps/web/src/components/toast-rules.ts","to":"/workspace/apps/web/src/components/toast-rules.ts:handleEventToast","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/toast-rules.ts","to":"/workspace/apps/web/src/components/toast-rules.ts:handleEventToast","kind":"defines"},{"from":"/workspace/apps/web/src/components/toast-rules.ts:handleEventToast","to":"/workspace/apps/web/src/components/toast-rules.ts:shouldShowToast","kind":"calls","metadata":{"unresolvedName":"shouldShowToast"}},{"from":"/workspace/apps/web/src/components/toast-rules.ts:handleEventToast","to":"external:toast.info","kind":"calls","metadata":{"unresolvedName":"toast.info"}},{"from":"/workspace/apps/web/src/components/toast-rules.ts:handleEventToast","to":"external:toast.success","kind":"calls","metadata":{"unresolvedName":"toast.success"}},{"from":"/workspace/apps/web/src/components/toast-rules.ts:handleEventToast","to":"external:toast.warning","kind":"calls","metadata":{"unresolvedName":"toast.warning"}},{"from":"/workspace/apps/web/src/components/toast-rules.ts:handleEventToast","to":"external:toast.error","kind":"calls","metadata":{"unresolvedName":"toast.error"}},{"from":"file:/workspace/apps/web/src/components/toast-rules.ts","to":"/workspace/apps/web/src/components/toast-rules.ts:clearToastDedup","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/toast-rules.ts","to":"/workspace/apps/web/src/components/toast-rules.ts:clearToastDedup","kind":"defines"},{"from":"/workspace/apps/web/src/components/toast-rules.ts:clearToastDedup","to":"external:lastToastTime.clear","kind":"calls","metadata":{"unresolvedName":"lastToastTime.clear"}},{"from":"file:/workspace/apps/web/src/components/tool-starter.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/tool-starter.tsx","to":"external:@/components/ui/Icon","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/tool-starter.tsx","to":"external:@/api/tool-types","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/tool-starter.tsx","to":"file:/workspace/apps/web/src/api/config_profiles.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/tool-starter.tsx","to":"external:@/api/ssh-keys","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/tool-starter.tsx","to":"file:/workspace/apps/web/src/types/workspace.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/tool-starter.tsx","to":"file:/workspace/apps/web/src/api/sessions.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/tool-starter.tsx","to":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/tool-starter.tsx","to":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","kind":"defines"},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"/workspace/apps/web/src/api/tool-types.ts:listToolTypes","kind":"calls","metadata":{"unresolvedName":"listToolTypes"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"symbol-name:setToolTypes","kind":"calls","metadata":{"unresolvedName":"setToolTypes"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"symbol-name:setToolTypesError","kind":"calls","metadata":{"unresolvedName":"setToolTypesError"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"symbol-name:setToolTypesLoading","kind":"calls","metadata":{"unresolvedName":"setToolTypesLoading"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"symbol-name:load","kind":"calls","metadata":{"unresolvedName":"load"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"symbol-name:setProfiles","kind":"calls","metadata":{"unresolvedName":"setProfiles"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"symbol-name:setSelectedProfileId","kind":"calls","metadata":{"unresolvedName":"setSelectedProfileId"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"symbol-name:setProfilesLoading","kind":"calls","metadata":{"unresolvedName":"setProfilesLoading"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"/workspace/apps/web/src/api/config_profiles.ts:listConfigProfiles","kind":"calls","metadata":{"unresolvedName":"listConfigProfiles"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"external:data.find","kind":"calls","metadata":{"unresolvedName":"data.find"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"/workspace/apps/web/src/api/ssh-keys.ts:listSSHKeys","kind":"calls","metadata":{"unresolvedName":"listSSHKeys"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"symbol-name:setSshKeys","kind":"calls","metadata":{"unresolvedName":"setSshKeys"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"symbol-name:setSelectedSshKeyIds","kind":"calls","metadata":{"unresolvedName":"setSelectedSshKeyIds"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"external:console.error","kind":"calls","metadata":{"unresolvedName":"console.error"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"symbol-name:setSshKeysLoading","kind":"calls","metadata":{"unresolvedName":"setSshKeysLoading"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"external:sshKeys.find","kind":"calls","metadata":{"unresolvedName":"sshKeys.find"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"symbol-name:setStarting","kind":"calls","metadata":{"unresolvedName":"setStarting"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"symbol-name:import","kind":"calls","metadata":{"unresolvedName":"import"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"symbol-name:onStarted","kind":"calls","metadata":{"unresolvedName":"onStarted"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"symbol-name:setSelectedToolTypeId","kind":"calls","metadata":{"unresolvedName":"setSelectedToolTypeId"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"external:toolTypes.map","kind":"calls","metadata":{"unresolvedName":"toolTypes.map"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"external:profiles.map","kind":"calls","metadata":{"unresolvedName":"profiles.map"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"external:sshKeys.map","kind":"calls","metadata":{"unresolvedName":"sshKeys.map"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"external:selectedSshKeyIds.includes","kind":"calls","metadata":{"unresolvedName":"selectedSshKeyIds.includes"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"external:prev.filter","kind":"calls","metadata":{"unresolvedName":"prev.filter"}},{"from":"file:/workspace/apps/web/src/components/tool-starter.tsx","to":"/workspace/apps/web/src/components/tool-starter.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/tool-starter.tsx","to":"/workspace/apps/web/src/components/tool-starter.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:","to":"/workspace/apps/web/src/api/tool-types.ts:listToolTypes","kind":"calls","metadata":{"unresolvedName":"listToolTypes"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:","to":"symbol-name:setToolTypes","kind":"calls","metadata":{"unresolvedName":"setToolTypes"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:","to":"symbol-name:setToolTypesError","kind":"calls","metadata":{"unresolvedName":"setToolTypesError"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:","to":"symbol-name:setToolTypesLoading","kind":"calls","metadata":{"unresolvedName":"setToolTypesLoading"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:","to":"symbol-name:load","kind":"calls","metadata":{"unresolvedName":"load"}},{"from":"file:/workspace/apps/web/src/components/tool-starter.tsx","to":"/workspace/apps/web/src/components/tool-starter.tsx:load","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/tool-starter.tsx","to":"/workspace/apps/web/src/components/tool-starter.tsx:load","kind":"defines"},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:load","to":"/workspace/apps/web/src/api/tool-types.ts:listToolTypes","kind":"calls","metadata":{"unresolvedName":"listToolTypes"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:load","to":"symbol-name:setToolTypes","kind":"calls","metadata":{"unresolvedName":"setToolTypes"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:load","to":"symbol-name:setToolTypesError","kind":"calls","metadata":{"unresolvedName":"setToolTypesError"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:load","to":"symbol-name:setToolTypesLoading","kind":"calls","metadata":{"unresolvedName":"setToolTypesLoading"}},{"from":"file:/workspace/apps/web/src/components/tool-starter.tsx","to":"/workspace/apps/web/src/components/tool-starter.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/tool-starter.tsx","to":"/workspace/apps/web/src/components/tool-starter.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:","to":"symbol-name:setProfiles","kind":"calls","metadata":{"unresolvedName":"setProfiles"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:","to":"symbol-name:setSelectedProfileId","kind":"calls","metadata":{"unresolvedName":"setSelectedProfileId"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:","to":"symbol-name:setProfilesLoading","kind":"calls","metadata":{"unresolvedName":"setProfilesLoading"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:","to":"/workspace/apps/web/src/api/config_profiles.ts:listConfigProfiles","kind":"calls","metadata":{"unresolvedName":"listConfigProfiles"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:","to":"external:data.find","kind":"calls","metadata":{"unresolvedName":"data.find"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:","to":"symbol-name:load","kind":"calls","metadata":{"unresolvedName":"load"}},{"from":"file:/workspace/apps/web/src/components/tool-starter.tsx","to":"/workspace/apps/web/src/components/tool-starter.tsx:load","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/tool-starter.tsx","to":"/workspace/apps/web/src/components/tool-starter.tsx:load","kind":"defines"},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:load","to":"symbol-name:setProfilesLoading","kind":"calls","metadata":{"unresolvedName":"setProfilesLoading"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:load","to":"/workspace/apps/web/src/api/config_profiles.ts:listConfigProfiles","kind":"calls","metadata":{"unresolvedName":"listConfigProfiles"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:load","to":"symbol-name:setProfiles","kind":"calls","metadata":{"unresolvedName":"setProfiles"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:load","to":"external:data.find","kind":"calls","metadata":{"unresolvedName":"data.find"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:load","to":"symbol-name:setSelectedProfileId","kind":"calls","metadata":{"unresolvedName":"setSelectedProfileId"}},{"from":"file:/workspace/apps/web/src/components/tool-starter.tsx","to":"/workspace/apps/web/src/components/tool-starter.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/tool-starter.tsx","to":"/workspace/apps/web/src/components/tool-starter.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:","to":"/workspace/apps/web/src/api/ssh-keys.ts:listSSHKeys","kind":"calls","metadata":{"unresolvedName":"listSSHKeys"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:","to":"symbol-name:setSshKeys","kind":"calls","metadata":{"unresolvedName":"setSshKeys"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:","to":"symbol-name:setSelectedSshKeyIds","kind":"calls","metadata":{"unresolvedName":"setSelectedSshKeyIds"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:","to":"external:console.error","kind":"calls","metadata":{"unresolvedName":"console.error"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:","to":"symbol-name:setSshKeysLoading","kind":"calls","metadata":{"unresolvedName":"setSshKeysLoading"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:","to":"symbol-name:load","kind":"calls","metadata":{"unresolvedName":"load"}},{"from":"file:/workspace/apps/web/src/components/tool-starter.tsx","to":"/workspace/apps/web/src/components/tool-starter.tsx:load","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/tool-starter.tsx","to":"/workspace/apps/web/src/components/tool-starter.tsx:load","kind":"defines"},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:load","to":"/workspace/apps/web/src/api/ssh-keys.ts:listSSHKeys","kind":"calls","metadata":{"unresolvedName":"listSSHKeys"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:load","to":"symbol-name:setSshKeys","kind":"calls","metadata":{"unresolvedName":"setSshKeys"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:load","to":"symbol-name:setSelectedSshKeyIds","kind":"calls","metadata":{"unresolvedName":"setSelectedSshKeyIds"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:load","to":"external:console.error","kind":"calls","metadata":{"unresolvedName":"console.error"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:load","to":"symbol-name:setSshKeysLoading","kind":"calls","metadata":{"unresolvedName":"setSshKeysLoading"}},{"from":"file:/workspace/apps/web/src/components/tool-starter.tsx","to":"/workspace/apps/web/src/components/tool-starter.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/tool-starter.tsx","to":"/workspace/apps/web/src/components/tool-starter.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:","to":"symbol-name:setStarting","kind":"calls","metadata":{"unresolvedName":"setStarting"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:","to":"symbol-name:import","kind":"calls","metadata":{"unresolvedName":"import"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:","to":"symbol-name:onStarted","kind":"calls","metadata":{"unresolvedName":"onStarted"}},{"from":"file:/workspace/apps/web/src/components/tool-starter.tsx","to":"/workspace/apps/web/src/components/tool-starter.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/tool-starter.tsx","to":"/workspace/apps/web/src/components/tool-starter.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:","to":"symbol-name:setSelectedToolTypeId","kind":"calls","metadata":{"unresolvedName":"setSelectedToolTypeId"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"file:/workspace/apps/web/src/components/tool-starter.tsx","to":"/workspace/apps/web/src/components/tool-starter.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/tool-starter.tsx","to":"/workspace/apps/web/src/components/tool-starter.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:","to":"symbol-name:setSelectedSshKeyIds","kind":"calls","metadata":{"unresolvedName":"setSelectedSshKeyIds"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:","to":"external:prev.filter","kind":"calls","metadata":{"unresolvedName":"prev.filter"}},{"from":"file:/workspace/apps/web/src/components/tools-bottom-sheet.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/tools-bottom-sheet.tsx","to":"external:@/components/ui/Icon","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/tools-bottom-sheet.tsx","to":"/workspace/apps/web/src/components/tools-bottom-sheet.tsx:ToolsBottomSheet","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/tools-bottom-sheet.tsx","to":"/workspace/apps/web/src/components/tools-bottom-sheet.tsx:ToolsBottomSheet","kind":"defines"},{"from":"/workspace/apps/web/src/components/tools-bottom-sheet.tsx:ToolsBottomSheet","to":"symbol-name:useLocation","kind":"calls","metadata":{"unresolvedName":"useLocation"}},{"from":"/workspace/apps/web/src/components/tools-bottom-sheet.tsx:ToolsBottomSheet","to":"symbol-name:useNavigate","kind":"calls","metadata":{"unresolvedName":"useNavigate"}},{"from":"/workspace/apps/web/src/components/tools-bottom-sheet.tsx:ToolsBottomSheet","to":"symbol-name:onClose","kind":"calls","metadata":{"unresolvedName":"onClose"}},{"from":"/workspace/apps/web/src/components/tools-bottom-sheet.tsx:ToolsBottomSheet","to":"symbol-name:navigate","kind":"calls","metadata":{"unresolvedName":"navigate"}},{"from":"/workspace/apps/web/src/components/tools-bottom-sheet.tsx:ToolsBottomSheet","to":"external:e.stopPropagation","kind":"calls","metadata":{"unresolvedName":"e.stopPropagation"}},{"from":"/workspace/apps/web/src/components/tools-bottom-sheet.tsx:ToolsBottomSheet","to":"external:TOOLS_ITEMS.map","kind":"calls","metadata":{"unresolvedName":"TOOLS_ITEMS.map"}},{"from":"/workspace/apps/web/src/components/tools-bottom-sheet.tsx:ToolsBottomSheet","to":"symbol-name:handleSelect","kind":"calls","metadata":{"unresolvedName":"handleSelect"}},{"from":"file:/workspace/apps/web/src/components/tools-bottom-sheet.tsx","to":"/workspace/apps/web/src/components/tools-bottom-sheet.tsx:handleSelect","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/tools-bottom-sheet.tsx","to":"/workspace/apps/web/src/components/tools-bottom-sheet.tsx:handleSelect","kind":"defines"},{"from":"/workspace/apps/web/src/components/tools-bottom-sheet.tsx:handleSelect","to":"symbol-name:onClose","kind":"calls","metadata":{"unresolvedName":"onClose"}},{"from":"/workspace/apps/web/src/components/tools-bottom-sheet.tsx:handleSelect","to":"symbol-name:navigate","kind":"calls","metadata":{"unresolvedName":"navigate"}},{"from":"file:/workspace/apps/web/src/components/ui/CodeEditor.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/ui/CodeEditor.tsx","to":"external:react-simple-code-editor","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/ui/CodeEditor.tsx","to":"file:/workspace/apps/web/src/utils/language.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/ui/CodeEditor.tsx","to":"/workspace/apps/web/src/components/ui/CodeEditor.tsx:CodeEditor","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/ui/CodeEditor.tsx","to":"/workspace/apps/web/src/components/ui/CodeEditor.tsx:CodeEditor","kind":"defines"},{"from":"/workspace/apps/web/src/components/ui/CodeEditor.tsx:CodeEditor","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/components/ui/CodeEditor.tsx:CodeEditor","to":"/workspace/apps/web/src/utils/language.ts:loadLanguage","kind":"calls","metadata":{"unresolvedName":"loadLanguage"}},{"from":"/workspace/apps/web/src/components/ui/CodeEditor.tsx:CodeEditor","to":"symbol-name:highlight","kind":"calls","metadata":{"unresolvedName":"highlight"}},{"from":"/workspace/apps/web/src/components/ui/CodeEditor.tsx:CodeEditor","to":"external:input\n .split(\"\\n\")\n .map","kind":"calls","metadata":{"unresolvedName":"input\n .split(\"\\n\")\n .map"}},{"from":"/workspace/apps/web/src/components/ui/CodeEditor.tsx:CodeEditor","to":"external:input\n .split","kind":"calls","metadata":{"unresolvedName":"input\n .split"}},{"from":"/workspace/apps/web/src/components/ui/CodeEditor.tsx:CodeEditor","to":"/workspace/apps/web/src/utils/language.ts:highlightCode","kind":"calls","metadata":{"unresolvedName":"highlightCode"}},{"from":"file:/workspace/apps/web/src/components/ui/CodeEditor.tsx","to":"/workspace/apps/web/src/components/ui/CodeEditor.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/ui/CodeEditor.tsx","to":"/workspace/apps/web/src/components/ui/CodeEditor.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/ui/CodeEditor.tsx:","to":"/workspace/apps/web/src/utils/language.ts:loadLanguage","kind":"calls","metadata":{"unresolvedName":"loadLanguage"}},{"from":"/workspace/apps/web/src/components/ui/CodeEditor.tsx:","to":"symbol-name:highlight","kind":"calls","metadata":{"unresolvedName":"highlight"}},{"from":"file:/workspace/apps/web/src/components/ui/CodeEditor.tsx","to":"/workspace/apps/web/src/components/ui/CodeEditor.tsx:highlight","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/ui/CodeEditor.tsx","to":"/workspace/apps/web/src/components/ui/CodeEditor.tsx:highlight","kind":"defines"},{"from":"/workspace/apps/web/src/components/ui/CodeEditor.tsx:highlight","to":"/workspace/apps/web/src/utils/language.ts:loadLanguage","kind":"calls","metadata":{"unresolvedName":"loadLanguage"}},{"from":"file:/workspace/apps/web/src/components/ui/ConfirmDialog.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/ui/ConfirmDialog.tsx","to":"/workspace/apps/web/src/components/ui/ConfirmDialog.tsx:ConfirmDialog","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/ui/ConfirmDialog.tsx","to":"/workspace/apps/web/src/components/ui/ConfirmDialog.tsx:ConfirmDialog","kind":"defines"},{"from":"file:/workspace/apps/web/src/components/ui/ErrorState.test.tsx","to":"external:@testing-library/react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/ui/ErrorState.test.tsx","to":"external:vitest","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/ui/ErrorState.test.tsx","to":"file:/workspace/apps/web/src/components/ui/ErrorState.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/ui/ErrorState.test.tsx","to":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/ui/ErrorState.test.tsx","to":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","to":"external:expect(screen.getByText(\"Failed to load\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Failed to load\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","to":"external:expect(screen.queryByText(\"Retry\")).not.toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.queryByText(\"Retry\")).not.toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","to":"external:screen.queryByText","kind":"calls","metadata":{"unresolvedName":"screen.queryByText"}},{"from":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","to":"external:expect(onRetry).toHaveBeenCalledTimes","kind":"calls","metadata":{"unresolvedName":"expect(onRetry).toHaveBeenCalledTimes"}},{"from":"file:/workspace/apps/web/src/components/ui/ErrorState.test.tsx","to":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/ui/ErrorState.test.tsx","to":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","to":"external:expect(screen.getByText(\"Failed to load\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Failed to load\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/components/ui/ErrorState.test.tsx","to":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/ui/ErrorState.test.tsx","to":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","to":"external:expect(screen.queryByText(\"Retry\")).not.toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.queryByText(\"Retry\")).not.toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","to":"external:screen.queryByText","kind":"calls","metadata":{"unresolvedName":"screen.queryByText"}},{"from":"file:/workspace/apps/web/src/components/ui/ErrorState.test.tsx","to":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/ui/ErrorState.test.tsx","to":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","to":"external:expect(onRetry).toHaveBeenCalledTimes","kind":"calls","metadata":{"unresolvedName":"expect(onRetry).toHaveBeenCalledTimes"}},{"from":"/workspace/apps/web/src/components/ui/ErrorState.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/components/ui/ErrorState.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/ui/Icon.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/ui/Icon.tsx","to":"external:@phosphor-icons/react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/ui/Icon.tsx","to":"/workspace/apps/web/src/components/ui/Icon.tsx:Icon","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/ui/Icon.tsx","to":"/workspace/apps/web/src/components/ui/Icon.tsx:Icon","kind":"defines"},{"from":"/workspace/apps/web/src/components/ui/Icon.tsx:Icon","to":"external:console.warn","kind":"calls","metadata":{"unresolvedName":"console.warn"}},{"from":"file:/workspace/apps/web/src/components/ui/LoadingState.test.tsx","to":"external:@testing-library/react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/ui/LoadingState.test.tsx","to":"external:vitest","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/ui/LoadingState.test.tsx","to":"file:/workspace/apps/web/src/components/ui/LoadingState.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/ui/LoadingState.test.tsx","to":"/workspace/apps/web/src/components/ui/LoadingState.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/ui/LoadingState.test.tsx","to":"/workspace/apps/web/src/components/ui/LoadingState.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/ui/LoadingState.test.tsx:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/components/ui/LoadingState.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/ui/LoadingState.test.tsx:","to":"external:expect(screen.getByText(\"Loading...\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Loading...\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/ui/LoadingState.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/ui/LoadingState.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/components/ui/LoadingState.test.tsx:","to":"external:expect(screen.getByText(\"Loading sessions...\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Loading sessions...\")).toBeInTheDocument"}},{"from":"file:/workspace/apps/web/src/components/ui/LoadingState.test.tsx","to":"/workspace/apps/web/src/components/ui/LoadingState.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/ui/LoadingState.test.tsx","to":"/workspace/apps/web/src/components/ui/LoadingState.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/ui/LoadingState.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/ui/LoadingState.test.tsx:","to":"external:expect(screen.getByText(\"Loading...\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Loading...\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/ui/LoadingState.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/ui/LoadingState.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/components/ui/LoadingState.test.tsx","to":"/workspace/apps/web/src/components/ui/LoadingState.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/ui/LoadingState.test.tsx","to":"/workspace/apps/web/src/components/ui/LoadingState.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/ui/LoadingState.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/components/ui/LoadingState.test.tsx:","to":"external:expect(screen.getByText(\"Loading sessions...\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Loading sessions...\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/components/ui/LoadingState.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/components/ui/LoadingState.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/components/ui/LoadingState.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/ui/StatusBadge.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/workspace-card.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/workspace-card.tsx","to":"external:@/components/ui/Icon","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/workspace-card.tsx","to":"file:/workspace/apps/web/src/components/workspace-instance-chips.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/workspace-card.tsx","to":"file:/workspace/apps/web/src/types/workspace.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/workspace-card.tsx","to":"/workspace/apps/web/src/components/workspace-card.tsx:WorkspaceCard","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/workspace-card.tsx","to":"/workspace/apps/web/src/components/workspace-card.tsx:WorkspaceCard","kind":"defines"},{"from":"/workspace/apps/web/src/components/workspace-card.tsx:WorkspaceCard","to":"symbol-name:onStartTool","kind":"calls","metadata":{"unresolvedName":"onStartTool"}},{"from":"/workspace/apps/web/src/components/workspace-card.tsx:WorkspaceCard","to":"symbol-name:onSync","kind":"calls","metadata":{"unresolvedName":"onSync"}},{"from":"/workspace/apps/web/src/components/workspace-card.tsx:WorkspaceCard","to":"symbol-name:onDelete","kind":"calls","metadata":{"unresolvedName":"onDelete"}},{"from":"file:/workspace/apps/web/src/components/workspace-create-form.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/workspace-create-form.tsx","to":"external:@/components/ui/Icon","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/workspace-create-form.tsx","to":"file:/workspace/apps/web/src/api/projects.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/workspace-create-form.tsx","to":"external:@/api/git-repositories","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/workspace-create-form.tsx","to":"file:/workspace/apps/web/src/api/workspaces.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/workspace-create-form.tsx","to":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/workspace-create-form.tsx","to":"file:/workspace/apps/web/src/types","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/workspace-create-form.tsx","to":"external:@/api/git-repositories","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/workspace-create-form.tsx","to":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/workspace-create-form.tsx","to":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","kind":"defines"},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","to":"symbol-name:Boolean","kind":"calls","metadata":{"unresolvedName":"Boolean"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","kind":"calls","metadata":{"unresolvedName":"useGitRepo"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","to":"external:git.branches.includes","kind":"calls","metadata":{"unresolvedName":"git.branches.includes"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","to":"symbol-name:setSelectedBranch","kind":"calls","metadata":{"unresolvedName":"setSelectedBranch"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","to":"symbol-name:setIsNewBranch","kind":"calls","metadata":{"unresolvedName":"setIsNewBranch"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","to":"/workspace/apps/web/src/api/projects.ts:listProjects","kind":"calls","metadata":{"unresolvedName":"listProjects"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","to":"symbol-name:setProjects","kind":"calls","metadata":{"unresolvedName":"setProjects"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","to":"symbol-name:setSelectedProject","kind":"calls","metadata":{"unresolvedName":"setSelectedProject"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","to":"symbol-name:setFetchingProjects","kind":"calls","metadata":{"unresolvedName":"setFetchingProjects"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:loadProjects","kind":"calls","metadata":{"unresolvedName":"loadProjects"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","to":"symbol-name:setRepos","kind":"calls","metadata":{"unresolvedName":"setRepos"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","to":"symbol-name:setSelectedRepo","kind":"calls","metadata":{"unresolvedName":"setSelectedRepo"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","to":"symbol-name:loadRepos","kind":"calls","metadata":{"unresolvedName":"loadRepos"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","to":"symbol-name:setNewBranchName","kind":"calls","metadata":{"unresolvedName":"setNewBranchName"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","to":"external:name.trim","kind":"calls","metadata":{"unresolvedName":"name.trim"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","to":"external:newBranchName.trim","kind":"calls","metadata":{"unresolvedName":"newBranchName.trim"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","to":"symbol-name:setSubmitting","kind":"calls","metadata":{"unresolvedName":"setSubmitting"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","to":"/workspace/apps/web/src/api/workspaces.ts:createWorkspaceTopLevel","kind":"calls","metadata":{"unresolvedName":"createWorkspaceTopLevel"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","to":"symbol-name:onSubmit","kind":"calls","metadata":{"unresolvedName":"onSubmit"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","to":"external:projects.map","kind":"calls","metadata":{"unresolvedName":"projects.map"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","to":"external:repos.map","kind":"calls","metadata":{"unresolvedName":"repos.map"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","to":"symbol-name:setName","kind":"calls","metadata":{"unresolvedName":"setName"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","to":"/workspace/apps/web/src/components/workspace-create-form.tsx:handleBranchChange","kind":"calls","metadata":{"unresolvedName":"handleBranchChange"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","to":"external:git.branches.map","kind":"calls","metadata":{"unresolvedName":"git.branches.map"}},{"from":"file:/workspace/apps/web/src/components/workspace-create-form.tsx","to":"/workspace/apps/web/src/components/workspace-create-form.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/workspace-create-form.tsx","to":"/workspace/apps/web/src/components/workspace-create-form.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:","to":"external:git.branches.includes","kind":"calls","metadata":{"unresolvedName":"git.branches.includes"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:","to":"symbol-name:setSelectedBranch","kind":"calls","metadata":{"unresolvedName":"setSelectedBranch"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:","to":"symbol-name:setIsNewBranch","kind":"calls","metadata":{"unresolvedName":"setIsNewBranch"}},{"from":"file:/workspace/apps/web/src/components/workspace-create-form.tsx","to":"/workspace/apps/web/src/components/workspace-create-form.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/workspace-create-form.tsx","to":"/workspace/apps/web/src/components/workspace-create-form.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:","to":"/workspace/apps/web/src/api/projects.ts:listProjects","kind":"calls","metadata":{"unresolvedName":"listProjects"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:","to":"symbol-name:setProjects","kind":"calls","metadata":{"unresolvedName":"setProjects"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:","to":"symbol-name:setSelectedProject","kind":"calls","metadata":{"unresolvedName":"setSelectedProject"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:","to":"symbol-name:setFetchingProjects","kind":"calls","metadata":{"unresolvedName":"setFetchingProjects"}},{"from":"file:/workspace/apps/web/src/components/workspace-create-form.tsx","to":"/workspace/apps/web/src/components/workspace-create-form.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/workspace-create-form.tsx","to":"/workspace/apps/web/src/components/workspace-create-form.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:loadProjects","kind":"calls","metadata":{"unresolvedName":"loadProjects"}},{"from":"file:/workspace/apps/web/src/components/workspace-create-form.tsx","to":"/workspace/apps/web/src/components/workspace-create-form.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/workspace-create-form.tsx","to":"/workspace/apps/web/src/components/workspace-create-form.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:","to":"symbol-name:setRepos","kind":"calls","metadata":{"unresolvedName":"setRepos"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:","to":"symbol-name:setSelectedRepo","kind":"calls","metadata":{"unresolvedName":"setSelectedRepo"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:","to":"symbol-name:loadRepos","kind":"calls","metadata":{"unresolvedName":"loadRepos"}},{"from":"file:/workspace/apps/web/src/components/workspace-create-form.tsx","to":"/workspace/apps/web/src/components/workspace-create-form.tsx:loadRepos","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/workspace-create-form.tsx","to":"/workspace/apps/web/src/components/workspace-create-form.tsx:loadRepos","kind":"defines"},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:loadRepos","to":"symbol-name:setRepos","kind":"calls","metadata":{"unresolvedName":"setRepos"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:loadRepos","to":"symbol-name:setSelectedRepo","kind":"calls","metadata":{"unresolvedName":"setSelectedRepo"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:loadRepos","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"file:/workspace/apps/web/src/components/workspace-create-form.tsx","to":"/workspace/apps/web/src/components/workspace-create-form.tsx:handleBranchChange","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/workspace-create-form.tsx","to":"/workspace/apps/web/src/components/workspace-create-form.tsx:handleBranchChange","kind":"defines"},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:handleBranchChange","to":"symbol-name:setIsNewBranch","kind":"calls","metadata":{"unresolvedName":"setIsNewBranch"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:handleBranchChange","to":"symbol-name:setSelectedBranch","kind":"calls","metadata":{"unresolvedName":"setSelectedBranch"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:handleBranchChange","to":"symbol-name:setNewBranchName","kind":"calls","metadata":{"unresolvedName":"setNewBranchName"}},{"from":"file:/workspace/apps/web/src/components/workspace-create-form.tsx","to":"/workspace/apps/web/src/components/workspace-create-form.tsx:handleSubmit","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/workspace-create-form.tsx","to":"/workspace/apps/web/src/components/workspace-create-form.tsx:handleSubmit","kind":"defines"},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:handleSubmit","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:handleSubmit","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:handleSubmit","to":"external:name.trim","kind":"calls","metadata":{"unresolvedName":"name.trim"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:handleSubmit","to":"external:newBranchName.trim","kind":"calls","metadata":{"unresolvedName":"newBranchName.trim"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:handleSubmit","to":"symbol-name:setSubmitting","kind":"calls","metadata":{"unresolvedName":"setSubmitting"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:handleSubmit","to":"/workspace/apps/web/src/api/workspaces.ts:createWorkspaceTopLevel","kind":"calls","metadata":{"unresolvedName":"createWorkspaceTopLevel"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:handleSubmit","to":"symbol-name:onSubmit","kind":"calls","metadata":{"unresolvedName":"onSubmit"}},{"from":"file:/workspace/apps/web/src/components/workspace-create-form.tsx","to":"/workspace/apps/web/src/components/workspace-create-form.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/workspace-create-form.tsx","to":"/workspace/apps/web/src/components/workspace-create-form.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:","to":"symbol-name:setSelectedProject","kind":"calls","metadata":{"unresolvedName":"setSelectedProject"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:","to":"symbol-name:setSelectedBranch","kind":"calls","metadata":{"unresolvedName":"setSelectedBranch"}},{"from":"file:/workspace/apps/web/src/components/workspace-create-form.tsx","to":"/workspace/apps/web/src/components/workspace-create-form.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/workspace-create-form.tsx","to":"/workspace/apps/web/src/components/workspace-create-form.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:","to":"symbol-name:setSelectedRepo","kind":"calls","metadata":{"unresolvedName":"setSelectedRepo"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:","to":"symbol-name:setSelectedBranch","kind":"calls","metadata":{"unresolvedName":"setSelectedBranch"}},{"from":"file:/workspace/apps/web/src/components/workspace-instance-chips.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/workspace-instance-chips.tsx","to":"file:/workspace/apps/web/src/api/workspace-instances.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/workspace-instance-chips.tsx","to":"file:/workspace/apps/web/src/api/sessions.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/workspace-instance-chips.tsx","to":"/workspace/apps/web/src/components/workspace-instance-chips.tsx:WorkspaceInstanceChips","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/workspace-instance-chips.tsx","to":"/workspace/apps/web/src/components/workspace-instance-chips.tsx:WorkspaceInstanceChips","kind":"defines"},{"from":"/workspace/apps/web/src/components/workspace-instance-chips.tsx:WorkspaceInstanceChips","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/workspace-instance-chips.tsx:WorkspaceInstanceChips","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/components/workspace-instance-chips.tsx:WorkspaceInstanceChips","to":"/workspace/apps/web/src/api/workspace-instances.ts:listWorkspaceInstances","kind":"calls","metadata":{"unresolvedName":"listWorkspaceInstances"}},{"from":"/workspace/apps/web/src/components/workspace-instance-chips.tsx:WorkspaceInstanceChips","to":"symbol-name:setInstances","kind":"calls","metadata":{"unresolvedName":"setInstances"}},{"from":"/workspace/apps/web/src/components/workspace-instance-chips.tsx:WorkspaceInstanceChips","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/components/workspace-instance-chips.tsx:WorkspaceInstanceChips","to":"symbol-name:load","kind":"calls","metadata":{"unresolvedName":"load"}},{"from":"/workspace/apps/web/src/components/workspace-instance-chips.tsx:WorkspaceInstanceChips","to":"external:instances.map","kind":"calls","metadata":{"unresolvedName":"instances.map"}},{"from":"/workspace/apps/web/src/components/workspace-instance-chips.tsx:WorkspaceInstanceChips","to":"external:e.stopPropagation","kind":"calls","metadata":{"unresolvedName":"e.stopPropagation"}},{"from":"file:/workspace/apps/web/src/components/workspace-instance-chips.tsx","to":"/workspace/apps/web/src/components/workspace-instance-chips.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/workspace-instance-chips.tsx","to":"/workspace/apps/web/src/components/workspace-instance-chips.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/workspace-instance-chips.tsx:","to":"/workspace/apps/web/src/api/workspace-instances.ts:listWorkspaceInstances","kind":"calls","metadata":{"unresolvedName":"listWorkspaceInstances"}},{"from":"/workspace/apps/web/src/components/workspace-instance-chips.tsx:","to":"symbol-name:setInstances","kind":"calls","metadata":{"unresolvedName":"setInstances"}},{"from":"/workspace/apps/web/src/components/workspace-instance-chips.tsx:","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/components/workspace-instance-chips.tsx:","to":"symbol-name:load","kind":"calls","metadata":{"unresolvedName":"load"}},{"from":"file:/workspace/apps/web/src/components/workspace-instance-chips.tsx","to":"/workspace/apps/web/src/components/workspace-instance-chips.tsx:load","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/workspace-instance-chips.tsx","to":"/workspace/apps/web/src/components/workspace-instance-chips.tsx:load","kind":"defines"},{"from":"/workspace/apps/web/src/components/workspace-instance-chips.tsx:load","to":"/workspace/apps/web/src/api/workspace-instances.ts:listWorkspaceInstances","kind":"calls","metadata":{"unresolvedName":"listWorkspaceInstances"}},{"from":"/workspace/apps/web/src/components/workspace-instance-chips.tsx:load","to":"symbol-name:setInstances","kind":"calls","metadata":{"unresolvedName":"setInstances"}},{"from":"/workspace/apps/web/src/components/workspace-instance-chips.tsx:load","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"file:/workspace/apps/web/src/hooks/use-async-data.ts","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-async-data.ts","to":"/workspace/apps/web/src/hooks/use-async-data.ts:useAsyncData","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-async-data.ts","to":"/workspace/apps/web/src/hooks/use-async-data.ts:useAsyncData","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-async-data.ts:useAsyncData","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/hooks/use-async-data.ts:useAsyncData","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/hooks/use-async-data.ts:useAsyncData","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/hooks/use-async-data.ts:useAsyncData","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/hooks/use-async-data.ts:useAsyncData","to":"symbol-name:fetcher","kind":"calls","metadata":{"unresolvedName":"fetcher"}},{"from":"/workspace/apps/web/src/hooks/use-async-data.ts:useAsyncData","to":"symbol-name:setData","kind":"calls","metadata":{"unresolvedName":"setData"}},{"from":"/workspace/apps/web/src/hooks/use-async-data.ts:useAsyncData","to":"symbol-name:load","kind":"calls","metadata":{"unresolvedName":"load"}},{"from":"/workspace/apps/web/src/hooks/use-async-data.ts:useAsyncData","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"file:/workspace/apps/web/src/hooks/use-async-data.ts","to":"/workspace/apps/web/src/hooks/use-async-data.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-async-data.ts","to":"/workspace/apps/web/src/hooks/use-async-data.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-async-data.ts:","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/hooks/use-async-data.ts:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/hooks/use-async-data.ts:","to":"symbol-name:fetcher","kind":"calls","metadata":{"unresolvedName":"fetcher"}},{"from":"/workspace/apps/web/src/hooks/use-async-data.ts:","to":"symbol-name:setData","kind":"calls","metadata":{"unresolvedName":"setData"}},{"from":"file:/workspace/apps/web/src/hooks/use-async-data.ts","to":"/workspace/apps/web/src/hooks/use-async-data.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-async-data.ts","to":"/workspace/apps/web/src/hooks/use-async-data.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-async-data.ts:","to":"symbol-name:load","kind":"calls","metadata":{"unresolvedName":"load"}},{"from":"file:/workspace/apps/web/src/hooks/use-async-data.ts","to":"/workspace/apps/web/src/hooks/use-async-data.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-async-data.ts","to":"/workspace/apps/web/src/hooks/use-async-data.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-async-data.ts:","to":"symbol-name:load","kind":"calls","metadata":{"unresolvedName":"load"}},{"from":"file:/workspace/apps/web/src/hooks/use-auto-hide.ts","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-auto-hide.ts","to":"/workspace/apps/web/src/hooks/use-auto-hide.ts:useAutoHide","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-auto-hide.ts","to":"/workspace/apps/web/src/hooks/use-auto-hide.ts:useAutoHide","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-auto-hide.ts:useAutoHide","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/hooks/use-auto-hide.ts:useAutoHide","to":"symbol-name:useRef","kind":"calls","metadata":{"unresolvedName":"useRef"}},{"from":"/workspace/apps/web/src/hooks/use-auto-hide.ts:useAutoHide","to":"external:Date.now","kind":"calls","metadata":{"unresolvedName":"Date.now"}},{"from":"/workspace/apps/web/src/hooks/use-auto-hide.ts:useAutoHide","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/hooks/use-auto-hide.ts:useAutoHide","to":"symbol-name:setIsVisible","kind":"calls","metadata":{"unresolvedName":"setIsVisible"}},{"from":"/workspace/apps/web/src/hooks/use-auto-hide.ts:useAutoHide","to":"symbol-name:clearTimeout","kind":"calls","metadata":{"unresolvedName":"clearTimeout"}},{"from":"/workspace/apps/web/src/hooks/use-auto-hide.ts:useAutoHide","to":"symbol-name:setTimeout","kind":"calls","metadata":{"unresolvedName":"setTimeout"}},{"from":"/workspace/apps/web/src/hooks/use-auto-hide.ts:useAutoHide","to":"symbol-name:hide","kind":"calls","metadata":{"unresolvedName":"hide"}},{"from":"/workspace/apps/web/src/hooks/use-auto-hide.ts:useAutoHide","to":"symbol-name:show","kind":"calls","metadata":{"unresolvedName":"show"}},{"from":"/workspace/apps/web/src/hooks/use-auto-hide.ts:useAutoHide","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"file:/workspace/apps/web/src/hooks/use-auto-hide.ts","to":"/workspace/apps/web/src/hooks/use-auto-hide.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-auto-hide.ts","to":"/workspace/apps/web/src/hooks/use-auto-hide.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-auto-hide.ts:","to":"symbol-name:setIsVisible","kind":"calls","metadata":{"unresolvedName":"setIsVisible"}},{"from":"/workspace/apps/web/src/hooks/use-auto-hide.ts:","to":"external:Date.now","kind":"calls","metadata":{"unresolvedName":"Date.now"}},{"from":"/workspace/apps/web/src/hooks/use-auto-hide.ts:","to":"symbol-name:clearTimeout","kind":"calls","metadata":{"unresolvedName":"clearTimeout"}},{"from":"/workspace/apps/web/src/hooks/use-auto-hide.ts:","to":"symbol-name:setTimeout","kind":"calls","metadata":{"unresolvedName":"setTimeout"}},{"from":"file:/workspace/apps/web/src/hooks/use-auto-hide.ts","to":"/workspace/apps/web/src/hooks/use-auto-hide.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-auto-hide.ts","to":"/workspace/apps/web/src/hooks/use-auto-hide.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-auto-hide.ts:","to":"symbol-name:setIsVisible","kind":"calls","metadata":{"unresolvedName":"setIsVisible"}},{"from":"file:/workspace/apps/web/src/hooks/use-auto-hide.ts","to":"/workspace/apps/web/src/hooks/use-auto-hide.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-auto-hide.ts","to":"/workspace/apps/web/src/hooks/use-auto-hide.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-auto-hide.ts:","to":"symbol-name:setIsVisible","kind":"calls","metadata":{"unresolvedName":"setIsVisible"}},{"from":"/workspace/apps/web/src/hooks/use-auto-hide.ts:","to":"symbol-name:clearTimeout","kind":"calls","metadata":{"unresolvedName":"clearTimeout"}},{"from":"file:/workspace/apps/web/src/hooks/use-auto-hide.ts","to":"/workspace/apps/web/src/hooks/use-auto-hide.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-auto-hide.ts","to":"/workspace/apps/web/src/hooks/use-auto-hide.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-auto-hide.ts:","to":"symbol-name:hide","kind":"calls","metadata":{"unresolvedName":"hide"}},{"from":"/workspace/apps/web/src/hooks/use-auto-hide.ts:","to":"symbol-name:show","kind":"calls","metadata":{"unresolvedName":"show"}},{"from":"file:/workspace/apps/web/src/hooks/use-auto-hide.ts","to":"/workspace/apps/web/src/hooks/use-auto-hide.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-auto-hide.ts","to":"/workspace/apps/web/src/hooks/use-auto-hide.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-auto-hide.ts:","to":"symbol-name:setIsVisible","kind":"calls","metadata":{"unresolvedName":"setIsVisible"}},{"from":"/workspace/apps/web/src/hooks/use-auto-hide.ts:","to":"symbol-name:show","kind":"calls","metadata":{"unresolvedName":"show"}},{"from":"/workspace/apps/web/src/hooks/use-auto-hide.ts:","to":"symbol-name:clearTimeout","kind":"calls","metadata":{"unresolvedName":"clearTimeout"}},{"from":"file:/workspace/apps/web/src/hooks/use-auto-hide.ts","to":"/workspace/apps/web/src/hooks/use-auto-hide.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-auto-hide.ts","to":"/workspace/apps/web/src/hooks/use-auto-hide.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-auto-hide.ts:","to":"symbol-name:clearTimeout","kind":"calls","metadata":{"unresolvedName":"clearTimeout"}},{"from":"file:/workspace/apps/web/src/hooks/use-dashboard-actions.ts","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-dashboard-actions.ts","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-dashboard-actions.ts","to":"file:/workspace/apps/web/src/api/sessions.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-dashboard-actions.ts","to":"file:/workspace/apps/web/src/api/settings.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-dashboard-actions.ts","to":"file:/workspace/apps/web/src/types/session.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-dashboard-actions.ts","to":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:useDashboardActions","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-dashboard-actions.ts","to":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:useDashboardActions","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:useDashboardActions","to":"symbol-name:useNavigate","kind":"calls","metadata":{"unresolvedName":"useNavigate"}},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:useDashboardActions","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:useDashboardActions","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:useDashboardActions","to":"symbol-name:setSaveState","kind":"calls","metadata":{"unresolvedName":"setSaveState"}},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:useDashboardActions","to":"/workspace/apps/web/src/api/settings.ts:updateUserConfig","kind":"calls","metadata":{"unresolvedName":"updateUserConfig"}},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:useDashboardActions","to":"symbol-name:onRefresh","kind":"calls","metadata":{"unresolvedName":"onRefresh"}},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:useDashboardActions","to":"external:window.open","kind":"calls","metadata":{"unresolvedName":"window.open"}},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:useDashboardActions","to":"external:session.tool_type_interfaces.includes","kind":"calls","metadata":{"unresolvedName":"session.tool_type_interfaces.includes"}},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:useDashboardActions","to":"symbol-name:navigate","kind":"calls","metadata":{"unresolvedName":"navigate"}},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:useDashboardActions","to":"symbol-name:setActionBusy","kind":"calls","metadata":{"unresolvedName":"setActionBusy"}},{"from":"file:/workspace/apps/web/src/hooks/use-dashboard-actions.ts","to":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-dashboard-actions.ts","to":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:","to":"symbol-name:setSaveState","kind":"calls","metadata":{"unresolvedName":"setSaveState"}},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:","to":"/workspace/apps/web/src/api/settings.ts:updateUserConfig","kind":"calls","metadata":{"unresolvedName":"updateUserConfig"}},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:","to":"symbol-name:onRefresh","kind":"calls","metadata":{"unresolvedName":"onRefresh"}},{"from":"file:/workspace/apps/web/src/hooks/use-dashboard-actions.ts","to":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-dashboard-actions.ts","to":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:","to":"external:window.open","kind":"calls","metadata":{"unresolvedName":"window.open"}},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:","to":"external:session.tool_type_interfaces.includes","kind":"calls","metadata":{"unresolvedName":"session.tool_type_interfaces.includes"}},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:","to":"symbol-name:navigate","kind":"calls","metadata":{"unresolvedName":"navigate"}},{"from":"file:/workspace/apps/web/src/hooks/use-dashboard-actions.ts","to":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-dashboard-actions.ts","to":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:","to":"symbol-name:setActionBusy","kind":"calls","metadata":{"unresolvedName":"setActionBusy"}},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:","to":"symbol-name:onRefresh","kind":"calls","metadata":{"unresolvedName":"onRefresh"}},{"from":"file:/workspace/apps/web/src/hooks/use-dashboard-actions.ts","to":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-dashboard-actions.ts","to":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:","to":"symbol-name:setActionBusy","kind":"calls","metadata":{"unresolvedName":"setActionBusy"}},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:","to":"symbol-name:onRefresh","kind":"calls","metadata":{"unresolvedName":"onRefresh"}},{"from":"file:/workspace/apps/web/src/hooks/use-dashboard-actions.ts","to":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-dashboard-actions.ts","to":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:","to":"symbol-name:setActionBusy","kind":"calls","metadata":{"unresolvedName":"setActionBusy"}},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:","to":"symbol-name:onRefresh","kind":"calls","metadata":{"unresolvedName":"onRefresh"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"external:vitest","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"external:@testing-library/react","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"file:/workspace/apps/web/src/hooks/use-events.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"file:/workspace/apps/web/src/api/events.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:beforeEach","kind":"calls","metadata":{"unresolvedName":"beforeEach"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:vi.useFakeTimers","kind":"calls","metadata":{"unresolvedName":"vi.useFakeTimers"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:mockedCreateEventSource.mockReturnValue","kind":"calls","metadata":{"unresolvedName":"mockedCreateEventSource.mockReturnValue"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:mockedProbeEventStreamStatus.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedProbeEventStreamStatus.mockResolvedValue"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:afterEach","kind":"calls","metadata":{"unresolvedName":"afterEach"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:vi.useRealTimers","kind":"calls","metadata":{"unresolvedName":"vi.useRealTimers"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:vi.clearAllMocks","kind":"calls","metadata":{"unresolvedName":"vi.clearAllMocks"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","kind":"calls","metadata":{"unresolvedName":"useEvents"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:mockEs.onopen","kind":"calls","metadata":{"unresolvedName":"mockEs.onopen"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:mockEs.onmessage","kind":"calls","metadata":{"unresolvedName":"mockEs.onmessage"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:JSON.stringify","kind":"calls","metadata":{"unresolvedName":"JSON.stringify"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:expect(result.current.events).toHaveLength","kind":"calls","metadata":{"unresolvedName":"expect(result.current.events).toHaveLength"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:expect(result.current.events[0].instance_id).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.current.events[0].instance_id).toBe"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:expect(result.current.connected).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.current.connected).toBe"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:mockEs.onerror","kind":"calls","metadata":{"unresolvedName":"mockEs.onerror"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:expect(mockEs.close).toHaveBeenCalled","kind":"calls","metadata":{"unresolvedName":"expect(mockEs.close).toHaveBeenCalled"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:expect(mockedCreateEventSource).toHaveBeenCalledTimes","kind":"calls","metadata":{"unresolvedName":"expect(mockedCreateEventSource).toHaveBeenCalledTimes"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:secondEs.onerror","kind":"calls","metadata":{"unresolvedName":"secondEs.onerror"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:currentEs.onerror","kind":"calls","metadata":{"unresolvedName":"currentEs.onerror"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:expect(mockedCreateEventSource.mock.calls.length).toBeGreaterThan","kind":"calls","metadata":{"unresolvedName":"expect(mockedCreateEventSource.mock.calls.length).toBeGreaterThan"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:expect(window.location.assign).toHaveBeenCalled","kind":"calls","metadata":{"unresolvedName":"expect(window.location.assign).toHaveBeenCalled"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:unmount","kind":"calls","metadata":{"unresolvedName":"unmount"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:expect(result.current.reconnectCount).toBeGreaterThan","kind":"calls","metadata":{"unresolvedName":"expect(result.current.reconnectCount).toBeGreaterThan"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:vi.useFakeTimers","kind":"calls","metadata":{"unresolvedName":"vi.useFakeTimers"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:mockedCreateEventSource.mockReturnValue","kind":"calls","metadata":{"unresolvedName":"mockedCreateEventSource.mockReturnValue"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:mockedProbeEventStreamStatus.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedProbeEventStreamStatus.mockResolvedValue"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:vi.useRealTimers","kind":"calls","metadata":{"unresolvedName":"vi.useRealTimers"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:vi.clearAllMocks","kind":"calls","metadata":{"unresolvedName":"vi.clearAllMocks"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","kind":"calls","metadata":{"unresolvedName":"useEvents"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:mockEs.onopen","kind":"calls","metadata":{"unresolvedName":"mockEs.onopen"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:mockEs.onmessage","kind":"calls","metadata":{"unresolvedName":"mockEs.onmessage"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:JSON.stringify","kind":"calls","metadata":{"unresolvedName":"JSON.stringify"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:expect(result.current.events).toHaveLength","kind":"calls","metadata":{"unresolvedName":"expect(result.current.events).toHaveLength"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:expect(result.current.events[0].instance_id).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.current.events[0].instance_id).toBe"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:expect(result.current.connected).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.current.connected).toBe"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:mockEs.onopen","kind":"calls","metadata":{"unresolvedName":"mockEs.onopen"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:mockEs.onmessage","kind":"calls","metadata":{"unresolvedName":"mockEs.onmessage"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:JSON.stringify","kind":"calls","metadata":{"unresolvedName":"JSON.stringify"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:expect(result.current.events).toHaveLength","kind":"calls","metadata":{"unresolvedName":"expect(result.current.events).toHaveLength"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:expect(result.current.events[0].instance_id).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.current.events[0].instance_id).toBe"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","kind":"calls","metadata":{"unresolvedName":"useEvents"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:mockEs.onerror","kind":"calls","metadata":{"unresolvedName":"mockEs.onerror"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:expect(mockEs.close).toHaveBeenCalled","kind":"calls","metadata":{"unresolvedName":"expect(mockEs.close).toHaveBeenCalled"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:expect(mockedCreateEventSource).toHaveBeenCalledTimes","kind":"calls","metadata":{"unresolvedName":"expect(mockedCreateEventSource).toHaveBeenCalledTimes"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:secondEs.onerror","kind":"calls","metadata":{"unresolvedName":"secondEs.onerror"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:mockEs.onerror","kind":"calls","metadata":{"unresolvedName":"mockEs.onerror"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:secondEs.onerror","kind":"calls","metadata":{"unresolvedName":"secondEs.onerror"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","kind":"calls","metadata":{"unresolvedName":"useEvents"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:currentEs.onerror","kind":"calls","metadata":{"unresolvedName":"currentEs.onerror"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:expect(mockedCreateEventSource.mock.calls.length).toBeGreaterThan","kind":"calls","metadata":{"unresolvedName":"expect(mockedCreateEventSource.mock.calls.length).toBeGreaterThan"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:currentEs.onerror","kind":"calls","metadata":{"unresolvedName":"currentEs.onerror"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:mockedProbeEventStreamStatus.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedProbeEventStreamStatus.mockResolvedValue"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","kind":"calls","metadata":{"unresolvedName":"useEvents"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:mockEs.onerror","kind":"calls","metadata":{"unresolvedName":"mockEs.onerror"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:expect(window.location.assign).toHaveBeenCalled","kind":"calls","metadata":{"unresolvedName":"expect(window.location.assign).toHaveBeenCalled"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:mockEs.onerror","kind":"calls","metadata":{"unresolvedName":"mockEs.onerror"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:mockedProbeEventStreamStatus.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedProbeEventStreamStatus.mockResolvedValue"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","kind":"calls","metadata":{"unresolvedName":"useEvents"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:mockEs.onerror","kind":"calls","metadata":{"unresolvedName":"mockEs.onerror"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:expect(mockedCreateEventSource).toHaveBeenCalledTimes","kind":"calls","metadata":{"unresolvedName":"expect(mockedCreateEventSource).toHaveBeenCalledTimes"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:mockEs.onerror","kind":"calls","metadata":{"unresolvedName":"mockEs.onerror"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","kind":"calls","metadata":{"unresolvedName":"useEvents"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:unmount","kind":"calls","metadata":{"unresolvedName":"unmount"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:expect(mockEs.close).toHaveBeenCalled","kind":"calls","metadata":{"unresolvedName":"expect(mockEs.close).toHaveBeenCalled"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","kind":"calls","metadata":{"unresolvedName":"useEvents"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:mockEs.onerror","kind":"calls","metadata":{"unresolvedName":"mockEs.onerror"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:expect(result.current.reconnectCount).toBeGreaterThan","kind":"calls","metadata":{"unresolvedName":"expect(result.current.reconnectCount).toBeGreaterThan"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:mockEs.onerror","kind":"calls","metadata":{"unresolvedName":"mockEs.onerror"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.test.ts","to":"/workspace/apps/web/src/hooks/use-events.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"external:expect(result.current.reconnectCount).toBeGreaterThan","kind":"calls","metadata":{"unresolvedName":"expect(result.current.reconnectCount).toBeGreaterThan"}},{"from":"/workspace/apps/web/src/hooks/use-events.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.ts","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-events.ts","to":"file:/workspace/apps/web/src/api/events.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-events.ts","to":"file:/workspace/apps/web/src/types/events.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-events.ts","to":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.ts","to":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","to":"symbol-name:useRef","kind":"calls","metadata":{"unresolvedName":"useRef"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","to":"symbol-name:clearTimeout","kind":"calls","metadata":{"unresolvedName":"clearTimeout"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","to":"/workspace/apps/web/src/api/events.ts:createEventSource","kind":"calls","metadata":{"unresolvedName":"createEventSource"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","to":"symbol-name:setConnected","kind":"calls","metadata":{"unresolvedName":"setConnected"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","to":"symbol-name:setReconnectCount","kind":"calls","metadata":{"unresolvedName":"setReconnectCount"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","to":"external:JSON.parse","kind":"calls","metadata":{"unresolvedName":"JSON.parse"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","to":"symbol-name:setEvents","kind":"calls","metadata":{"unresolvedName":"setEvents"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","to":"external:es.close","kind":"calls","metadata":{"unresolvedName":"es.close"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","to":"external:Math.min","kind":"calls","metadata":{"unresolvedName":"Math.min"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","to":"external:Math.pow","kind":"calls","metadata":{"unresolvedName":"Math.pow"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","to":"external:Math.random","kind":"calls","metadata":{"unresolvedName":"Math.random"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","to":"symbol-name:setTimeout","kind":"calls","metadata":{"unresolvedName":"setTimeout"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","to":"/workspace/apps/web/src/api/events.ts:probeEventStreamStatus","kind":"calls","metadata":{"unresolvedName":"probeEventStreamStatus"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","to":"external:window.location.assign","kind":"calls","metadata":{"unresolvedName":"window.location.assign"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","to":"symbol-name:connect","kind":"calls","metadata":{"unresolvedName":"connect"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","to":"external:esRef.current.close","kind":"calls","metadata":{"unresolvedName":"esRef.current.close"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.ts","to":"/workspace/apps/web/src/hooks/use-events.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.ts","to":"/workspace/apps/web/src/hooks/use-events.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"symbol-name:clearTimeout","kind":"calls","metadata":{"unresolvedName":"clearTimeout"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"/workspace/apps/web/src/api/events.ts:createEventSource","kind":"calls","metadata":{"unresolvedName":"createEventSource"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"symbol-name:setConnected","kind":"calls","metadata":{"unresolvedName":"setConnected"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"symbol-name:setReconnectCount","kind":"calls","metadata":{"unresolvedName":"setReconnectCount"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"external:JSON.parse","kind":"calls","metadata":{"unresolvedName":"JSON.parse"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"symbol-name:setEvents","kind":"calls","metadata":{"unresolvedName":"setEvents"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"external:es.close","kind":"calls","metadata":{"unresolvedName":"es.close"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"external:Math.min","kind":"calls","metadata":{"unresolvedName":"Math.min"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"external:Math.pow","kind":"calls","metadata":{"unresolvedName":"Math.pow"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"external:Math.random","kind":"calls","metadata":{"unresolvedName":"Math.random"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"symbol-name:setTimeout","kind":"calls","metadata":{"unresolvedName":"setTimeout"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"/workspace/apps/web/src/api/events.ts:probeEventStreamStatus","kind":"calls","metadata":{"unresolvedName":"probeEventStreamStatus"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"external:window.location.assign","kind":"calls","metadata":{"unresolvedName":"window.location.assign"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"symbol-name:connect","kind":"calls","metadata":{"unresolvedName":"connect"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.ts","to":"/workspace/apps/web/src/hooks/use-events.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.ts","to":"/workspace/apps/web/src/hooks/use-events.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"symbol-name:setConnected","kind":"calls","metadata":{"unresolvedName":"setConnected"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"symbol-name:setReconnectCount","kind":"calls","metadata":{"unresolvedName":"setReconnectCount"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.ts","to":"/workspace/apps/web/src/hooks/use-events.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.ts","to":"/workspace/apps/web/src/hooks/use-events.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"external:JSON.parse","kind":"calls","metadata":{"unresolvedName":"JSON.parse"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"symbol-name:setEvents","kind":"calls","metadata":{"unresolvedName":"setEvents"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.ts","to":"/workspace/apps/web/src/hooks/use-events.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.ts","to":"/workspace/apps/web/src/hooks/use-events.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"symbol-name:setConnected","kind":"calls","metadata":{"unresolvedName":"setConnected"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"external:es.close","kind":"calls","metadata":{"unresolvedName":"es.close"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"external:Math.min","kind":"calls","metadata":{"unresolvedName":"Math.min"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"external:Math.pow","kind":"calls","metadata":{"unresolvedName":"Math.pow"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"external:Math.random","kind":"calls","metadata":{"unresolvedName":"Math.random"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"symbol-name:setReconnectCount","kind":"calls","metadata":{"unresolvedName":"setReconnectCount"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"symbol-name:setTimeout","kind":"calls","metadata":{"unresolvedName":"setTimeout"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"/workspace/apps/web/src/api/events.ts:probeEventStreamStatus","kind":"calls","metadata":{"unresolvedName":"probeEventStreamStatus"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"external:window.location.assign","kind":"calls","metadata":{"unresolvedName":"window.location.assign"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"symbol-name:connect","kind":"calls","metadata":{"unresolvedName":"connect"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.ts","to":"/workspace/apps/web/src/hooks/use-events.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.ts","to":"/workspace/apps/web/src/hooks/use-events.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"/workspace/apps/web/src/api/events.ts:probeEventStreamStatus","kind":"calls","metadata":{"unresolvedName":"probeEventStreamStatus"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"external:window.location.assign","kind":"calls","metadata":{"unresolvedName":"window.location.assign"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"symbol-name:setTimeout","kind":"calls","metadata":{"unresolvedName":"setTimeout"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"symbol-name:connect","kind":"calls","metadata":{"unresolvedName":"connect"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.ts","to":"/workspace/apps/web/src/hooks/use-events.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.ts","to":"/workspace/apps/web/src/hooks/use-events.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"symbol-name:connect","kind":"calls","metadata":{"unresolvedName":"connect"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.ts","to":"/workspace/apps/web/src/hooks/use-events.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.ts","to":"/workspace/apps/web/src/hooks/use-events.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"symbol-name:connect","kind":"calls","metadata":{"unresolvedName":"connect"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"symbol-name:clearTimeout","kind":"calls","metadata":{"unresolvedName":"clearTimeout"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"external:esRef.current.close","kind":"calls","metadata":{"unresolvedName":"esRef.current.close"}},{"from":"file:/workspace/apps/web/src/hooks/use-events.ts","to":"/workspace/apps/web/src/hooks/use-events.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-events.ts","to":"/workspace/apps/web/src/hooks/use-events.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"symbol-name:clearTimeout","kind":"calls","metadata":{"unresolvedName":"clearTimeout"}},{"from":"/workspace/apps/web/src/hooks/use-events.ts:","to":"external:esRef.current.close","kind":"calls","metadata":{"unresolvedName":"esRef.current.close"}},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"external:@/api/git-repositories","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","to":"symbol-name:async","kind":"calls","metadata":{"unresolvedName":"async"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","to":"symbol-name:fn","kind":"calls","metadata":{"unresolvedName":"fn"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:extractError","kind":"calls","metadata":{"unresolvedName":"extractError"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","to":"symbol-name:withLoading","kind":"calls","metadata":{"unresolvedName":"withLoading"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","to":"/workspace/apps/web/src/api/git-repositories.ts:listRepositoryBranches","kind":"calls","metadata":{"unresolvedName":"listRepositoryBranches"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","to":"symbol-name:setBranches","kind":"calls","metadata":{"unresolvedName":"setBranches"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","to":"external:data.branches.map","kind":"calls","metadata":{"unresolvedName":"data.branches.map"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","to":"symbol-name:setDefaultBranch","kind":"calls","metadata":{"unresolvedName":"setDefaultBranch"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","to":"symbol-name:setHistory","kind":"calls","metadata":{"unresolvedName":"setHistory"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","to":"symbol-name:setCommitDetail","kind":"calls","metadata":{"unresolvedName":"setCommitDetail"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","to":"symbol-name:refreshStatus","kind":"calls","metadata":{"unresolvedName":"refreshStatus"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","to":"symbol-name:refreshBranches","kind":"calls","metadata":{"unresolvedName":"refreshBranches"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","to":"symbol-name:refreshHistory","kind":"calls","metadata":{"unresolvedName":"refreshHistory"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:extractError","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:extractError","kind":"defines"},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:withLoading","kind":"calls","metadata":{"unresolvedName":"withLoading"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"/workspace/apps/web/src/api/git-repositories.ts:listRepositoryBranches","kind":"calls","metadata":{"unresolvedName":"listRepositoryBranches"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:setBranches","kind":"calls","metadata":{"unresolvedName":"setBranches"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"external:data.branches.map","kind":"calls","metadata":{"unresolvedName":"data.branches.map"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:setDefaultBranch","kind":"calls","metadata":{"unresolvedName":"setDefaultBranch"}},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:withLoading","kind":"calls","metadata":{"unresolvedName":"withLoading"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:withLoading","kind":"calls","metadata":{"unresolvedName":"withLoading"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:setHistory","kind":"calls","metadata":{"unresolvedName":"setHistory"}},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:withLoading","kind":"calls","metadata":{"unresolvedName":"withLoading"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:setCommitDetail","kind":"calls","metadata":{"unresolvedName":"setCommitDetail"}},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:withLoading","kind":"calls","metadata":{"unresolvedName":"withLoading"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:refreshStatus","kind":"calls","metadata":{"unresolvedName":"refreshStatus"}},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:withLoading","kind":"calls","metadata":{"unresolvedName":"withLoading"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:refreshStatus","kind":"calls","metadata":{"unresolvedName":"refreshStatus"}},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:withLoading","kind":"calls","metadata":{"unresolvedName":"withLoading"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:refreshStatus","kind":"calls","metadata":{"unresolvedName":"refreshStatus"}},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:withLoading","kind":"calls","metadata":{"unresolvedName":"withLoading"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:refreshStatus","kind":"calls","metadata":{"unresolvedName":"refreshStatus"}},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:withLoading","kind":"calls","metadata":{"unresolvedName":"withLoading"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:refreshStatus","kind":"calls","metadata":{"unresolvedName":"refreshStatus"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:refreshBranches","kind":"calls","metadata":{"unresolvedName":"refreshBranches"}},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:withLoading","kind":"calls","metadata":{"unresolvedName":"withLoading"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:refreshBranches","kind":"calls","metadata":{"unresolvedName":"refreshBranches"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:refreshStatus","kind":"calls","metadata":{"unresolvedName":"refreshStatus"}},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:withLoading","kind":"calls","metadata":{"unresolvedName":"withLoading"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:refreshBranches","kind":"calls","metadata":{"unresolvedName":"refreshBranches"}},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:withLoading","kind":"calls","metadata":{"unresolvedName":"withLoading"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:refreshStatus","kind":"calls","metadata":{"unresolvedName":"refreshStatus"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:refreshHistory","kind":"calls","metadata":{"unresolvedName":"refreshHistory"}},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-git-repo.ts","to":"/workspace/apps/web/src/hooks/use-git-repo.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:refreshBranches","kind":"calls","metadata":{"unresolvedName":"refreshBranches"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:setBranches","kind":"calls","metadata":{"unresolvedName":"setBranches"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"symbol-name:setDefaultBranch","kind":"calls","metadata":{"unresolvedName":"setDefaultBranch"}},{"from":"file:/workspace/apps/web/src/hooks/use-instance-actions.ts","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-instance-actions.ts","to":"file:/workspace/apps/web/src/api/sessions.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-instance-actions.ts","to":"file:/workspace/apps/web/src/api/sessions.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-instance-actions.ts","to":"/workspace/apps/web/src/hooks/use-instance-actions.ts:useInstanceActions","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-instance-actions.ts","to":"/workspace/apps/web/src/hooks/use-instance-actions.ts:useInstanceActions","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:useInstanceActions","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:useInstanceActions","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:useInstanceActions","to":"external:window.open","kind":"calls","metadata":{"unresolvedName":"window.open"}},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:useInstanceActions","to":"external:session.tool_type_interfaces?.includes","kind":"calls","metadata":{"unresolvedName":"session.tool_type_interfaces?.includes"}},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:useInstanceActions","to":"symbol-name:setLoadingSessionId","kind":"calls","metadata":{"unresolvedName":"setLoadingSessionId"}},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:useInstanceActions","to":"symbol-name:onRefresh","kind":"calls","metadata":{"unresolvedName":"onRefresh"}},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:useInstanceActions","to":"symbol-name:setDirtyDeleteSession","kind":"calls","metadata":{"unresolvedName":"setDirtyDeleteSession"}},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:useInstanceActions","to":"symbol-name:setDirtyDeleteFiles","kind":"calls","metadata":{"unresolvedName":"setDirtyDeleteFiles"}},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:useInstanceActions","to":"symbol-name:alert","kind":"calls","metadata":{"unresolvedName":"alert"}},{"from":"file:/workspace/apps/web/src/hooks/use-instance-actions.ts","to":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-instance-actions.ts","to":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","to":"external:window.open","kind":"calls","metadata":{"unresolvedName":"window.open"}},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","to":"external:session.tool_type_interfaces?.includes","kind":"calls","metadata":{"unresolvedName":"session.tool_type_interfaces?.includes"}},{"from":"file:/workspace/apps/web/src/hooks/use-instance-actions.ts","to":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-instance-actions.ts","to":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","to":"symbol-name:setLoadingSessionId","kind":"calls","metadata":{"unresolvedName":"setLoadingSessionId"}},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","to":"symbol-name:onRefresh","kind":"calls","metadata":{"unresolvedName":"onRefresh"}},{"from":"file:/workspace/apps/web/src/hooks/use-instance-actions.ts","to":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-instance-actions.ts","to":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","to":"symbol-name:setLoadingSessionId","kind":"calls","metadata":{"unresolvedName":"setLoadingSessionId"}},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","to":"symbol-name:onRefresh","kind":"calls","metadata":{"unresolvedName":"onRefresh"}},{"from":"file:/workspace/apps/web/src/hooks/use-instance-actions.ts","to":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-instance-actions.ts","to":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","to":"symbol-name:setLoadingSessionId","kind":"calls","metadata":{"unresolvedName":"setLoadingSessionId"}},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","to":"symbol-name:setDirtyDeleteSession","kind":"calls","metadata":{"unresolvedName":"setDirtyDeleteSession"}},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","to":"symbol-name:setDirtyDeleteFiles","kind":"calls","metadata":{"unresolvedName":"setDirtyDeleteFiles"}},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","to":"symbol-name:onRefresh","kind":"calls","metadata":{"unresolvedName":"onRefresh"}},{"from":"file:/workspace/apps/web/src/hooks/use-instance-actions.ts","to":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-instance-actions.ts","to":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","to":"symbol-name:setLoadingSessionId","kind":"calls","metadata":{"unresolvedName":"setLoadingSessionId"}},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","to":"symbol-name:setDirtyDeleteSession","kind":"calls","metadata":{"unresolvedName":"setDirtyDeleteSession"}},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","to":"symbol-name:setDirtyDeleteFiles","kind":"calls","metadata":{"unresolvedName":"setDirtyDeleteFiles"}},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","to":"symbol-name:onRefresh","kind":"calls","metadata":{"unresolvedName":"onRefresh"}},{"from":"file:/workspace/apps/web/src/hooks/use-instance-actions.ts","to":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-instance-actions.ts","to":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","to":"symbol-name:setLoadingSessionId","kind":"calls","metadata":{"unresolvedName":"setLoadingSessionId"}},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","to":"symbol-name:onRefresh","kind":"calls","metadata":{"unresolvedName":"onRefresh"}},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","to":"symbol-name:alert","kind":"calls","metadata":{"unresolvedName":"alert"}},{"from":"file:/workspace/apps/web/src/hooks/use-instance-actions.ts","to":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-instance-actions.ts","to":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","to":"symbol-name:setDirtyDeleteSession","kind":"calls","metadata":{"unresolvedName":"setDirtyDeleteSession"}},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","to":"symbol-name:setDirtyDeleteFiles","kind":"calls","metadata":{"unresolvedName":"setDirtyDeleteFiles"}},{"from":"file:/workspace/apps/web/src/hooks/use-mobile-viewport.ts","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-mobile-viewport.ts","to":"/workspace/apps/web/src/hooks/use-mobile-viewport.ts:useMobileViewport","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-mobile-viewport.ts","to":"/workspace/apps/web/src/hooks/use-mobile-viewport.ts:useMobileViewport","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-mobile-viewport.ts:useMobileViewport","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/hooks/use-mobile-viewport.ts:useMobileViewport","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/hooks/use-mobile-viewport.ts:useMobileViewport","to":"symbol-name:setIsMobile","kind":"calls","metadata":{"unresolvedName":"setIsMobile"}},{"from":"/workspace/apps/web/src/hooks/use-mobile-viewport.ts:useMobileViewport","to":"external:window.addEventListener","kind":"calls","metadata":{"unresolvedName":"window.addEventListener"}},{"from":"/workspace/apps/web/src/hooks/use-mobile-viewport.ts:useMobileViewport","to":"external:window.removeEventListener","kind":"calls","metadata":{"unresolvedName":"window.removeEventListener"}},{"from":"file:/workspace/apps/web/src/hooks/use-mobile-viewport.ts","to":"/workspace/apps/web/src/hooks/use-mobile-viewport.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-mobile-viewport.ts","to":"/workspace/apps/web/src/hooks/use-mobile-viewport.ts:","kind":"defines"},{"from":"file:/workspace/apps/web/src/hooks/use-mobile-viewport.ts","to":"/workspace/apps/web/src/hooks/use-mobile-viewport.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-mobile-viewport.ts","to":"/workspace/apps/web/src/hooks/use-mobile-viewport.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-mobile-viewport.ts:","to":"symbol-name:setIsMobile","kind":"calls","metadata":{"unresolvedName":"setIsMobile"}},{"from":"/workspace/apps/web/src/hooks/use-mobile-viewport.ts:","to":"external:window.addEventListener","kind":"calls","metadata":{"unresolvedName":"window.addEventListener"}},{"from":"/workspace/apps/web/src/hooks/use-mobile-viewport.ts:","to":"external:window.removeEventListener","kind":"calls","metadata":{"unresolvedName":"window.removeEventListener"}},{"from":"file:/workspace/apps/web/src/hooks/use-mobile-viewport.ts","to":"/workspace/apps/web/src/hooks/use-mobile-viewport.ts:handleResize","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-mobile-viewport.ts","to":"/workspace/apps/web/src/hooks/use-mobile-viewport.ts:handleResize","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-mobile-viewport.ts:handleResize","to":"symbol-name:setIsMobile","kind":"calls","metadata":{"unresolvedName":"setIsMobile"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"external:vitest","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"external:@testing-library/react","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"file:/workspace/apps/web/src/hooks/use-notifications.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"file:/workspace/apps/web/src/state/notifications.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"file:/workspace/apps/web/src/api/notifications.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"file:/workspace/apps/web/src/api/notifications.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:wrapper","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:wrapper","kind":"defines"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:beforeEach","kind":"calls","metadata":{"unresolvedName":"beforeEach"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:vi.useFakeTimers","kind":"calls","metadata":{"unresolvedName":"vi.useFakeTimers"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedGetNotifications.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetNotifications.mockResolvedValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedGetUnreadCount.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetUnreadCount.mockResolvedValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedMarkNotificationRead.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedMarkNotificationRead.mockResolvedValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:makeNotification","kind":"calls","metadata":{"unresolvedName":"makeNotification"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedMarkAllNotificationsRead.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedMarkAllNotificationsRead.mockResolvedValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedDismissNotification.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedDismissNotification.mockResolvedValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:afterEach","kind":"calls","metadata":{"unresolvedName":"afterEach"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:vi.useRealTimers","kind":"calls","metadata":{"unresolvedName":"vi.useRealTimers"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:vi.clearAllMocks","kind":"calls","metadata":{"unresolvedName":"vi.clearAllMocks"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"/workspace/apps/web/src/hooks/use-notifications.ts:useNotifications","kind":"calls","metadata":{"unresolvedName":"useNotifications"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:expect(result.current.notifications).toHaveLength","kind":"calls","metadata":{"unresolvedName":"expect(result.current.notifications).toHaveLength"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:expect(result.current.unreadCount).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.current.unreadCount).toBe"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedMarkNotificationRead.mockReturnValue","kind":"calls","metadata":{"unresolvedName":"mockedMarkNotificationRead.mockReturnValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:result.current.markRead","kind":"calls","metadata":{"unresolvedName":"result.current.markRead"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:result.current.notifications.find","kind":"calls","metadata":{"unresolvedName":"result.current.notifications.find"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:expect(n?.read_at).not.toBeNull","kind":"calls","metadata":{"unresolvedName":"expect(n?.read_at).not.toBeNull"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:resolveApi","kind":"calls","metadata":{"unresolvedName":"resolveApi"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedMarkNotificationRead.mockRejectedValue","kind":"calls","metadata":{"unresolvedName":"mockedMarkNotificationRead.mockRejectedValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:expect(n?.read_at).toBeNull","kind":"calls","metadata":{"unresolvedName":"expect(n?.read_at).toBeNull"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:expect(result.current.error).toBeInstanceOf","kind":"calls","metadata":{"unresolvedName":"expect(result.current.error).toBeInstanceOf"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedDismissNotification.mockReturnValue","kind":"calls","metadata":{"unresolvedName":"mockedDismissNotification.mockReturnValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:result.current.dismiss","kind":"calls","metadata":{"unresolvedName":"result.current.dismiss"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedDismissNotification.mockRejectedValue","kind":"calls","metadata":{"unresolvedName":"mockedDismissNotification.mockRejectedValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:result.current.refreshList","kind":"calls","metadata":{"unresolvedName":"result.current.refreshList"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:expect(mockedGetNotifications).toHaveBeenCalledTimes","kind":"calls","metadata":{"unresolvedName":"expect(mockedGetNotifications).toHaveBeenCalledTimes"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedGetUnreadCount.mockRejectedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetUnreadCount.mockRejectedValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:expect(mockedGetUnreadCount.mock.calls.length).toBe","kind":"calls","metadata":{"unresolvedName":"expect(mockedGetUnreadCount.mock.calls.length).toBe"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:Object.defineProperty","kind":"calls","metadata":{"unresolvedName":"Object.defineProperty"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:document.dispatchEvent","kind":"calls","metadata":{"unresolvedName":"document.dispatchEvent"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:expect(mockedGetUnreadCount.mock.calls.length).toBeGreaterThan","kind":"calls","metadata":{"unresolvedName":"expect(mockedGetUnreadCount.mock.calls.length).toBeGreaterThan"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:vi.useFakeTimers","kind":"calls","metadata":{"unresolvedName":"vi.useFakeTimers"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedGetNotifications.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetNotifications.mockResolvedValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedGetUnreadCount.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetUnreadCount.mockResolvedValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedMarkNotificationRead.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedMarkNotificationRead.mockResolvedValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:makeNotification","kind":"calls","metadata":{"unresolvedName":"makeNotification"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedMarkAllNotificationsRead.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedMarkAllNotificationsRead.mockResolvedValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedDismissNotification.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedDismissNotification.mockResolvedValue"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:vi.useRealTimers","kind":"calls","metadata":{"unresolvedName":"vi.useRealTimers"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:vi.clearAllMocks","kind":"calls","metadata":{"unresolvedName":"vi.clearAllMocks"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedGetNotifications.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetNotifications.mockResolvedValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:makeNotification","kind":"calls","metadata":{"unresolvedName":"makeNotification"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedGetUnreadCount.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetUnreadCount.mockResolvedValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"/workspace/apps/web/src/hooks/use-notifications.ts:useNotifications","kind":"calls","metadata":{"unresolvedName":"useNotifications"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:expect(result.current.notifications).toHaveLength","kind":"calls","metadata":{"unresolvedName":"expect(result.current.notifications).toHaveLength"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:expect(result.current.unreadCount).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.current.unreadCount).toBe"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:expect(result.current.notifications).toHaveLength","kind":"calls","metadata":{"unresolvedName":"expect(result.current.notifications).toHaveLength"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:expect(result.current.unreadCount).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.current.unreadCount).toBe"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedGetNotifications.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetNotifications.mockResolvedValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:makeNotification","kind":"calls","metadata":{"unresolvedName":"makeNotification"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedGetUnreadCount.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetUnreadCount.mockResolvedValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"/workspace/apps/web/src/hooks/use-notifications.ts:useNotifications","kind":"calls","metadata":{"unresolvedName":"useNotifications"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:expect(result.current.unreadCount).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.current.unreadCount).toBe"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedMarkNotificationRead.mockReturnValue","kind":"calls","metadata":{"unresolvedName":"mockedMarkNotificationRead.mockReturnValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:result.current.markRead","kind":"calls","metadata":{"unresolvedName":"result.current.markRead"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:result.current.notifications.find","kind":"calls","metadata":{"unresolvedName":"result.current.notifications.find"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:expect(n?.read_at).not.toBeNull","kind":"calls","metadata":{"unresolvedName":"expect(n?.read_at).not.toBeNull"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:resolveApi","kind":"calls","metadata":{"unresolvedName":"resolveApi"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:result.current.markRead","kind":"calls","metadata":{"unresolvedName":"result.current.markRead"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:result.current.notifications.find","kind":"calls","metadata":{"unresolvedName":"result.current.notifications.find"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:expect(n?.read_at).not.toBeNull","kind":"calls","metadata":{"unresolvedName":"expect(n?.read_at).not.toBeNull"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:resolveApi","kind":"calls","metadata":{"unresolvedName":"resolveApi"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:makeNotification","kind":"calls","metadata":{"unresolvedName":"makeNotification"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedGetNotifications.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetNotifications.mockResolvedValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:makeNotification","kind":"calls","metadata":{"unresolvedName":"makeNotification"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedGetUnreadCount.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetUnreadCount.mockResolvedValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"/workspace/apps/web/src/hooks/use-notifications.ts:useNotifications","kind":"calls","metadata":{"unresolvedName":"useNotifications"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:expect(result.current.unreadCount).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.current.unreadCount).toBe"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedMarkNotificationRead.mockRejectedValue","kind":"calls","metadata":{"unresolvedName":"mockedMarkNotificationRead.mockRejectedValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:result.current.markRead","kind":"calls","metadata":{"unresolvedName":"result.current.markRead"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:result.current.notifications.find","kind":"calls","metadata":{"unresolvedName":"result.current.notifications.find"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:expect(n?.read_at).toBeNull","kind":"calls","metadata":{"unresolvedName":"expect(n?.read_at).toBeNull"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:expect(result.current.error).toBeInstanceOf","kind":"calls","metadata":{"unresolvedName":"expect(result.current.error).toBeInstanceOf"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:result.current.markRead","kind":"calls","metadata":{"unresolvedName":"result.current.markRead"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedGetNotifications.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetNotifications.mockResolvedValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:makeNotification","kind":"calls","metadata":{"unresolvedName":"makeNotification"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedGetUnreadCount.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetUnreadCount.mockResolvedValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"/workspace/apps/web/src/hooks/use-notifications.ts:useNotifications","kind":"calls","metadata":{"unresolvedName":"useNotifications"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:expect(result.current.notifications).toHaveLength","kind":"calls","metadata":{"unresolvedName":"expect(result.current.notifications).toHaveLength"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedDismissNotification.mockReturnValue","kind":"calls","metadata":{"unresolvedName":"mockedDismissNotification.mockReturnValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:result.current.dismiss","kind":"calls","metadata":{"unresolvedName":"result.current.dismiss"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:expect(result.current.unreadCount).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.current.unreadCount).toBe"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:result.current.dismiss","kind":"calls","metadata":{"unresolvedName":"result.current.dismiss"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:expect(result.current.notifications).toHaveLength","kind":"calls","metadata":{"unresolvedName":"expect(result.current.notifications).toHaveLength"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedGetNotifications.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetNotifications.mockResolvedValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:makeNotification","kind":"calls","metadata":{"unresolvedName":"makeNotification"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedGetUnreadCount.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetUnreadCount.mockResolvedValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"/workspace/apps/web/src/hooks/use-notifications.ts:useNotifications","kind":"calls","metadata":{"unresolvedName":"useNotifications"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:expect(result.current.notifications).toHaveLength","kind":"calls","metadata":{"unresolvedName":"expect(result.current.notifications).toHaveLength"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedDismissNotification.mockRejectedValue","kind":"calls","metadata":{"unresolvedName":"mockedDismissNotification.mockRejectedValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:result.current.dismiss","kind":"calls","metadata":{"unresolvedName":"result.current.dismiss"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:expect(result.current.unreadCount).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.current.unreadCount).toBe"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:expect(result.current.error).toBeInstanceOf","kind":"calls","metadata":{"unresolvedName":"expect(result.current.error).toBeInstanceOf"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:result.current.dismiss","kind":"calls","metadata":{"unresolvedName":"result.current.dismiss"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedGetNotifications.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetNotifications.mockResolvedValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:makeNotification","kind":"calls","metadata":{"unresolvedName":"makeNotification"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"/workspace/apps/web/src/hooks/use-notifications.ts:useNotifications","kind":"calls","metadata":{"unresolvedName":"useNotifications"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:result.current.refreshList","kind":"calls","metadata":{"unresolvedName":"result.current.refreshList"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:expect(mockedGetNotifications).toHaveBeenCalledTimes","kind":"calls","metadata":{"unresolvedName":"expect(mockedGetNotifications).toHaveBeenCalledTimes"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:expect(result.current.notifications).toHaveLength","kind":"calls","metadata":{"unresolvedName":"expect(result.current.notifications).toHaveLength"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:result.current.refreshList","kind":"calls","metadata":{"unresolvedName":"result.current.refreshList"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedGetUnreadCount.mockRejectedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetUnreadCount.mockRejectedValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"/workspace/apps/web/src/hooks/use-notifications.ts:useNotifications","kind":"calls","metadata":{"unresolvedName":"useNotifications"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:expect(mockedGetUnreadCount.mock.calls.length).toBe","kind":"calls","metadata":{"unresolvedName":"expect(mockedGetUnreadCount.mock.calls.length).toBe"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"/workspace/apps/web/src/hooks/use-notifications.ts:useNotifications","kind":"calls","metadata":{"unresolvedName":"useNotifications"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:Object.defineProperty","kind":"calls","metadata":{"unresolvedName":"Object.defineProperty"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:document.dispatchEvent","kind":"calls","metadata":{"unresolvedName":"document.dispatchEvent"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:expect(mockedGetUnreadCount.mock.calls.length).toBe","kind":"calls","metadata":{"unresolvedName":"expect(mockedGetUnreadCount.mock.calls.length).toBe"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:expect(mockedGetUnreadCount.mock.calls.length).toBeGreaterThan","kind":"calls","metadata":{"unresolvedName":"expect(mockedGetUnreadCount.mock.calls.length).toBeGreaterThan"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:Object.defineProperty","kind":"calls","metadata":{"unresolvedName":"Object.defineProperty"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:document.dispatchEvent","kind":"calls","metadata":{"unresolvedName":"document.dispatchEvent"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:Object.defineProperty","kind":"calls","metadata":{"unresolvedName":"Object.defineProperty"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:document.dispatchEvent","kind":"calls","metadata":{"unresolvedName":"document.dispatchEvent"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedGetNotifications.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetNotifications.mockResolvedValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:makeNotification","kind":"calls","metadata":{"unresolvedName":"makeNotification"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedGetUnreadCount.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedGetUnreadCount.mockResolvedValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"/workspace/apps/web/src/hooks/use-notifications.ts:useNotifications","kind":"calls","metadata":{"unresolvedName":"useNotifications"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:expect(result.current.unreadCount).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.current.unreadCount).toBe"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:mockedMarkNotificationRead.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockedMarkNotificationRead.mockResolvedValue"}},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:result.current.markRead","kind":"calls","metadata":{"unresolvedName":"result.current.markRead"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.test.tsx","to":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.test.tsx:","to":"external:result.current.markRead","kind":"calls","metadata":{"unresolvedName":"result.current.markRead"}},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.ts","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.ts","to":"file:/workspace/apps/web/src/state/notifications.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.ts","to":"/workspace/apps/web/src/hooks/use-notifications.ts:useNotifications","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-notifications.ts","to":"/workspace/apps/web/src/hooks/use-notifications.ts:useNotifications","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-notifications.ts:useNotifications","to":"symbol-name:useContext","kind":"calls","metadata":{"unresolvedName":"useContext"}},{"from":"file:/workspace/apps/web/src/hooks/use-special-keys.ts","to":"/workspace/apps/web/src/hooks/use-special-keys.ts:getSequenceWithModifier","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-special-keys.ts","to":"/workspace/apps/web/src/hooks/use-special-keys.ts:getSequenceWithModifier","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-special-keys.ts:getSequenceWithModifier","to":"external:char.toLowerCase","kind":"calls","metadata":{"unresolvedName":"char.toLowerCase"}},{"from":"file:/workspace/apps/web/src/hooks/use-special-keys.ts","to":"/workspace/apps/web/src/hooks/use-special-keys.ts:applyModifierToChar","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-special-keys.ts","to":"/workspace/apps/web/src/hooks/use-special-keys.ts:applyModifierToChar","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-special-keys.ts:applyModifierToChar","to":"external:char.toLowerCase","kind":"calls","metadata":{"unresolvedName":"char.toLowerCase"}},{"from":"file:/workspace/apps/web/src/hooks/use-start-tool.ts","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-start-tool.ts","to":"file:/workspace/apps/web/src/api/sessions.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-start-tool.ts","to":"file:/workspace/apps/web/src/types/workspace.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-start-tool.ts","to":"file:/workspace/apps/web/src/api/sessions.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-start-tool.ts","to":"/workspace/apps/web/src/hooks/use-start-tool.ts:useStartTool","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-start-tool.ts","to":"/workspace/apps/web/src/hooks/use-start-tool.ts:useStartTool","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-start-tool.ts:useStartTool","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/hooks/use-start-tool.ts:useStartTool","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/hooks/use-start-tool.ts:useStartTool","to":"symbol-name:setStarting","kind":"calls","metadata":{"unresolvedName":"setStarting"}},{"from":"/workspace/apps/web/src/hooks/use-start-tool.ts:useStartTool","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"file:/workspace/apps/web/src/hooks/use-start-tool.ts","to":"/workspace/apps/web/src/hooks/use-start-tool.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-start-tool.ts","to":"/workspace/apps/web/src/hooks/use-start-tool.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-start-tool.ts:","to":"symbol-name:setStarting","kind":"calls","metadata":{"unresolvedName":"setStarting"}},{"from":"/workspace/apps/web/src/hooks/use-start-tool.ts:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"external:vitest","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"external:@testing-library/react","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:send","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:send","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:send","to":"external:this.sent.push","kind":"calls","metadata":{"unresolvedName":"this.sent.push"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:close","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:close","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:close","to":"external:this.onclose","kind":"calls","metadata":{"unresolvedName":"this.onclose"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:simulateOpen","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:simulateOpen","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:simulateOpen","to":"external:this.onopen","kind":"calls","metadata":{"unresolvedName":"this.onopen"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:simulateMessage","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:simulateMessage","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:simulateMessage","to":"external:this.onmessage","kind":"calls","metadata":{"unresolvedName":"this.onmessage"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:simulateError","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:simulateError","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:simulateError","to":"external:this.onerror","kind":"calls","metadata":{"unresolvedName":"this.onerror"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:beforeEach","kind":"calls","metadata":{"unresolvedName":"beforeEach"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:vi.useFakeTimers","kind":"calls","metadata":{"unresolvedName":"vi.useFakeTimers"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:vi.stubGlobal","kind":"calls","metadata":{"unresolvedName":"vi.stubGlobal"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:afterEach","kind":"calls","metadata":{"unresolvedName":"afterEach"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:vi.useRealTimers","kind":"calls","metadata":{"unresolvedName":"vi.useRealTimers"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:vi.unstubAllGlobals","kind":"calls","metadata":{"unresolvedName":"vi.unstubAllGlobals"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","kind":"calls","metadata":{"unresolvedName":"useTerminalConnection"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(result.current.state.status).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.current.state.status).toBe"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(MockWebSocket.instances).toHaveLength","kind":"calls","metadata":{"unresolvedName":"expect(MockWebSocket.instances).toHaveLength"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateOpen","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateOpen"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].sent.filter","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].sent.filter"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:s.includes","kind":"calls","metadata":{"unresolvedName":"s.includes"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(pings.length).toBeGreaterThanOrEqual","kind":"calls","metadata":{"unresolvedName":"expect(pings.length).toBeGreaterThanOrEqual"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateMessage","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateMessage"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:JSON.stringify","kind":"calls","metadata":{"unresolvedName":"JSON.stringify"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(result.current.state.latency).not.toBeNull","kind":"calls","metadata":{"unresolvedName":"expect(result.current.state.latency).not.toBeNull"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(result.current.state.latency).toBeGreaterThanOrEqual","kind":"calls","metadata":{"unresolvedName":"expect(result.current.state.latency).toBeGreaterThanOrEqual"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].close","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].close"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(result.current.state.attempt).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.current.state.attempt).toBe"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:ws.close","kind":"calls","metadata":{"unresolvedName":"ws.close"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:Math.min","kind":"calls","metadata":{"unresolvedName":"Math.min"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(result.current.state.error).toContain","kind":"calls","metadata":{"unresolvedName":"expect(result.current.state.error).toContain"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:result.current.sendResize","kind":"calls","metadata":{"unresolvedName":"result.current.sendResize"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(resizes.length).toBeGreaterThanOrEqual","kind":"calls","metadata":{"unresolvedName":"expect(resizes.length).toBeGreaterThanOrEqual"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(resizes.length).toBe","kind":"calls","metadata":{"unresolvedName":"expect(resizes.length).toBe"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:result.current.sendInput","kind":"calls","metadata":{"unresolvedName":"result.current.sendInput"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(MockWebSocket.instances[0].sent).toContain","kind":"calls","metadata":{"unresolvedName":"expect(MockWebSocket.instances[0].sent).toContain"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:result.current.reconnect","kind":"calls","metadata":{"unresolvedName":"result.current.reconnect"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(onData).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(onData).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect.any","kind":"calls","metadata":{"unresolvedName":"expect.any"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(onControl).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(onControl).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect.objectContaining","kind":"calls","metadata":{"unresolvedName":"expect.objectContaining"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(serializeFn).toHaveBeenCalled","kind":"calls","metadata":{"unresolvedName":"expect(serializeFn).toHaveBeenCalled"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(onRestoreScrollback).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(onRestoreScrollback).toHaveBeenCalledWith"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:vi.useFakeTimers","kind":"calls","metadata":{"unresolvedName":"vi.useFakeTimers"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:vi.stubGlobal","kind":"calls","metadata":{"unresolvedName":"vi.stubGlobal"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:vi.useRealTimers","kind":"calls","metadata":{"unresolvedName":"vi.useRealTimers"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:vi.unstubAllGlobals","kind":"calls","metadata":{"unresolvedName":"vi.unstubAllGlobals"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","kind":"calls","metadata":{"unresolvedName":"useTerminalConnection"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(result.current.state.status).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.current.state.status).toBe"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(MockWebSocket.instances).toHaveLength","kind":"calls","metadata":{"unresolvedName":"expect(MockWebSocket.instances).toHaveLength"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","kind":"calls","metadata":{"unresolvedName":"useTerminalConnection"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateOpen","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateOpen"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(result.current.state.status).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.current.state.status).toBe"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateOpen","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateOpen"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","kind":"calls","metadata":{"unresolvedName":"useTerminalConnection"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateOpen","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateOpen"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].sent.filter","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].sent.filter"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:s.includes","kind":"calls","metadata":{"unresolvedName":"s.includes"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(pings.length).toBeGreaterThanOrEqual","kind":"calls","metadata":{"unresolvedName":"expect(pings.length).toBeGreaterThanOrEqual"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateOpen","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateOpen"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","kind":"calls","metadata":{"unresolvedName":"useTerminalConnection"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateOpen","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateOpen"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateMessage","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateMessage"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:JSON.stringify","kind":"calls","metadata":{"unresolvedName":"JSON.stringify"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(result.current.state.latency).not.toBeNull","kind":"calls","metadata":{"unresolvedName":"expect(result.current.state.latency).not.toBeNull"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(result.current.state.latency).toBeGreaterThanOrEqual","kind":"calls","metadata":{"unresolvedName":"expect(result.current.state.latency).toBeGreaterThanOrEqual"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateOpen","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateOpen"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateMessage","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateMessage"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:JSON.stringify","kind":"calls","metadata":{"unresolvedName":"JSON.stringify"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","kind":"calls","metadata":{"unresolvedName":"useTerminalConnection"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateOpen","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateOpen"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].close","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].close"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(result.current.state.status).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.current.state.status).toBe"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(result.current.state.attempt).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.current.state.attempt).toBe"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(MockWebSocket.instances).toHaveLength","kind":"calls","metadata":{"unresolvedName":"expect(MockWebSocket.instances).toHaveLength"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateOpen","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateOpen"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].close","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].close"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","kind":"calls","metadata":{"unresolvedName":"useTerminalConnection"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateOpen","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateOpen"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:ws.close","kind":"calls","metadata":{"unresolvedName":"ws.close"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:Math.min","kind":"calls","metadata":{"unresolvedName":"Math.min"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(result.current.state.status).toBe","kind":"calls","metadata":{"unresolvedName":"expect(result.current.state.status).toBe"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(result.current.state.error).toContain","kind":"calls","metadata":{"unresolvedName":"expect(result.current.state.error).toContain"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateOpen","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateOpen"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:ws.close","kind":"calls","metadata":{"unresolvedName":"ws.close"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","kind":"calls","metadata":{"unresolvedName":"useTerminalConnection"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateOpen","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateOpen"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:result.current.sendResize","kind":"calls","metadata":{"unresolvedName":"result.current.sendResize"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].sent.filter","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].sent.filter"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:s.includes","kind":"calls","metadata":{"unresolvedName":"s.includes"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(resizes.length).toBeGreaterThanOrEqual","kind":"calls","metadata":{"unresolvedName":"expect(resizes.length).toBeGreaterThanOrEqual"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateOpen","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateOpen"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:result.current.sendResize","kind":"calls","metadata":{"unresolvedName":"result.current.sendResize"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","kind":"calls","metadata":{"unresolvedName":"useTerminalConnection"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateOpen","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateOpen"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:result.current.sendResize","kind":"calls","metadata":{"unresolvedName":"result.current.sendResize"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].sent.filter","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].sent.filter"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:s.includes","kind":"calls","metadata":{"unresolvedName":"s.includes"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(resizes.length).toBe","kind":"calls","metadata":{"unresolvedName":"expect(resizes.length).toBe"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateOpen","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateOpen"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:result.current.sendResize","kind":"calls","metadata":{"unresolvedName":"result.current.sendResize"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:result.current.sendResize","kind":"calls","metadata":{"unresolvedName":"result.current.sendResize"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","kind":"calls","metadata":{"unresolvedName":"useTerminalConnection"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateOpen","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateOpen"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:result.current.sendInput","kind":"calls","metadata":{"unresolvedName":"result.current.sendInput"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(MockWebSocket.instances[0].sent).toContain","kind":"calls","metadata":{"unresolvedName":"expect(MockWebSocket.instances[0].sent).toContain"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateOpen","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateOpen"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:result.current.sendInput","kind":"calls","metadata":{"unresolvedName":"result.current.sendInput"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","kind":"calls","metadata":{"unresolvedName":"useTerminalConnection"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateOpen","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateOpen"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:result.current.reconnect","kind":"calls","metadata":{"unresolvedName":"result.current.reconnect"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(MockWebSocket.instances).toHaveLength","kind":"calls","metadata":{"unresolvedName":"expect(MockWebSocket.instances).toHaveLength"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateOpen","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateOpen"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:result.current.reconnect","kind":"calls","metadata":{"unresolvedName":"result.current.reconnect"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","kind":"calls","metadata":{"unresolvedName":"useTerminalConnection"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateOpen","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateOpen"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateMessage","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateMessage"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(onData).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(onData).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect.any","kind":"calls","metadata":{"unresolvedName":"expect.any"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateOpen","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateOpen"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateMessage","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateMessage"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","kind":"calls","metadata":{"unresolvedName":"useTerminalConnection"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateOpen","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateOpen"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateMessage","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateMessage"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:JSON.stringify","kind":"calls","metadata":{"unresolvedName":"JSON.stringify"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(onControl).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(onControl).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect.objectContaining","kind":"calls","metadata":{"unresolvedName":"expect.objectContaining"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateOpen","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateOpen"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateMessage","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateMessage"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:JSON.stringify","kind":"calls","metadata":{"unresolvedName":"JSON.stringify"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:vi.fn","kind":"calls","metadata":{"unresolvedName":"vi.fn"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:renderHook","kind":"calls","metadata":{"unresolvedName":"renderHook"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","kind":"calls","metadata":{"unresolvedName":"useTerminalConnection"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:act","kind":"calls","metadata":{"unresolvedName":"act"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateOpen","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateOpen"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].close","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].close"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(serializeFn).toHaveBeenCalled","kind":"calls","metadata":{"unresolvedName":"expect(serializeFn).toHaveBeenCalled"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:expect(onRestoreScrollback).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(onRestoreScrollback).toHaveBeenCalledWith"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].simulateOpen","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].simulateOpen"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:MockWebSocket.instances[0].close","kind":"calls","metadata":{"unresolvedName":"MockWebSocket.instances[0].close"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","to":"external:vi.advanceTimersByTime","kind":"calls","metadata":{"unresolvedName":"vi.advanceTimersByTime"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.test.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.test.ts:","kind":"defines"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"file:/workspace/apps/web/src/types/terminal.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"file:/workspace/apps/web/src/utils/terminal-protocol.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"symbol-name:useRef","kind":"calls","metadata":{"unresolvedName":"useRef"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"symbol-name:setState","kind":"calls","metadata":{"unresolvedName":"setState"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"symbol-name:updater","kind":"calls","metadata":{"unresolvedName":"updater"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"symbol-name:clearTimeout","kind":"calls","metadata":{"unresolvedName":"clearTimeout"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"symbol-name:onLocalEcho","kind":"calls","metadata":{"unresolvedName":"onLocalEcho"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"external:pendingEchoRef.current.slice","kind":"calls","metadata":{"unresolvedName":"pendingEchoRef.current.slice"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"external:data.slice","kind":"calls","metadata":{"unresolvedName":"data.slice"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"external:new TextDecoder().decode","kind":"calls","metadata":{"unresolvedName":"new TextDecoder().decode"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"symbol-name:onData","kind":"calls","metadata":{"unresolvedName":"onData"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"symbol-name:deduplicateServerData","kind":"calls","metadata":{"unresolvedName":"deduplicateServerData"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"symbol-name:flushPendingEcho","kind":"calls","metadata":{"unresolvedName":"flushPendingEcho"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"symbol-name:onControl","kind":"calls","metadata":{"unresolvedName":"onControl"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"external:Date.now","kind":"calls","metadata":{"unresolvedName":"Date.now"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"symbol-name:setStableState","kind":"calls","metadata":{"unresolvedName":"setStableState"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"symbol-name:clearTimers","kind":"calls","metadata":{"unresolvedName":"clearTimers"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"external:wsRef.current?.close","kind":"calls","metadata":{"unresolvedName":"wsRef.current?.close"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"symbol-name:setTimeout","kind":"calls","metadata":{"unresolvedName":"setTimeout"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"external:ws.send","kind":"calls","metadata":{"unresolvedName":"ws.send"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"/workspace/apps/web/src/utils/terminal-protocol.ts:encodeControlMessage","kind":"calls","metadata":{"unresolvedName":"encodeControlMessage"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"external:ws.close","kind":"calls","metadata":{"unresolvedName":"ws.close"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"symbol-name:serializeFn","kind":"calls","metadata":{"unresolvedName":"serializeFn"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"external:sessionStorage.setItem","kind":"calls","metadata":{"unresolvedName":"sessionStorage.setItem"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"external:sessionStorage.getItem","kind":"calls","metadata":{"unresolvedName":"sessionStorage.getItem"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"symbol-name:onRestoreScrollback","kind":"calls","metadata":{"unresolvedName":"onRestoreScrollback"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"external:sessionStorage.removeItem","kind":"calls","metadata":{"unresolvedName":"sessionStorage.removeItem"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"external:apiUrl.replace(/^https?:\\/\\//, \"\").replace","kind":"calls","metadata":{"unresolvedName":"apiUrl.replace(/^https?:\\/\\//, \"\").replace"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"external:apiUrl.replace","kind":"calls","metadata":{"unresolvedName":"apiUrl.replace"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"symbol-name:restoreScrollback","kind":"calls","metadata":{"unresolvedName":"restoreScrollback"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"symbol-name:schedulePing","kind":"calls","metadata":{"unresolvedName":"schedulePing"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"/workspace/apps/web/src/utils/terminal-protocol.ts:isControlMessage","kind":"calls","metadata":{"unresolvedName":"isControlMessage"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"/workspace/apps/web/src/utils/terminal-protocol.ts:decodeControlMessage","kind":"calls","metadata":{"unresolvedName":"decodeControlMessage"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"symbol-name:handleControlMessage","kind":"calls","metadata":{"unresolvedName":"handleControlMessage"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"symbol-name:handleBinaryMessage","kind":"calls","metadata":{"unresolvedName":"handleBinaryMessage"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"external:event.data.arrayBuffer().then","kind":"calls","metadata":{"unresolvedName":"event.data.arrayBuffer().then"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"external:event.data.arrayBuffer","kind":"calls","metadata":{"unresolvedName":"event.data.arrayBuffer"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"symbol-name:serializeScrollback","kind":"calls","metadata":{"unresolvedName":"serializeScrollback"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"external:Math.min","kind":"calls","metadata":{"unresolvedName":"Math.min"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"external:Math.round","kind":"calls","metadata":{"unresolvedName":"Math.round"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"symbol-name:connect","kind":"calls","metadata":{"unresolvedName":"connect"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"external:data.charCodeAt","kind":"calls","metadata":{"unresolvedName":"data.charCodeAt"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"external:wsRef.current.close","kind":"calls","metadata":{"unresolvedName":"wsRef.current.close"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"symbol-name:reconnect","kind":"calls","metadata":{"unresolvedName":"reconnect"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"external:window.addEventListener","kind":"calls","metadata":{"unresolvedName":"window.addEventListener"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","to":"external:window.removeEventListener","kind":"calls","metadata":{"unresolvedName":"window.removeEventListener"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:setState","kind":"calls","metadata":{"unresolvedName":"setState"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:updater","kind":"calls","metadata":{"unresolvedName":"updater"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:updater","kind":"calls","metadata":{"unresolvedName":"updater"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:clearTimeout","kind":"calls","metadata":{"unresolvedName":"clearTimeout"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:onLocalEcho","kind":"calls","metadata":{"unresolvedName":"onLocalEcho"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:pendingEchoRef.current.slice","kind":"calls","metadata":{"unresolvedName":"pendingEchoRef.current.slice"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:data.slice","kind":"calls","metadata":{"unresolvedName":"data.slice"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:new TextDecoder().decode","kind":"calls","metadata":{"unresolvedName":"new TextDecoder().decode"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:onData","kind":"calls","metadata":{"unresolvedName":"onData"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:deduplicateServerData","kind":"calls","metadata":{"unresolvedName":"deduplicateServerData"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:onLocalEcho","kind":"calls","metadata":{"unresolvedName":"onLocalEcho"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:flushPendingEcho","kind":"calls","metadata":{"unresolvedName":"flushPendingEcho"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:onControl","kind":"calls","metadata":{"unresolvedName":"onControl"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:Date.now","kind":"calls","metadata":{"unresolvedName":"Date.now"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:setStableState","kind":"calls","metadata":{"unresolvedName":"setStableState"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:flushPendingEcho","kind":"calls","metadata":{"unresolvedName":"flushPendingEcho"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:clearTimers","kind":"calls","metadata":{"unresolvedName":"clearTimers"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:wsRef.current?.close","kind":"calls","metadata":{"unresolvedName":"wsRef.current?.close"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:setTimeout","kind":"calls","metadata":{"unresolvedName":"setTimeout"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:Date.now","kind":"calls","metadata":{"unresolvedName":"Date.now"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:ws.send","kind":"calls","metadata":{"unresolvedName":"ws.send"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"/workspace/apps/web/src/utils/terminal-protocol.ts:encodeControlMessage","kind":"calls","metadata":{"unresolvedName":"encodeControlMessage"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:ws.close","kind":"calls","metadata":{"unresolvedName":"ws.close"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:Date.now","kind":"calls","metadata":{"unresolvedName":"Date.now"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:ws.send","kind":"calls","metadata":{"unresolvedName":"ws.send"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"/workspace/apps/web/src/utils/terminal-protocol.ts:encodeControlMessage","kind":"calls","metadata":{"unresolvedName":"encodeControlMessage"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:setTimeout","kind":"calls","metadata":{"unresolvedName":"setTimeout"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:ws.close","kind":"calls","metadata":{"unresolvedName":"ws.close"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:ws.close","kind":"calls","metadata":{"unresolvedName":"ws.close"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:serializeFn","kind":"calls","metadata":{"unresolvedName":"serializeFn"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:sessionStorage.setItem","kind":"calls","metadata":{"unresolvedName":"sessionStorage.setItem"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:sessionStorage.getItem","kind":"calls","metadata":{"unresolvedName":"sessionStorage.getItem"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:onRestoreScrollback","kind":"calls","metadata":{"unresolvedName":"onRestoreScrollback"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:sessionStorage.removeItem","kind":"calls","metadata":{"unresolvedName":"sessionStorage.removeItem"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:apiUrl.replace(/^https?:\\/\\//, \"\").replace","kind":"calls","metadata":{"unresolvedName":"apiUrl.replace(/^https?:\\/\\//, \"\").replace"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:apiUrl.replace","kind":"calls","metadata":{"unresolvedName":"apiUrl.replace"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:setStableState","kind":"calls","metadata":{"unresolvedName":"setStableState"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:restoreScrollback","kind":"calls","metadata":{"unresolvedName":"restoreScrollback"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:schedulePing","kind":"calls","metadata":{"unresolvedName":"schedulePing"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"/workspace/apps/web/src/utils/terminal-protocol.ts:isControlMessage","kind":"calls","metadata":{"unresolvedName":"isControlMessage"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"/workspace/apps/web/src/utils/terminal-protocol.ts:decodeControlMessage","kind":"calls","metadata":{"unresolvedName":"decodeControlMessage"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:handleControlMessage","kind":"calls","metadata":{"unresolvedName":"handleControlMessage"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:handleBinaryMessage","kind":"calls","metadata":{"unresolvedName":"handleBinaryMessage"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:event.data.arrayBuffer().then","kind":"calls","metadata":{"unresolvedName":"event.data.arrayBuffer().then"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:event.data.arrayBuffer","kind":"calls","metadata":{"unresolvedName":"event.data.arrayBuffer"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:clearTimers","kind":"calls","metadata":{"unresolvedName":"clearTimers"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:serializeScrollback","kind":"calls","metadata":{"unresolvedName":"serializeScrollback"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:Math.min","kind":"calls","metadata":{"unresolvedName":"Math.min"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:Math.round","kind":"calls","metadata":{"unresolvedName":"Math.round"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:setTimeout","kind":"calls","metadata":{"unresolvedName":"setTimeout"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:connect","kind":"calls","metadata":{"unresolvedName":"connect"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:setStableState","kind":"calls","metadata":{"unresolvedName":"setStableState"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:restoreScrollback","kind":"calls","metadata":{"unresolvedName":"restoreScrollback"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:schedulePing","kind":"calls","metadata":{"unresolvedName":"schedulePing"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"/workspace/apps/web/src/utils/terminal-protocol.ts:isControlMessage","kind":"calls","metadata":{"unresolvedName":"isControlMessage"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"/workspace/apps/web/src/utils/terminal-protocol.ts:decodeControlMessage","kind":"calls","metadata":{"unresolvedName":"decodeControlMessage"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:handleControlMessage","kind":"calls","metadata":{"unresolvedName":"handleControlMessage"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:handleBinaryMessage","kind":"calls","metadata":{"unresolvedName":"handleBinaryMessage"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:event.data.arrayBuffer().then","kind":"calls","metadata":{"unresolvedName":"event.data.arrayBuffer().then"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:event.data.arrayBuffer","kind":"calls","metadata":{"unresolvedName":"event.data.arrayBuffer"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:handleBinaryMessage","kind":"calls","metadata":{"unresolvedName":"handleBinaryMessage"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:clearTimers","kind":"calls","metadata":{"unresolvedName":"clearTimers"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:setStableState","kind":"calls","metadata":{"unresolvedName":"setStableState"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:serializeScrollback","kind":"calls","metadata":{"unresolvedName":"serializeScrollback"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:Math.min","kind":"calls","metadata":{"unresolvedName":"Math.min"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:Math.round","kind":"calls","metadata":{"unresolvedName":"Math.round"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:setTimeout","kind":"calls","metadata":{"unresolvedName":"setTimeout"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:connect","kind":"calls","metadata":{"unresolvedName":"connect"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:connect","kind":"calls","metadata":{"unresolvedName":"connect"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"defines"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:data.charCodeAt","kind":"calls","metadata":{"unresolvedName":"data.charCodeAt"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:onLocalEcho","kind":"calls","metadata":{"unresolvedName":"onLocalEcho"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:ws.send","kind":"calls","metadata":{"unresolvedName":"ws.send"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:clearTimeout","kind":"calls","metadata":{"unresolvedName":"clearTimeout"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:setTimeout","kind":"calls","metadata":{"unresolvedName":"setTimeout"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:Date.now","kind":"calls","metadata":{"unresolvedName":"Date.now"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:ws.send","kind":"calls","metadata":{"unresolvedName":"ws.send"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"/workspace/apps/web/src/utils/terminal-protocol.ts:encodeControlMessage","kind":"calls","metadata":{"unresolvedName":"encodeControlMessage"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:Date.now","kind":"calls","metadata":{"unresolvedName":"Date.now"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:ws.send","kind":"calls","metadata":{"unresolvedName":"ws.send"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"/workspace/apps/web/src/utils/terminal-protocol.ts:encodeControlMessage","kind":"calls","metadata":{"unresolvedName":"encodeControlMessage"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:clearTimers","kind":"calls","metadata":{"unresolvedName":"clearTimers"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:wsRef.current.close","kind":"calls","metadata":{"unresolvedName":"wsRef.current.close"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:setStableState","kind":"calls","metadata":{"unresolvedName":"setStableState"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:connect","kind":"calls","metadata":{"unresolvedName":"connect"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:connect","kind":"calls","metadata":{"unresolvedName":"connect"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:clearTimers","kind":"calls","metadata":{"unresolvedName":"clearTimers"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:clearTimeout","kind":"calls","metadata":{"unresolvedName":"clearTimeout"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:wsRef.current.close","kind":"calls","metadata":{"unresolvedName":"wsRef.current.close"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:clearTimers","kind":"calls","metadata":{"unresolvedName":"clearTimers"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:clearTimeout","kind":"calls","metadata":{"unresolvedName":"clearTimeout"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:wsRef.current.close","kind":"calls","metadata":{"unresolvedName":"wsRef.current.close"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"symbol-name:reconnect","kind":"calls","metadata":{"unresolvedName":"reconnect"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:window.addEventListener","kind":"calls","metadata":{"unresolvedName":"window.addEventListener"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:","to":"external:window.removeEventListener","kind":"calls","metadata":{"unresolvedName":"window.removeEventListener"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:handler","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:handler","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:handler","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:handler","to":"symbol-name:reconnect","kind":"calls","metadata":{"unresolvedName":"reconnect"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-sessions.ts","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-sessions.ts","to":"file:/workspace/apps/web/src/api/terminal.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-sessions.ts","to":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:useTerminalSessions","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-sessions.ts","to":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:useTerminalSessions","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:useTerminalSessions","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:useTerminalSessions","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:useTerminalSessions","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:useTerminalSessions","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:useTerminalSessions","to":"/workspace/apps/web/src/api/terminal.ts:listTerminalSessions","kind":"calls","metadata":{"unresolvedName":"listTerminalSessions"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:useTerminalSessions","to":"symbol-name:setSessions","kind":"calls","metadata":{"unresolvedName":"setSessions"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:useTerminalSessions","to":"symbol-name:setActiveSessionId","kind":"calls","metadata":{"unresolvedName":"setActiveSessionId"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:useTerminalSessions","to":"/workspace/apps/web/src/api/terminal.ts:createTerminalSession","kind":"calls","metadata":{"unresolvedName":"createTerminalSession"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:useTerminalSessions","to":"/workspace/apps/web/src/api/terminal.ts:closeTerminalSession","kind":"calls","metadata":{"unresolvedName":"closeTerminalSession"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:useTerminalSessions","to":"external:prev.filter","kind":"calls","metadata":{"unresolvedName":"prev.filter"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:useTerminalSessions","to":"/workspace/apps/web/src/api/terminal.ts:renameTerminalSession","kind":"calls","metadata":{"unresolvedName":"renameTerminalSession"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:useTerminalSessions","to":"external:prev.map","kind":"calls","metadata":{"unresolvedName":"prev.map"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:useTerminalSessions","to":"/workspace/apps/web/src/api/terminal.ts:resetTerminalSession","kind":"calls","metadata":{"unresolvedName":"resetTerminalSession"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:useTerminalSessions","to":"symbol-name:loadSessions","kind":"calls","metadata":{"unresolvedName":"loadSessions"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:useTerminalSessions","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-sessions.ts","to":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-sessions.ts","to":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","to":"/workspace/apps/web/src/api/terminal.ts:listTerminalSessions","kind":"calls","metadata":{"unresolvedName":"listTerminalSessions"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","to":"symbol-name:setSessions","kind":"calls","metadata":{"unresolvedName":"setSessions"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","to":"symbol-name:setActiveSessionId","kind":"calls","metadata":{"unresolvedName":"setActiveSessionId"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-sessions.ts","to":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-sessions.ts","to":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","to":"/workspace/apps/web/src/api/terminal.ts:createTerminalSession","kind":"calls","metadata":{"unresolvedName":"createTerminalSession"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","to":"symbol-name:setSessions","kind":"calls","metadata":{"unresolvedName":"setSessions"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","to":"symbol-name:setActiveSessionId","kind":"calls","metadata":{"unresolvedName":"setActiveSessionId"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-sessions.ts","to":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-sessions.ts","to":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","to":"/workspace/apps/web/src/api/terminal.ts:closeTerminalSession","kind":"calls","metadata":{"unresolvedName":"closeTerminalSession"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","to":"symbol-name:setSessions","kind":"calls","metadata":{"unresolvedName":"setSessions"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","to":"external:prev.filter","kind":"calls","metadata":{"unresolvedName":"prev.filter"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","to":"symbol-name:setActiveSessionId","kind":"calls","metadata":{"unresolvedName":"setActiveSessionId"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-sessions.ts","to":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-sessions.ts","to":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","to":"external:prev.filter","kind":"calls","metadata":{"unresolvedName":"prev.filter"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","to":"symbol-name:setActiveSessionId","kind":"calls","metadata":{"unresolvedName":"setActiveSessionId"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-sessions.ts","to":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-sessions.ts","to":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","to":"/workspace/apps/web/src/api/terminal.ts:renameTerminalSession","kind":"calls","metadata":{"unresolvedName":"renameTerminalSession"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","to":"symbol-name:setSessions","kind":"calls","metadata":{"unresolvedName":"setSessions"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","to":"external:prev.map","kind":"calls","metadata":{"unresolvedName":"prev.map"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-sessions.ts","to":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-sessions.ts","to":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","to":"/workspace/apps/web/src/api/terminal.ts:resetTerminalSession","kind":"calls","metadata":{"unresolvedName":"resetTerminalSession"}},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","to":"symbol-name:loadSessions","kind":"calls","metadata":{"unresolvedName":"loadSessions"}},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-sessions.ts","to":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-terminal-sessions.ts","to":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-terminal-sessions.ts:","to":"symbol-name:loadSessions","kind":"calls","metadata":{"unresolvedName":"loadSessions"}},{"from":"file:/workspace/apps/web/src/hooks/use-theme.ts","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-theme.ts","to":"file:/workspace/apps/web/src/api/settings.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-theme.ts","to":"/workspace/apps/web/src/hooks/use-theme.ts:useTheme","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-theme.ts","to":"/workspace/apps/web/src/hooks/use-theme.ts:useTheme","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-theme.ts:useTheme","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/hooks/use-theme.ts:useTheme","to":"/workspace/apps/web/src/api/settings.ts:getUserConfig","kind":"calls","metadata":{"unresolvedName":"getUserConfig"}},{"from":"/workspace/apps/web/src/hooks/use-theme.ts:useTheme","to":"external:document.documentElement.removeAttribute","kind":"calls","metadata":{"unresolvedName":"document.documentElement.removeAttribute"}},{"from":"/workspace/apps/web/src/hooks/use-theme.ts:useTheme","to":"external:document.documentElement.setAttribute","kind":"calls","metadata":{"unresolvedName":"document.documentElement.setAttribute"}},{"from":"file:/workspace/apps/web/src/hooks/use-theme.ts","to":"/workspace/apps/web/src/hooks/use-theme.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-theme.ts","to":"/workspace/apps/web/src/hooks/use-theme.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-theme.ts:","to":"/workspace/apps/web/src/api/settings.ts:getUserConfig","kind":"calls","metadata":{"unresolvedName":"getUserConfig"}},{"from":"/workspace/apps/web/src/hooks/use-theme.ts:","to":"external:document.documentElement.removeAttribute","kind":"calls","metadata":{"unresolvedName":"document.documentElement.removeAttribute"}},{"from":"/workspace/apps/web/src/hooks/use-theme.ts:","to":"external:document.documentElement.setAttribute","kind":"calls","metadata":{"unresolvedName":"document.documentElement.setAttribute"}},{"from":"file:/workspace/apps/web/src/hooks/use-theme.ts","to":"/workspace/apps/web/src/hooks/use-theme.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-theme.ts","to":"/workspace/apps/web/src/hooks/use-theme.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-theme.ts:","to":"/workspace/apps/web/src/api/settings.ts:getUserConfig","kind":"calls","metadata":{"unresolvedName":"getUserConfig"}},{"from":"/workspace/apps/web/src/hooks/use-theme.ts:","to":"external:document.documentElement.removeAttribute","kind":"calls","metadata":{"unresolvedName":"document.documentElement.removeAttribute"}},{"from":"/workspace/apps/web/src/hooks/use-theme.ts:","to":"external:document.documentElement.setAttribute","kind":"calls","metadata":{"unresolvedName":"document.documentElement.setAttribute"}},{"from":"file:/workspace/apps/web/src/hooks/use-virtual-keyboard.ts","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-virtual-keyboard.ts","to":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:useVirtualKeyboard","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-virtual-keyboard.ts","to":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:useVirtualKeyboard","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:useVirtualKeyboard","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:useVirtualKeyboard","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:useVirtualKeyboard","to":"symbol-name:setState","kind":"calls","metadata":{"unresolvedName":"setState"}},{"from":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:useVirtualKeyboard","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:useVirtualKeyboard","to":"external:visualViewport.addEventListener","kind":"calls","metadata":{"unresolvedName":"visualViewport.addEventListener"}},{"from":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:useVirtualKeyboard","to":"external:window.addEventListener","kind":"calls","metadata":{"unresolvedName":"window.addEventListener"}},{"from":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:useVirtualKeyboard","to":"symbol-name:updateKeyboardState","kind":"calls","metadata":{"unresolvedName":"updateKeyboardState"}},{"from":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:useVirtualKeyboard","to":"external:visualViewport.removeEventListener","kind":"calls","metadata":{"unresolvedName":"visualViewport.removeEventListener"}},{"from":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:useVirtualKeyboard","to":"external:window.removeEventListener","kind":"calls","metadata":{"unresolvedName":"window.removeEventListener"}},{"from":"file:/workspace/apps/web/src/hooks/use-virtual-keyboard.ts","to":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-virtual-keyboard.ts","to":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:","to":"symbol-name:setState","kind":"calls","metadata":{"unresolvedName":"setState"}},{"from":"file:/workspace/apps/web/src/hooks/use-virtual-keyboard.ts","to":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-virtual-keyboard.ts","to":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:","to":"external:visualViewport.addEventListener","kind":"calls","metadata":{"unresolvedName":"visualViewport.addEventListener"}},{"from":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:","to":"external:window.addEventListener","kind":"calls","metadata":{"unresolvedName":"window.addEventListener"}},{"from":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:","to":"symbol-name:updateKeyboardState","kind":"calls","metadata":{"unresolvedName":"updateKeyboardState"}},{"from":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:","to":"external:visualViewport.removeEventListener","kind":"calls","metadata":{"unresolvedName":"visualViewport.removeEventListener"}},{"from":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:","to":"external:window.removeEventListener","kind":"calls","metadata":{"unresolvedName":"window.removeEventListener"}},{"from":"file:/workspace/apps/web/src/hooks/use-virtual-keyboard.ts","to":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-virtual-keyboard.ts","to":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:","to":"external:visualViewport.removeEventListener","kind":"calls","metadata":{"unresolvedName":"visualViewport.removeEventListener"}},{"from":"/workspace/apps/web/src/hooks/use-virtual-keyboard.ts:","to":"external:window.removeEventListener","kind":"calls","metadata":{"unresolvedName":"window.removeEventListener"}},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-actions.ts","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-actions.ts","to":"file:/workspace/apps/web/src/api/workspaces.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-actions.ts","to":"file:/workspace/apps/web/src/types/workspace.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-actions.ts","to":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:useWorkspaceActions","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-actions.ts","to":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:useWorkspaceActions","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:useWorkspaceActions","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:useWorkspaceActions","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:useWorkspaceActions","to":"/workspace/apps/web/src/api/workspaces.ts:createWorkspace","kind":"calls","metadata":{"unresolvedName":"createWorkspace"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:useWorkspaceActions","to":"symbol-name:setLoadingId","kind":"calls","metadata":{"unresolvedName":"setLoadingId"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:useWorkspaceActions","to":"/workspace/apps/web/src/api/workspaces.ts:deleteWorkspace","kind":"calls","metadata":{"unresolvedName":"deleteWorkspace"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:useWorkspaceActions","to":"symbol-name:onRefresh","kind":"calls","metadata":{"unresolvedName":"onRefresh"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:useWorkspaceActions","to":"external:window.confirm","kind":"calls","metadata":{"unresolvedName":"window.confirm"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:useWorkspaceActions","to":"external:instances.map((i) => `- ${i.name}`).join","kind":"calls","metadata":{"unresolvedName":"instances.map((i) => `- ${i.name}`).join"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:useWorkspaceActions","to":"external:instances.map","kind":"calls","metadata":{"unresolvedName":"instances.map"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:useWorkspaceActions","to":"/workspace/apps/web/src/api/workspaces.ts:syncWorkspace","kind":"calls","metadata":{"unresolvedName":"syncWorkspace"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:useWorkspaceActions","to":"/workspace/apps/web/src/api/workspaces.ts:updateWorkspace","kind":"calls","metadata":{"unresolvedName":"updateWorkspace"}},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-actions.ts","to":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-actions.ts","to":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:","to":"/workspace/apps/web/src/api/workspaces.ts:createWorkspace","kind":"calls","metadata":{"unresolvedName":"createWorkspace"}},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-actions.ts","to":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-actions.ts","to":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:","to":"symbol-name:setLoadingId","kind":"calls","metadata":{"unresolvedName":"setLoadingId"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:","to":"/workspace/apps/web/src/api/workspaces.ts:deleteWorkspace","kind":"calls","metadata":{"unresolvedName":"deleteWorkspace"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:","to":"symbol-name:onRefresh","kind":"calls","metadata":{"unresolvedName":"onRefresh"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:","to":"external:window.confirm","kind":"calls","metadata":{"unresolvedName":"window.confirm"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:","to":"external:instances.map((i) => `- ${i.name}`).join","kind":"calls","metadata":{"unresolvedName":"instances.map((i) => `- ${i.name}`).join"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:","to":"external:instances.map","kind":"calls","metadata":{"unresolvedName":"instances.map"}},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-actions.ts","to":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-actions.ts","to":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:","to":"symbol-name:setLoadingId","kind":"calls","metadata":{"unresolvedName":"setLoadingId"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:","to":"/workspace/apps/web/src/api/workspaces.ts:syncWorkspace","kind":"calls","metadata":{"unresolvedName":"syncWorkspace"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:","to":"symbol-name:onRefresh","kind":"calls","metadata":{"unresolvedName":"onRefresh"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:","to":"external:window.confirm","kind":"calls","metadata":{"unresolvedName":"window.confirm"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:","to":"/workspace/apps/web/src/api/workspaces.ts:deleteWorkspace","kind":"calls","metadata":{"unresolvedName":"deleteWorkspace"}},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-actions.ts","to":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-actions.ts","to":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:","to":"/workspace/apps/web/src/api/workspaces.ts:updateWorkspace","kind":"calls","metadata":{"unresolvedName":"updateWorkspace"}},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-files.ts","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-files.ts","to":"file:/workspace/apps/web/src/api/workspace-files.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-files.ts","to":"/workspace/apps/web/src/hooks/use-workspace-files.ts:useWorkspaceFiles","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-files.ts","to":"/workspace/apps/web/src/hooks/use-workspace-files.ts:useWorkspaceFiles","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-workspace-files.ts:useWorkspaceFiles","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-files.ts:useWorkspaceFiles","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-files.ts:useWorkspaceFiles","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-files.ts:useWorkspaceFiles","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-files.ts:useWorkspaceFiles","to":"/workspace/apps/web/src/api/workspace-files.ts:listWorkspaceFiles","kind":"calls","metadata":{"unresolvedName":"listWorkspaceFiles"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-files.ts:useWorkspaceFiles","to":"symbol-name:setEntries","kind":"calls","metadata":{"unresolvedName":"setEntries"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-files.ts:useWorkspaceFiles","to":"/workspace/apps/web/src/api/workspace-files.ts:getWorkspaceFileContent","kind":"calls","metadata":{"unresolvedName":"getWorkspaceFileContent"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-files.ts:useWorkspaceFiles","to":"symbol-name:setContent","kind":"calls","metadata":{"unresolvedName":"setContent"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-files.ts:useWorkspaceFiles","to":"/workspace/apps/web/src/api/workspace-files.ts:saveWorkspaceFile","kind":"calls","metadata":{"unresolvedName":"saveWorkspaceFile"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-files.ts:useWorkspaceFiles","to":"symbol-name:refresh","kind":"calls","metadata":{"unresolvedName":"refresh"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-files.ts:useWorkspaceFiles","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-files.ts","to":"/workspace/apps/web/src/hooks/use-workspace-files.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-files.ts","to":"/workspace/apps/web/src/hooks/use-workspace-files.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-workspace-files.ts:","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-files.ts:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-files.ts:","to":"/workspace/apps/web/src/api/workspace-files.ts:listWorkspaceFiles","kind":"calls","metadata":{"unresolvedName":"listWorkspaceFiles"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-files.ts:","to":"symbol-name:setEntries","kind":"calls","metadata":{"unresolvedName":"setEntries"}},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-files.ts","to":"/workspace/apps/web/src/hooks/use-workspace-files.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-files.ts","to":"/workspace/apps/web/src/hooks/use-workspace-files.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-workspace-files.ts:","to":"/workspace/apps/web/src/api/workspace-files.ts:getWorkspaceFileContent","kind":"calls","metadata":{"unresolvedName":"getWorkspaceFileContent"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-files.ts:","to":"symbol-name:setContent","kind":"calls","metadata":{"unresolvedName":"setContent"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-files.ts:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-files.ts","to":"/workspace/apps/web/src/hooks/use-workspace-files.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-files.ts","to":"/workspace/apps/web/src/hooks/use-workspace-files.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-workspace-files.ts:","to":"/workspace/apps/web/src/api/workspace-files.ts:saveWorkspaceFile","kind":"calls","metadata":{"unresolvedName":"saveWorkspaceFile"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-files.ts:","to":"symbol-name:refresh","kind":"calls","metadata":{"unresolvedName":"refresh"}},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-files.ts","to":"/workspace/apps/web/src/hooks/use-workspace-files.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-files.ts","to":"/workspace/apps/web/src/hooks/use-workspace-files.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-workspace-files.ts:","to":"symbol-name:refresh","kind":"calls","metadata":{"unresolvedName":"refresh"}},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-git.ts","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-git.ts","to":"file:/workspace/apps/web/src/api/workspace-git.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-git.ts","to":"/workspace/apps/web/src/hooks/use-workspace-git.ts:useWorkspaceGit","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-git.ts","to":"/workspace/apps/web/src/hooks/use-workspace-git.ts:useWorkspaceGit","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:useWorkspaceGit","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:useWorkspaceGit","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:useWorkspaceGit","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:useWorkspaceGit","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:useWorkspaceGit","to":"external:Promise.all","kind":"calls","metadata":{"unresolvedName":"Promise.all"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:useWorkspaceGit","to":"/workspace/apps/web/src/api/workspace-git.ts:getGitStatus","kind":"calls","metadata":{"unresolvedName":"getGitStatus"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:useWorkspaceGit","to":"/workspace/apps/web/src/api/workspace-git.ts:getGitBranches","kind":"calls","metadata":{"unresolvedName":"getGitBranches"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:useWorkspaceGit","to":"/workspace/apps/web/src/api/workspace-git.ts:getGitHistory","kind":"calls","metadata":{"unresolvedName":"getGitHistory"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:useWorkspaceGit","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:useWorkspaceGit","to":"symbol-name:setBranches","kind":"calls","metadata":{"unresolvedName":"setBranches"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:useWorkspaceGit","to":"symbol-name:setCurrentBranch","kind":"calls","metadata":{"unresolvedName":"setCurrentBranch"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:useWorkspaceGit","to":"symbol-name:setHistory","kind":"calls","metadata":{"unresolvedName":"setHistory"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:useWorkspaceGit","to":"/workspace/apps/web/src/api/workspace-git.ts:gitCommit","kind":"calls","metadata":{"unresolvedName":"gitCommit"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:useWorkspaceGit","to":"symbol-name:refresh","kind":"calls","metadata":{"unresolvedName":"refresh"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:useWorkspaceGit","to":"/workspace/apps/web/src/api/workspace-git.ts:gitPush","kind":"calls","metadata":{"unresolvedName":"gitPush"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:useWorkspaceGit","to":"/workspace/apps/web/src/api/workspace-git.ts:gitPull","kind":"calls","metadata":{"unresolvedName":"gitPull"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:useWorkspaceGit","to":"/workspace/apps/web/src/api/workspace-git.ts:gitFetch","kind":"calls","metadata":{"unresolvedName":"gitFetch"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:useWorkspaceGit","to":"/workspace/apps/web/src/api/workspace-git.ts:gitCheckout","kind":"calls","metadata":{"unresolvedName":"gitCheckout"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:useWorkspaceGit","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-git.ts","to":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-git.ts","to":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","to":"external:Promise.all","kind":"calls","metadata":{"unresolvedName":"Promise.all"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","to":"/workspace/apps/web/src/api/workspace-git.ts:getGitStatus","kind":"calls","metadata":{"unresolvedName":"getGitStatus"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","to":"/workspace/apps/web/src/api/workspace-git.ts:getGitBranches","kind":"calls","metadata":{"unresolvedName":"getGitBranches"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","to":"/workspace/apps/web/src/api/workspace-git.ts:getGitHistory","kind":"calls","metadata":{"unresolvedName":"getGitHistory"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","to":"symbol-name:setBranches","kind":"calls","metadata":{"unresolvedName":"setBranches"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","to":"symbol-name:setCurrentBranch","kind":"calls","metadata":{"unresolvedName":"setCurrentBranch"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","to":"symbol-name:setHistory","kind":"calls","metadata":{"unresolvedName":"setHistory"}},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-git.ts","to":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-git.ts","to":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","to":"/workspace/apps/web/src/api/workspace-git.ts:gitCommit","kind":"calls","metadata":{"unresolvedName":"gitCommit"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","to":"symbol-name:refresh","kind":"calls","metadata":{"unresolvedName":"refresh"}},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-git.ts","to":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-git.ts","to":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","to":"/workspace/apps/web/src/api/workspace-git.ts:gitPush","kind":"calls","metadata":{"unresolvedName":"gitPush"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","to":"symbol-name:refresh","kind":"calls","metadata":{"unresolvedName":"refresh"}},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-git.ts","to":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-git.ts","to":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","to":"/workspace/apps/web/src/api/workspace-git.ts:gitPull","kind":"calls","metadata":{"unresolvedName":"gitPull"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","to":"symbol-name:refresh","kind":"calls","metadata":{"unresolvedName":"refresh"}},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-git.ts","to":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-git.ts","to":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","to":"/workspace/apps/web/src/api/workspace-git.ts:gitFetch","kind":"calls","metadata":{"unresolvedName":"gitFetch"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","to":"symbol-name:refresh","kind":"calls","metadata":{"unresolvedName":"refresh"}},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-git.ts","to":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-git.ts","to":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","to":"/workspace/apps/web/src/api/workspace-git.ts:gitCheckout","kind":"calls","metadata":{"unresolvedName":"gitCheckout"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","to":"symbol-name:refresh","kind":"calls","metadata":{"unresolvedName":"refresh"}},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-git.ts","to":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-git.ts","to":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-workspace-git.ts:","to":"symbol-name:refresh","kind":"calls","metadata":{"unresolvedName":"refresh"}},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-instances.ts","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-instances.ts","to":"file:/workspace/apps/web/src/api/workspace-instances.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-instances.ts","to":"file:/workspace/apps/web/src/api/sessions.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-instances.ts","to":"/workspace/apps/web/src/hooks/use-workspace-instances.ts:useWorkspaceInstances","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-instances.ts","to":"/workspace/apps/web/src/hooks/use-workspace-instances.ts:useWorkspaceInstances","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-workspace-instances.ts:useWorkspaceInstances","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-instances.ts:useWorkspaceInstances","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-instances.ts:useWorkspaceInstances","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-instances.ts:useWorkspaceInstances","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-instances.ts:useWorkspaceInstances","to":"/workspace/apps/web/src/api/workspace-instances.ts:listWorkspaceInstances","kind":"calls","metadata":{"unresolvedName":"listWorkspaceInstances"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-instances.ts:useWorkspaceInstances","to":"symbol-name:setInstances","kind":"calls","metadata":{"unresolvedName":"setInstances"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-instances.ts:useWorkspaceInstances","to":"/workspace/apps/web/src/api/workspace-instances.ts:createWorkspaceInstance","kind":"calls","metadata":{"unresolvedName":"createWorkspaceInstance"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-instances.ts:useWorkspaceInstances","to":"symbol-name:refresh","kind":"calls","metadata":{"unresolvedName":"refresh"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-instances.ts:useWorkspaceInstances","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-instances.ts","to":"/workspace/apps/web/src/hooks/use-workspace-instances.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-instances.ts","to":"/workspace/apps/web/src/hooks/use-workspace-instances.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-workspace-instances.ts:","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-instances.ts:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-instances.ts:","to":"/workspace/apps/web/src/api/workspace-instances.ts:listWorkspaceInstances","kind":"calls","metadata":{"unresolvedName":"listWorkspaceInstances"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-instances.ts:","to":"symbol-name:setInstances","kind":"calls","metadata":{"unresolvedName":"setInstances"}},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-instances.ts","to":"/workspace/apps/web/src/hooks/use-workspace-instances.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-instances.ts","to":"/workspace/apps/web/src/hooks/use-workspace-instances.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-workspace-instances.ts:","to":"/workspace/apps/web/src/api/workspace-instances.ts:createWorkspaceInstance","kind":"calls","metadata":{"unresolvedName":"createWorkspaceInstance"}},{"from":"/workspace/apps/web/src/hooks/use-workspace-instances.ts:","to":"symbol-name:refresh","kind":"calls","metadata":{"unresolvedName":"refresh"}},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-instances.ts","to":"/workspace/apps/web/src/hooks/use-workspace-instances.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-workspace-instances.ts","to":"/workspace/apps/web/src/hooks/use-workspace-instances.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-workspace-instances.ts:","to":"symbol-name:refresh","kind":"calls","metadata":{"unresolvedName":"refresh"}},{"from":"file:/workspace/apps/web/src/hooks/use-workspaces.ts","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-workspaces.ts","to":"file:/workspace/apps/web/src/api/workspaces.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-workspaces.ts","to":"file:/workspace/apps/web/src/types/workspace.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/hooks/use-workspaces.ts","to":"/workspace/apps/web/src/hooks/use-workspaces.ts:useWorkspaces","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-workspaces.ts","to":"/workspace/apps/web/src/hooks/use-workspaces.ts:useWorkspaces","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-workspaces.ts:useWorkspaces","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/hooks/use-workspaces.ts:useWorkspaces","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/hooks/use-workspaces.ts:useWorkspaces","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/hooks/use-workspaces.ts:useWorkspaces","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/hooks/use-workspaces.ts:useWorkspaces","to":"/workspace/apps/web/src/api/workspaces.ts:listWorkspaces","kind":"calls","metadata":{"unresolvedName":"listWorkspaces"}},{"from":"/workspace/apps/web/src/hooks/use-workspaces.ts:useWorkspaces","to":"/workspace/apps/web/src/api/workspaces.ts:listAllWorkspaces","kind":"calls","metadata":{"unresolvedName":"listAllWorkspaces"}},{"from":"/workspace/apps/web/src/hooks/use-workspaces.ts:useWorkspaces","to":"symbol-name:setWorkspaces","kind":"calls","metadata":{"unresolvedName":"setWorkspaces"}},{"from":"/workspace/apps/web/src/hooks/use-workspaces.ts:useWorkspaces","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/hooks/use-workspaces.ts:useWorkspaces","to":"symbol-name:refresh","kind":"calls","metadata":{"unresolvedName":"refresh"}},{"from":"file:/workspace/apps/web/src/hooks/use-workspaces.ts","to":"/workspace/apps/web/src/hooks/use-workspaces.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-workspaces.ts","to":"/workspace/apps/web/src/hooks/use-workspaces.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-workspaces.ts:","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/hooks/use-workspaces.ts:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/hooks/use-workspaces.ts:","to":"/workspace/apps/web/src/api/workspaces.ts:listWorkspaces","kind":"calls","metadata":{"unresolvedName":"listWorkspaces"}},{"from":"/workspace/apps/web/src/hooks/use-workspaces.ts:","to":"/workspace/apps/web/src/api/workspaces.ts:listAllWorkspaces","kind":"calls","metadata":{"unresolvedName":"listAllWorkspaces"}},{"from":"/workspace/apps/web/src/hooks/use-workspaces.ts:","to":"symbol-name:setWorkspaces","kind":"calls","metadata":{"unresolvedName":"setWorkspaces"}},{"from":"file:/workspace/apps/web/src/hooks/use-workspaces.ts","to":"/workspace/apps/web/src/hooks/use-workspaces.ts:","kind":"contains"},{"from":"file:/workspace/apps/web/src/hooks/use-workspaces.ts","to":"/workspace/apps/web/src/hooks/use-workspaces.ts:","kind":"defines"},{"from":"/workspace/apps/web/src/hooks/use-workspaces.ts:","to":"symbol-name:refresh","kind":"calls","metadata":{"unresolvedName":"refresh"}},{"from":"file:/workspace/apps/web/src/main.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/main.tsx","to":"external:react-dom/client","kind":"imports"},{"from":"file:/workspace/apps/web/src/main.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/main.tsx","to":"file:/workspace/apps/web/src/router.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/main.tsx","to":"file:/workspace/apps/web/src/state/auth.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/main.tsx","to":"file:/workspace/apps/web/src/state/sessions.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/main.tsx","to":"file:/workspace/apps/web/src/styles/tokens.css","kind":"imports"},{"from":"file:/workspace/apps/web/src/main.tsx","to":"file:/workspace/apps/web/src/styles/global.css","kind":"imports"},{"from":"file:/workspace/apps/web/src/main.tsx","to":"file:/workspace/apps/web/src/styles/utilities.css","kind":"imports"},{"from":"file:/workspace/apps/web/src/main.tsx","to":"file:/workspace/apps/web/src/styles/syntax-highlight.css","kind":"imports"},{"from":"file:/workspace/apps/web/src/main.tsx","to":"file:/workspace/apps/web/src/styles/pages/sessions.css","kind":"imports"},{"from":"file:/workspace/apps/web/src/main.tsx","to":"file:/workspace/apps/web/src/styles/pages/repo-workspace.css","kind":"imports"},{"from":"file:/workspace/apps/web/src/main.tsx","to":"file:/workspace/apps/web/src/styles/pages/dashboard.css","kind":"imports"},{"from":"file:/workspace/apps/web/src/main.tsx","to":"file:/workspace/apps/web/src/styles/pages/projects.css","kind":"imports"},{"from":"file:/workspace/apps/web/src/main.tsx","to":"file:/workspace/apps/web/src/styles/pages/git-history.css","kind":"imports"},{"from":"file:/workspace/apps/web/src/main.tsx","to":"file:/workspace/apps/web/src/styles/pages/ssh-keys.css","kind":"imports"},{"from":"file:/workspace/apps/web/src/main.tsx","to":"file:/workspace/apps/web/src/styles/pages/settings.css","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.test.tsx","to":"external:@testing-library/react","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.test.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.test.tsx","to":"external:vitest","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.test.tsx","to":"file:/workspace/apps/web/src/pages/DashboardPage.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.test.tsx","to":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.test.tsx","to":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"symbol-name:beforeEach","kind":"calls","metadata":{"unresolvedName":"beforeEach"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:mockDashboard.mockReset","kind":"calls","metadata":{"unresolvedName":"mockDashboard.mockReset"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:mockSessions.mockReset","kind":"calls","metadata":{"unresolvedName":"mockSessions.mockReset"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:mockProjects.mockReset","kind":"calls","metadata":{"unresolvedName":"mockProjects.mockReset"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:mockRepos.mockReset","kind":"calls","metadata":{"unresolvedName":"mockRepos.mockReset"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:mockDashboard.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockDashboard.mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:mockSessions.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockSessions.mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:mockProjects.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockProjects.mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:mockRepos.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockRepos.mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:expect(screen.getByText(\"Loading overview...\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Loading overview...\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:expect(screen.getAllByText(\"Open sessions\").length).toBeGreaterThan","kind":"calls","metadata":{"unresolvedName":"expect(screen.getAllByText(\"Open sessions\").length).toBeGreaterThan"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:screen.getAllByText","kind":"calls","metadata":{"unresolvedName":"screen.getAllByText"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:expect(screen.getByText(\"Available projects\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Available projects\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:mockDashboard.mockRejectedValueOnce","kind":"calls","metadata":{"unresolvedName":"mockDashboard.mockRejectedValueOnce"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:mockSessions.mockRejectedValueOnce","kind":"calls","metadata":{"unresolvedName":"mockSessions.mockRejectedValueOnce"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:mockProjects.mockRejectedValueOnce","kind":"calls","metadata":{"unresolvedName":"mockProjects.mockRejectedValueOnce"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:screen.getAllByRole","kind":"calls","metadata":{"unresolvedName":"screen.getAllByRole"}},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.test.tsx","to":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.test.tsx","to":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:mockDashboard.mockReset","kind":"calls","metadata":{"unresolvedName":"mockDashboard.mockReset"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:mockSessions.mockReset","kind":"calls","metadata":{"unresolvedName":"mockSessions.mockReset"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:mockProjects.mockReset","kind":"calls","metadata":{"unresolvedName":"mockProjects.mockReset"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:mockRepos.mockReset","kind":"calls","metadata":{"unresolvedName":"mockRepos.mockReset"}},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.test.tsx","to":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.test.tsx","to":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:mockDashboard.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockDashboard.mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:mockSessions.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockSessions.mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:mockProjects.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockProjects.mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:mockRepos.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"mockRepos.mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:expect(screen.getByText(\"Loading overview...\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Loading overview...\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:expect(screen.getAllByText(\"Open sessions\").length).toBeGreaterThan","kind":"calls","metadata":{"unresolvedName":"expect(screen.getAllByText(\"Open sessions\").length).toBeGreaterThan"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:screen.getAllByText","kind":"calls","metadata":{"unresolvedName":"screen.getAllByText"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:expect(screen.getByText(\"Available projects\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Available projects\")).toBeInTheDocument"}},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.test.tsx","to":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.test.tsx","to":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:expect(screen.getAllByText(\"Open sessions\").length).toBeGreaterThan","kind":"calls","metadata":{"unresolvedName":"expect(screen.getAllByText(\"Open sessions\").length).toBeGreaterThan"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:screen.getAllByText","kind":"calls","metadata":{"unresolvedName":"screen.getAllByText"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:expect(screen.getByText(\"Available projects\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Available projects\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.test.tsx","to":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.test.tsx","to":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:mockDashboard.mockRejectedValueOnce","kind":"calls","metadata":{"unresolvedName":"mockDashboard.mockRejectedValueOnce"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:mockSessions.mockRejectedValueOnce","kind":"calls","metadata":{"unresolvedName":"mockSessions.mockRejectedValueOnce"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:mockProjects.mockRejectedValueOnce","kind":"calls","metadata":{"unresolvedName":"mockProjects.mockRejectedValueOnce"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:screen.getAllByRole","kind":"calls","metadata":{"unresolvedName":"screen.getAllByRole"}},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.test.tsx","to":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.test.tsx","to":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.tsx","to":"file:/workspace/apps/web/src/api/dashboard.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.tsx","to":"file:/workspace/apps/web/src/api/sessions.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.tsx","to":"file:/workspace/apps/web/src/api/projects.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.tsx","to":"file:/workspace/apps/web/src/api/tool-types.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.tsx","to":"file:/workspace/apps/web/src/types/session.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.tsx","to":"file:/workspace/apps/web/src/types/project.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.tsx","to":"file:/workspace/apps/web/src/types/tool-type.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.tsx","to":"file:/workspace/apps/web/src/components/features/dashboard","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.tsx","to":"file:/workspace/apps/web/src/components/ui","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.tsx","to":"file:/workspace/apps/web/src/hooks/use-dashboard-actions.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.tsx","to":"file:/workspace/apps/web/src/api/git-repositories.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.tsx","to":"file:/workspace/apps/web/src/types/git-repository.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.tsx","to":"/workspace/apps/web/src/pages/DashboardPage.tsx:HomePage","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.tsx","to":"/workspace/apps/web/src/pages/DashboardPage.tsx:HomePage","kind":"defines"},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:HomePage","to":"symbol-name:useNavigate","kind":"calls","metadata":{"unresolvedName":"useNavigate"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:HomePage","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:HomePage","to":"external:Array.isArray","kind":"calls","metadata":{"unresolvedName":"Array.isArray"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:HomePage","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:HomePage","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:HomePage","to":"external:Promise.all","kind":"calls","metadata":{"unresolvedName":"Promise.all"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:HomePage","to":"/workspace/apps/web/src/api/dashboard.ts:getDashboardSummary","kind":"calls","metadata":{"unresolvedName":"getDashboardSummary"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:HomePage","to":"/workspace/apps/web/src/api/projects.ts:listProjects","kind":"calls","metadata":{"unresolvedName":"listProjects"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:HomePage","to":"/workspace/apps/web/src/api/tool-types.ts:listToolTypes","kind":"calls","metadata":{"unresolvedName":"listToolTypes"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:HomePage","to":"symbol-name:setSummary","kind":"calls","metadata":{"unresolvedName":"setSummary"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:HomePage","to":"symbol-name:setSessions","kind":"calls","metadata":{"unresolvedName":"setSessions"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:HomePage","to":"symbol-name:setProjects","kind":"calls","metadata":{"unresolvedName":"setProjects"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:HomePage","to":"symbol-name:setToolTypes","kind":"calls","metadata":{"unresolvedName":"setToolTypes"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:HomePage","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:HomePage","to":"symbol-name:loadHome","kind":"calls","metadata":{"unresolvedName":"loadHome"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:HomePage","to":"symbol-name:setRepositories","kind":"calls","metadata":{"unresolvedName":"setRepositories"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:HomePage","to":"symbol-name:useMemo","kind":"calls","metadata":{"unresolvedName":"useMemo"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:HomePage","to":"external:safeSessions.filter","kind":"calls","metadata":{"unresolvedName":"safeSessions.filter"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:HomePage","to":"external:[\"running\", \"building\", \"pending\"].includes","kind":"calls","metadata":{"unresolvedName":"[\"running\", \"building\", \"pending\"].includes"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:HomePage","to":"external:safeSessions\n\t\t\t\t.filter","kind":"calls","metadata":{"unresolvedName":"safeSessions\n\t\t\t\t.filter"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:HomePage","to":"external:[\"stopped\", \"error\"].includes","kind":"calls","metadata":{"unresolvedName":"[\"stopped\", \"error\"].includes"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:HomePage","to":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:useDashboardActions","kind":"calls","metadata":{"unresolvedName":"useDashboardActions"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:HomePage","to":"symbol-name:navigate","kind":"calls","metadata":{"unresolvedName":"navigate"}},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.tsx","to":"/workspace/apps/web/src/pages/DashboardPage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.tsx","to":"/workspace/apps/web/src/pages/DashboardPage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:","to":"external:Promise.all","kind":"calls","metadata":{"unresolvedName":"Promise.all"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:","to":"/workspace/apps/web/src/api/dashboard.ts:getDashboardSummary","kind":"calls","metadata":{"unresolvedName":"getDashboardSummary"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:","to":"/workspace/apps/web/src/api/projects.ts:listProjects","kind":"calls","metadata":{"unresolvedName":"listProjects"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:","to":"/workspace/apps/web/src/api/tool-types.ts:listToolTypes","kind":"calls","metadata":{"unresolvedName":"listToolTypes"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:","to":"symbol-name:setSummary","kind":"calls","metadata":{"unresolvedName":"setSummary"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:","to":"symbol-name:setSessions","kind":"calls","metadata":{"unresolvedName":"setSessions"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:","to":"symbol-name:setProjects","kind":"calls","metadata":{"unresolvedName":"setProjects"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:","to":"symbol-name:setToolTypes","kind":"calls","metadata":{"unresolvedName":"setToolTypes"}},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.tsx","to":"/workspace/apps/web/src/pages/DashboardPage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.tsx","to":"/workspace/apps/web/src/pages/DashboardPage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:","to":"symbol-name:loadHome","kind":"calls","metadata":{"unresolvedName":"loadHome"}},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.tsx","to":"/workspace/apps/web/src/pages/DashboardPage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/DashboardPage.tsx","to":"/workspace/apps/web/src/pages/DashboardPage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:","to":"symbol-name:setRepositories","kind":"calls","metadata":{"unresolvedName":"setRepositories"}},{"from":"file:/workspace/apps/web/src/pages/GitHistoryPage.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/GitHistoryPage.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/GitHistoryPage.tsx","to":"file:/workspace/apps/web/src/api/git-repositories.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/GitHistoryPage.tsx","to":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/GitHistoryPage.tsx","to":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:GitHistoryPage","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/GitHistoryPage.tsx","to":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:GitHistoryPage","kind":"defines"},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:GitHistoryPage","to":"symbol-name:useParams","kind":"calls","metadata":{"unresolvedName":"useParams"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:GitHistoryPage","to":"symbol-name:useNavigate","kind":"calls","metadata":{"unresolvedName":"useNavigate"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:GitHistoryPage","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:GitHistoryPage","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:GitHistoryPage","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:GitHistoryPage","to":"symbol-name:setCommits","kind":"calls","metadata":{"unresolvedName":"setCommits"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:GitHistoryPage","to":"symbol-name:setBranches","kind":"calls","metadata":{"unresolvedName":"setBranches"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:GitHistoryPage","to":"symbol-name:setSelectedBranch","kind":"calls","metadata":{"unresolvedName":"setSelectedBranch"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:GitHistoryPage","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:GitHistoryPage","to":"symbol-name:loadHistory","kind":"calls","metadata":{"unresolvedName":"loadHistory"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:GitHistoryPage","to":"symbol-name:setSelectedCommit","kind":"calls","metadata":{"unresolvedName":"setSelectedCommit"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:GitHistoryPage","to":"symbol-name:setDetailStatus","kind":"calls","metadata":{"unresolvedName":"setDetailStatus"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:GitHistoryPage","to":"symbol-name:setCommitDetail","kind":"calls","metadata":{"unresolvedName":"setCommitDetail"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:GitHistoryPage","to":"external:new Date(dateStr).toLocaleString","kind":"calls","metadata":{"unresolvedName":"new Date(dateStr).toLocaleString"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:GitHistoryPage","to":"external:branches.map","kind":"calls","metadata":{"unresolvedName":"branches.map"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:GitHistoryPage","to":"symbol-name:navigate","kind":"calls","metadata":{"unresolvedName":"navigate"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:GitHistoryPage","to":"external:commits.map","kind":"calls","metadata":{"unresolvedName":"commits.map"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:GitHistoryPage","to":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:handleCommitClick","kind":"calls","metadata":{"unresolvedName":"handleCommitClick"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:GitHistoryPage","to":"external:commit.refs.map","kind":"calls","metadata":{"unresolvedName":"commit.refs.map"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:GitHistoryPage","to":"external:commit.message.split","kind":"calls","metadata":{"unresolvedName":"commit.message.split"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:GitHistoryPage","to":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:formatDate","kind":"calls","metadata":{"unresolvedName":"formatDate"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:GitHistoryPage","to":"external:commitDetail.parents.map","kind":"calls","metadata":{"unresolvedName":"commitDetail.parents.map"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:GitHistoryPage","to":"external:parent.substring","kind":"calls","metadata":{"unresolvedName":"parent.substring"}},{"from":"file:/workspace/apps/web/src/pages/GitHistoryPage.tsx","to":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/GitHistoryPage.tsx","to":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:","to":"symbol-name:setCommits","kind":"calls","metadata":{"unresolvedName":"setCommits"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:","to":"symbol-name:setBranches","kind":"calls","metadata":{"unresolvedName":"setBranches"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:","to":"symbol-name:setSelectedBranch","kind":"calls","metadata":{"unresolvedName":"setSelectedBranch"}},{"from":"file:/workspace/apps/web/src/pages/GitHistoryPage.tsx","to":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/GitHistoryPage.tsx","to":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:","to":"symbol-name:loadHistory","kind":"calls","metadata":{"unresolvedName":"loadHistory"}},{"from":"file:/workspace/apps/web/src/pages/GitHistoryPage.tsx","to":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:handleCommitClick","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/GitHistoryPage.tsx","to":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:handleCommitClick","kind":"defines"},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:handleCommitClick","to":"symbol-name:setSelectedCommit","kind":"calls","metadata":{"unresolvedName":"setSelectedCommit"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:handleCommitClick","to":"symbol-name:setDetailStatus","kind":"calls","metadata":{"unresolvedName":"setDetailStatus"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:handleCommitClick","to":"symbol-name:setCommitDetail","kind":"calls","metadata":{"unresolvedName":"setCommitDetail"}},{"from":"file:/workspace/apps/web/src/pages/GitHistoryPage.tsx","to":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:handleCloseDetail","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/GitHistoryPage.tsx","to":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:handleCloseDetail","kind":"defines"},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:handleCloseDetail","to":"symbol-name:setSelectedCommit","kind":"calls","metadata":{"unresolvedName":"setSelectedCommit"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:handleCloseDetail","to":"symbol-name:setCommitDetail","kind":"calls","metadata":{"unresolvedName":"setCommitDetail"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:handleCloseDetail","to":"symbol-name:setDetailStatus","kind":"calls","metadata":{"unresolvedName":"setDetailStatus"}},{"from":"file:/workspace/apps/web/src/pages/GitHistoryPage.tsx","to":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:formatDate","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/GitHistoryPage.tsx","to":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:formatDate","kind":"defines"},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:formatDate","to":"external:new Date(dateStr).toLocaleString","kind":"calls","metadata":{"unresolvedName":"new Date(dateStr).toLocaleString"}},{"from":"file:/workspace/apps/web/src/pages/GitHistoryPage.tsx","to":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/GitHistoryPage.tsx","to":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:","to":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:handleCommitClick","kind":"calls","metadata":{"unresolvedName":"handleCommitClick"}},{"from":"file:/workspace/apps/web/src/pages/GitRepositoriesPage.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/GitRepositoriesPage.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/GitRepositoriesPage.tsx","to":"file:/workspace/apps/web/src/types/git-repository.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/GitRepositoriesPage.tsx","to":"file:/workspace/apps/web/src/api/git-repositories.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/GitRepositoriesPage.tsx","to":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/GitRepositoriesPage.tsx","to":"file:/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/GitRepositoriesPage.tsx","to":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:GitRepositoriesPage","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/GitRepositoriesPage.tsx","to":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:GitRepositoriesPage","kind":"defines"},{"from":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:GitRepositoriesPage","to":"symbol-name:useParams","kind":"calls","metadata":{"unresolvedName":"useParams"}},{"from":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:GitRepositoriesPage","to":"symbol-name:useNavigate","kind":"calls","metadata":{"unresolvedName":"useNavigate"}},{"from":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:GitRepositoriesPage","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:GitRepositoriesPage","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:GitRepositoriesPage","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:GitRepositoriesPage","to":"symbol-name:setRepositories","kind":"calls","metadata":{"unresolvedName":"setRepositories"}},{"from":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:GitRepositoriesPage","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:GitRepositoriesPage","to":"symbol-name:loadRepositories","kind":"calls","metadata":{"unresolvedName":"loadRepositories"}},{"from":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:GitRepositoriesPage","to":"symbol-name:setDeleteConfirmId","kind":"calls","metadata":{"unresolvedName":"setDeleteConfirmId"}},{"from":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:GitRepositoriesPage","to":"symbol-name:setShowCreate","kind":"calls","metadata":{"unresolvedName":"setShowCreate"}},{"from":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:GitRepositoriesPage","to":"external:repositories.map","kind":"calls","metadata":{"unresolvedName":"repositories.map"}},{"from":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:GitRepositoriesPage","to":"symbol-name:navigate","kind":"calls","metadata":{"unresolvedName":"navigate"}},{"from":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:GitRepositoriesPage","to":"symbol-name:handleDelete","kind":"calls","metadata":{"unresolvedName":"handleDelete"}},{"from":"file:/workspace/apps/web/src/pages/GitRepositoriesPage.tsx","to":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/GitRepositoriesPage.tsx","to":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:","to":"symbol-name:setRepositories","kind":"calls","metadata":{"unresolvedName":"setRepositories"}},{"from":"file:/workspace/apps/web/src/pages/GitRepositoriesPage.tsx","to":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/GitRepositoriesPage.tsx","to":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:","to":"symbol-name:loadRepositories","kind":"calls","metadata":{"unresolvedName":"loadRepositories"}},{"from":"file:/workspace/apps/web/src/pages/GitRepositoriesPage.tsx","to":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:handleDelete","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/GitRepositoriesPage.tsx","to":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:handleDelete","kind":"defines"},{"from":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:handleDelete","to":"symbol-name:setDeleteConfirmId","kind":"calls","metadata":{"unresolvedName":"setDeleteConfirmId"}},{"from":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:handleDelete","to":"symbol-name:loadRepositories","kind":"calls","metadata":{"unresolvedName":"loadRepositories"}},{"from":"file:/workspace/apps/web/src/pages/PlaceholderPage.tsx","to":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/PlaceholderPage.tsx","to":"/workspace/apps/web/src/pages/PlaceholderPage.tsx:PlaceholderPage","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/PlaceholderPage.tsx","to":"/workspace/apps/web/src/pages/PlaceholderPage.tsx:PlaceholderPage","kind":"defines"},{"from":"file:/workspace/apps/web/src/pages/PlaceholderPage.tsx","to":"/workspace/apps/web/src/pages/PlaceholderPage.tsx:NotFoundPage","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/PlaceholderPage.tsx","to":"/workspace/apps/web/src/pages/PlaceholderPage.tsx:NotFoundPage","kind":"defines"},{"from":"file:/workspace/apps/web/src/pages/PlaceholderPage.tsx","to":"/workspace/apps/web/src/pages/PlaceholderPage.tsx:LoginRedirectPage","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/PlaceholderPage.tsx","to":"/workspace/apps/web/src/pages/PlaceholderPage.tsx:LoginRedirectPage","kind":"defines"},{"from":"/workspace/apps/web/src/pages/PlaceholderPage.tsx:LoginRedirectPage","to":"external:new URLSearchParams(window.location.search).get","kind":"calls","metadata":{"unresolvedName":"new URLSearchParams(window.location.search).get"}},{"from":"/workspace/apps/web/src/pages/PlaceholderPage.tsx:LoginRedirectPage","to":"symbol-name:encodeURIComponent","kind":"calls","metadata":{"unresolvedName":"encodeURIComponent"}},{"from":"file:/workspace/apps/web/src/pages/ProfilePage.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ProfilePage.tsx","to":"file:/workspace/apps/web/src/api/profile.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ProfilePage.tsx","to":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ProfilePage.tsx","to":"file:/workspace/apps/web/src/state/auth.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ProfilePage.tsx","to":"file:/workspace/apps/web/src/api/profile.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ProfilePage.tsx","to":"/workspace/apps/web/src/pages/ProfilePage.tsx:ProfilePage","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProfilePage.tsx","to":"/workspace/apps/web/src/pages/ProfilePage.tsx:ProfilePage","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:ProfilePage","to":"/workspace/apps/web/src/state/auth.tsx:useAuth","kind":"calls","metadata":{"unresolvedName":"useAuth"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:ProfilePage","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:ProfilePage","to":"symbol-name:useRef","kind":"calls","metadata":{"unresolvedName":"useRef"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:ProfilePage","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:ProfilePage","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:ProfilePage","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:ProfilePage","to":"/workspace/apps/web/src/api/profile.ts:getProfile","kind":"calls","metadata":{"unresolvedName":"getProfile"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:ProfilePage","to":"symbol-name:setProfile","kind":"calls","metadata":{"unresolvedName":"setProfile"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:ProfilePage","to":"symbol-name:setName","kind":"calls","metadata":{"unresolvedName":"setName"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:ProfilePage","to":"symbol-name:setEmail","kind":"calls","metadata":{"unresolvedName":"setEmail"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:ProfilePage","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:ProfilePage","to":"symbol-name:loadProfile","kind":"calls","metadata":{"unresolvedName":"loadProfile"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:ProfilePage","to":"external:name.trim","kind":"calls","metadata":{"unresolvedName":"name.trim"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:ProfilePage","to":"external:email.includes","kind":"calls","metadata":{"unresolvedName":"email.includes"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:ProfilePage","to":"/workspace/apps/web/src/api/profile.ts:updateProfile","kind":"calls","metadata":{"unresolvedName":"updateProfile"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:ProfilePage","to":"external:email.trim","kind":"calls","metadata":{"unresolvedName":"email.trim"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:ProfilePage","to":"symbol-name:refreshSession","kind":"calls","metadata":{"unresolvedName":"refreshSession"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:ProfilePage","to":"external:file.type.startsWith","kind":"calls","metadata":{"unresolvedName":"file.type.startsWith"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:ProfilePage","to":"/workspace/apps/web/src/api/profile.ts:uploadAvatar","kind":"calls","metadata":{"unresolvedName":"uploadAvatar"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:ProfilePage","to":"external:profile.name.charAt(0).toUpperCase","kind":"calls","metadata":{"unresolvedName":"profile.name.charAt(0).toUpperCase"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:ProfilePage","to":"external:profile.name.charAt","kind":"calls","metadata":{"unresolvedName":"profile.name.charAt"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:ProfilePage","to":"external:fileInputRef.current?.click","kind":"calls","metadata":{"unresolvedName":"fileInputRef.current?.click"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:ProfilePage","to":"symbol-name:handleSave","kind":"calls","metadata":{"unresolvedName":"handleSave"}},{"from":"file:/workspace/apps/web/src/pages/ProfilePage.tsx","to":"/workspace/apps/web/src/pages/ProfilePage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProfilePage.tsx","to":"/workspace/apps/web/src/pages/ProfilePage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:","to":"/workspace/apps/web/src/api/profile.ts:getProfile","kind":"calls","metadata":{"unresolvedName":"getProfile"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:","to":"symbol-name:setProfile","kind":"calls","metadata":{"unresolvedName":"setProfile"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:","to":"symbol-name:setName","kind":"calls","metadata":{"unresolvedName":"setName"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:","to":"symbol-name:setEmail","kind":"calls","metadata":{"unresolvedName":"setEmail"}},{"from":"file:/workspace/apps/web/src/pages/ProfilePage.tsx","to":"/workspace/apps/web/src/pages/ProfilePage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProfilePage.tsx","to":"/workspace/apps/web/src/pages/ProfilePage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:","to":"symbol-name:loadProfile","kind":"calls","metadata":{"unresolvedName":"loadProfile"}},{"from":"file:/workspace/apps/web/src/pages/ProfilePage.tsx","to":"/workspace/apps/web/src/pages/ProfilePage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProfilePage.tsx","to":"/workspace/apps/web/src/pages/ProfilePage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:","to":"external:name.trim","kind":"calls","metadata":{"unresolvedName":"name.trim"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:","to":"external:email.includes","kind":"calls","metadata":{"unresolvedName":"email.includes"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:","to":"/workspace/apps/web/src/api/profile.ts:updateProfile","kind":"calls","metadata":{"unresolvedName":"updateProfile"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:","to":"external:email.trim","kind":"calls","metadata":{"unresolvedName":"email.trim"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:","to":"symbol-name:setProfile","kind":"calls","metadata":{"unresolvedName":"setProfile"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:","to":"symbol-name:refreshSession","kind":"calls","metadata":{"unresolvedName":"refreshSession"}},{"from":"file:/workspace/apps/web/src/pages/ProfilePage.tsx","to":"/workspace/apps/web/src/pages/ProfilePage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProfilePage.tsx","to":"/workspace/apps/web/src/pages/ProfilePage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:","to":"external:file.type.startsWith","kind":"calls","metadata":{"unresolvedName":"file.type.startsWith"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:","to":"/workspace/apps/web/src/api/profile.ts:uploadAvatar","kind":"calls","metadata":{"unresolvedName":"uploadAvatar"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:","to":"symbol-name:setProfile","kind":"calls","metadata":{"unresolvedName":"setProfile"}},{"from":"/workspace/apps/web/src/pages/ProfilePage.tsx:","to":"symbol-name:refreshSession","kind":"calls","metadata":{"unresolvedName":"refreshSession"}},{"from":"file:/workspace/apps/web/src/pages/ProjectSettingsPage.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ProjectSettingsPage.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ProjectSettingsPage.tsx","to":"file:/workspace/apps/web/src/components/features/settings/SettingsTabLayout.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ProjectSettingsPage.tsx","to":"file:/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ProjectSettingsPage.tsx","to":"file:/workspace/apps/web/src/api/client.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ProjectSettingsPage.tsx","to":"file:/workspace/apps/web/src/types","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ProjectSettingsPage.tsx","to":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:GeneralSettings","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectSettingsPage.tsx","to":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:GeneralSettings","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:GeneralSettings","to":"symbol-name:useNavigate","kind":"calls","metadata":{"unresolvedName":"useNavigate"}},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:GeneralSettings","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:GeneralSettings","to":"symbol-name:setSaving","kind":"calls","metadata":{"unresolvedName":"setSaving"}},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:GeneralSettings","to":"external:apiClient.patch","kind":"calls","metadata":{"unresolvedName":"apiClient.patch"}},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:GeneralSettings","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:GeneralSettings","to":"external:window.confirm","kind":"calls","metadata":{"unresolvedName":"window.confirm"}},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:GeneralSettings","to":"external:apiClient.delete","kind":"calls","metadata":{"unresolvedName":"apiClient.delete"}},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:GeneralSettings","to":"symbol-name:navigate","kind":"calls","metadata":{"unresolvedName":"navigate"}},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:GeneralSettings","to":"symbol-name:setName","kind":"calls","metadata":{"unresolvedName":"setName"}},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:GeneralSettings","to":"symbol-name:setDescription","kind":"calls","metadata":{"unresolvedName":"setDescription"}},{"from":"file:/workspace/apps/web/src/pages/ProjectSettingsPage.tsx","to":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:handleSave","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectSettingsPage.tsx","to":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:handleSave","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:handleSave","to":"symbol-name:setSaving","kind":"calls","metadata":{"unresolvedName":"setSaving"}},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:handleSave","to":"external:apiClient.patch","kind":"calls","metadata":{"unresolvedName":"apiClient.patch"}},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:handleSave","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"file:/workspace/apps/web/src/pages/ProjectSettingsPage.tsx","to":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:handleDelete","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectSettingsPage.tsx","to":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:handleDelete","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:handleDelete","to":"external:window.confirm","kind":"calls","metadata":{"unresolvedName":"window.confirm"}},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:handleDelete","to":"external:apiClient.delete","kind":"calls","metadata":{"unresolvedName":"apiClient.delete"}},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:handleDelete","to":"symbol-name:navigate","kind":"calls","metadata":{"unresolvedName":"navigate"}},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:handleDelete","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"file:/workspace/apps/web/src/pages/ProjectSettingsPage.tsx","to":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:ProjectSettingsPage","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectSettingsPage.tsx","to":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:ProjectSettingsPage","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:ProjectSettingsPage","to":"symbol-name:useParams","kind":"calls","metadata":{"unresolvedName":"useParams"}},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:ProjectSettingsPage","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:ProjectSettingsPage","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:ProjectSettingsPage","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:ProjectSettingsPage","to":"symbol-name:setProject","kind":"calls","metadata":{"unresolvedName":"setProject"}},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:ProjectSettingsPage","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:ProjectSettingsPage","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:ProjectSettingsPage","to":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:fetchProject","kind":"calls","metadata":{"unresolvedName":"fetchProject"}},{"from":"file:/workspace/apps/web/src/pages/ProjectSettingsPage.tsx","to":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectSettingsPage.tsx","to":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:","to":"symbol-name:setProject","kind":"calls","metadata":{"unresolvedName":"setProject"}},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:","to":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:fetchProject","kind":"calls","metadata":{"unresolvedName":"fetchProject"}},{"from":"file:/workspace/apps/web/src/pages/ProjectSettingsPage.tsx","to":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:fetchProject","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectSettingsPage.tsx","to":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:fetchProject","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:fetchProject","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:fetchProject","to":"symbol-name:setProject","kind":"calls","metadata":{"unresolvedName":"setProject"}},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:fetchProject","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/pages/ProjectSettingsPage.tsx:fetchProject","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"external:@testing-library/react","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"external:vitest","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"file:/workspace/apps/web/src/pages/ProjectsPage.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"file:/workspace/apps/web/src/api/projects.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:cleanup","kind":"calls","metadata":{"unresolvedName":"cleanup"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:vi.restoreAllMocks","kind":"calls","metadata":{"unresolvedName":"vi.restoreAllMocks"}},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:vi.spyOn(projectsApi, \"listProjects\").mockImplementation","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(projectsApi, \"listProjects\").mockImplementation"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:vi.spyOn","kind":"calls","metadata":{"unresolvedName":"vi.spyOn"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:renderWithRouter","kind":"calls","metadata":{"unresolvedName":"renderWithRouter"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByText(/loading projects/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/loading projects/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:vi.spyOn(projectsApi, \"listProjects\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(projectsApi, \"listProjects\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByText(\"Alpha Project\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Alpha Project\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByText(\"Beta Project\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Beta Project\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByText(\"First project\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"First project\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByText(/no projects yet/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/no projects yet/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:vi.spyOn(projectsApi, \"listProjects\").mockRejectedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(projectsApi, \"listProjects\").mockRejectedValue"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByText(/failed to load projects/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/failed to load projects/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByRole(\"button\", { name: /retry/i })).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByRole(\"button\", { name: /retry/i })).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:vi\n\t\t\t.spyOn(projectsApi, \"listProjects\")\n\t\t\t.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi\n\t\t\t.spyOn(projectsApi, \"listProjects\")\n\t\t\t.mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:vi\n\t\t\t.spyOn","kind":"calls","metadata":{"unresolvedName":"vi\n\t\t\t.spyOn"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:vi\n\t\t\t.spyOn(projectsApi, \"createProject\")\n\t\t\t.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi\n\t\t\t.spyOn(projectsApi, \"createProject\")\n\t\t\t.mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByRole(\"dialog\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByRole(\"dialog\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:fireEvent.change","kind":"calls","metadata":{"unresolvedName":"fireEvent.change"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen.getByPlaceholderText","kind":"calls","metadata":{"unresolvedName":"screen.getByPlaceholderText"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(createMock).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(createMock).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(listMock).toHaveBeenCalledTimes","kind":"calls","metadata":{"unresolvedName":"expect(listMock).toHaveBeenCalledTimes"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByText(/project name is required/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/project name is required/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen\n\t\t\t.getByText(\"Alpha Project\")\n\t\t\t.closest","kind":"calls","metadata":{"unresolvedName":"screen\n\t\t\t.getByText(\"Alpha Project\")\n\t\t\t.closest"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen\n\t\t\t.getByText","kind":"calls","metadata":{"unresolvedName":"screen\n\t\t\t.getByText"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:within(alphaCard).getByRole","kind":"calls","metadata":{"unresolvedName":"within(alphaCard).getByRole"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:within","kind":"calls","metadata":{"unresolvedName":"within"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(settingsLink).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(settingsLink).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(settingsLink).toHaveAttribute","kind":"calls","metadata":{"unresolvedName":"expect(settingsLink).toHaveAttribute"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:alphaCard.querySelector","kind":"calls","metadata":{"unresolvedName":"alphaCard.querySelector"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(workspaceLink).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(workspaceLink).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(workspaceLink).toHaveAttribute","kind":"calls","metadata":{"unresolvedName":"expect(workspaceLink).toHaveAttribute"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:actions.querySelectorAll","kind":"calls","metadata":{"unresolvedName":"actions.querySelectorAll"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(lastAction).toBe","kind":"calls","metadata":{"unresolvedName":"expect(lastAction).toBe"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:vi\n\t\t\t.spyOn(projectsApi, \"deleteProject\")\n\t\t\t.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi\n\t\t\t.spyOn(projectsApi, \"deleteProject\")\n\t\t\t.mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(within(alphaCard).getByText(/are you sure/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(within(alphaCard).getByText(/are you sure/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:within(alphaCard).getByText","kind":"calls","metadata":{"unresolvedName":"within(alphaCard).getByText"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(deleteMock).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(deleteMock).toHaveBeenCalledWith"}},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:vi.spyOn(projectsApi, \"listProjects\").mockImplementation","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(projectsApi, \"listProjects\").mockImplementation"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:vi.spyOn","kind":"calls","metadata":{"unresolvedName":"vi.spyOn"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:renderWithRouter","kind":"calls","metadata":{"unresolvedName":"renderWithRouter"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByText(/loading projects/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/loading projects/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:vi.spyOn(projectsApi, \"listProjects\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(projectsApi, \"listProjects\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:vi.spyOn","kind":"calls","metadata":{"unresolvedName":"vi.spyOn"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:renderWithRouter","kind":"calls","metadata":{"unresolvedName":"renderWithRouter"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByText(\"Alpha Project\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Alpha Project\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByText(\"Beta Project\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Beta Project\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByText(\"First project\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"First project\")).toBeInTheDocument"}},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByText(\"Alpha Project\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Alpha Project\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:vi.spyOn(projectsApi, \"listProjects\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(projectsApi, \"listProjects\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:vi.spyOn","kind":"calls","metadata":{"unresolvedName":"vi.spyOn"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:renderWithRouter","kind":"calls","metadata":{"unresolvedName":"renderWithRouter"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByText(/no projects yet/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/no projects yet/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByText(/no projects yet/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/no projects yet/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:vi.spyOn(projectsApi, \"listProjects\").mockRejectedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(projectsApi, \"listProjects\").mockRejectedValue"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:vi.spyOn","kind":"calls","metadata":{"unresolvedName":"vi.spyOn"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:renderWithRouter","kind":"calls","metadata":{"unresolvedName":"renderWithRouter"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByText(/failed to load projects/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/failed to load projects/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByRole(\"button\", { name: /retry/i })).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByRole(\"button\", { name: /retry/i })).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByText(/failed to load projects/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/failed to load projects/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:vi\n\t\t\t.spyOn(projectsApi, \"listProjects\")\n\t\t\t.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi\n\t\t\t.spyOn(projectsApi, \"listProjects\")\n\t\t\t.mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:vi\n\t\t\t.spyOn","kind":"calls","metadata":{"unresolvedName":"vi\n\t\t\t.spyOn"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:vi\n\t\t\t.spyOn(projectsApi, \"createProject\")\n\t\t\t.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi\n\t\t\t.spyOn(projectsApi, \"createProject\")\n\t\t\t.mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:renderWithRouter","kind":"calls","metadata":{"unresolvedName":"renderWithRouter"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByText(/no projects yet/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/no projects yet/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByRole(\"dialog\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByRole(\"dialog\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:fireEvent.change","kind":"calls","metadata":{"unresolvedName":"fireEvent.change"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen.getByPlaceholderText","kind":"calls","metadata":{"unresolvedName":"screen.getByPlaceholderText"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(createMock).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(createMock).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(listMock).toHaveBeenCalledTimes","kind":"calls","metadata":{"unresolvedName":"expect(listMock).toHaveBeenCalledTimes"}},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByText(/no projects yet/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/no projects yet/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(createMock).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(createMock).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:vi.spyOn(projectsApi, \"listProjects\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(projectsApi, \"listProjects\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:vi.spyOn","kind":"calls","metadata":{"unresolvedName":"vi.spyOn"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:renderWithRouter","kind":"calls","metadata":{"unresolvedName":"renderWithRouter"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByText(/no projects yet/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/no projects yet/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByText(/project name is required/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/project name is required/i)).toBeInTheDocument"}},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByText(/no projects yet/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/no projects yet/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:vi.spyOn(projectsApi, \"listProjects\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(projectsApi, \"listProjects\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:vi.spyOn","kind":"calls","metadata":{"unresolvedName":"vi.spyOn"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:renderWithRouter","kind":"calls","metadata":{"unresolvedName":"renderWithRouter"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByText(\"Alpha Project\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Alpha Project\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen\n\t\t\t.getByText(\"Alpha Project\")\n\t\t\t.closest","kind":"calls","metadata":{"unresolvedName":"screen\n\t\t\t.getByText(\"Alpha Project\")\n\t\t\t.closest"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen\n\t\t\t.getByText","kind":"calls","metadata":{"unresolvedName":"screen\n\t\t\t.getByText"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:within(alphaCard).getByRole","kind":"calls","metadata":{"unresolvedName":"within(alphaCard).getByRole"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:within","kind":"calls","metadata":{"unresolvedName":"within"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(settingsLink).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(settingsLink).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(settingsLink).toHaveAttribute","kind":"calls","metadata":{"unresolvedName":"expect(settingsLink).toHaveAttribute"}},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByText(\"Alpha Project\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Alpha Project\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:vi.spyOn(projectsApi, \"listProjects\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(projectsApi, \"listProjects\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:vi.spyOn","kind":"calls","metadata":{"unresolvedName":"vi.spyOn"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:renderWithRouter","kind":"calls","metadata":{"unresolvedName":"renderWithRouter"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByText(\"Alpha Project\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Alpha Project\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen\n\t\t\t.getByText(\"Alpha Project\")\n\t\t\t.closest","kind":"calls","metadata":{"unresolvedName":"screen\n\t\t\t.getByText(\"Alpha Project\")\n\t\t\t.closest"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen\n\t\t\t.getByText","kind":"calls","metadata":{"unresolvedName":"screen\n\t\t\t.getByText"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:alphaCard.querySelector","kind":"calls","metadata":{"unresolvedName":"alphaCard.querySelector"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:within(alphaCard).getByRole","kind":"calls","metadata":{"unresolvedName":"within(alphaCard).getByRole"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:within","kind":"calls","metadata":{"unresolvedName":"within"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(workspaceLink).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(workspaceLink).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(workspaceLink).toHaveAttribute","kind":"calls","metadata":{"unresolvedName":"expect(workspaceLink).toHaveAttribute"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:actions.querySelectorAll","kind":"calls","metadata":{"unresolvedName":"actions.querySelectorAll"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(lastAction).toBe","kind":"calls","metadata":{"unresolvedName":"expect(lastAction).toBe"}},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByText(\"Alpha Project\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Alpha Project\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:vi\n\t\t\t.spyOn(projectsApi, \"listProjects\")\n\t\t\t.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi\n\t\t\t.spyOn(projectsApi, \"listProjects\")\n\t\t\t.mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:vi\n\t\t\t.spyOn","kind":"calls","metadata":{"unresolvedName":"vi\n\t\t\t.spyOn"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:vi\n\t\t\t.spyOn(projectsApi, \"deleteProject\")\n\t\t\t.mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi\n\t\t\t.spyOn(projectsApi, \"deleteProject\")\n\t\t\t.mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:renderWithRouter","kind":"calls","metadata":{"unresolvedName":"renderWithRouter"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByText(\"Alpha Project\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Alpha Project\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen\n\t\t\t.getByText(\"Alpha Project\")\n\t\t\t.closest","kind":"calls","metadata":{"unresolvedName":"screen\n\t\t\t.getByText(\"Alpha Project\")\n\t\t\t.closest"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen\n\t\t\t.getByText","kind":"calls","metadata":{"unresolvedName":"screen\n\t\t\t.getByText"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:within(alphaCard).getByRole","kind":"calls","metadata":{"unresolvedName":"within(alphaCard).getByRole"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:within","kind":"calls","metadata":{"unresolvedName":"within"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(within(alphaCard).getByText(/are you sure/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(within(alphaCard).getByText(/are you sure/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:within(alphaCard).getByText","kind":"calls","metadata":{"unresolvedName":"within(alphaCard).getByText"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(deleteMock).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(deleteMock).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(listMock).toHaveBeenCalledTimes","kind":"calls","metadata":{"unresolvedName":"expect(listMock).toHaveBeenCalledTimes"}},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(screen.getByText(\"Alpha Project\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Alpha Project\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.test.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"external:expect(deleteMock).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(deleteMock).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.tsx","to":"file:/workspace/apps/web/src/api/projects.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.tsx","to":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.tsx","to":"file:/workspace/apps/web/src/types","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.tsx:ProjectsPage","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.tsx:ProjectsPage","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:ProjectsPage","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:ProjectsPage","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:ProjectsPage","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:ProjectsPage","to":"/workspace/apps/web/src/api/projects.ts:listProjects","kind":"calls","metadata":{"unresolvedName":"listProjects"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:ProjectsPage","to":"symbol-name:setProjects","kind":"calls","metadata":{"unresolvedName":"setProjects"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:ProjectsPage","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:ProjectsPage","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:loadProjects","kind":"calls","metadata":{"unresolvedName":"loadProjects"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:ProjectsPage","to":"symbol-name:setFormName","kind":"calls","metadata":{"unresolvedName":"setFormName"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:ProjectsPage","to":"symbol-name:setFormDescription","kind":"calls","metadata":{"unresolvedName":"setFormDescription"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:ProjectsPage","to":"symbol-name:setFormError","kind":"calls","metadata":{"unresolvedName":"setFormError"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:ProjectsPage","to":"symbol-name:setShowCreate","kind":"calls","metadata":{"unresolvedName":"setShowCreate"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:ProjectsPage","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:ProjectsPage","to":"external:formName.trim","kind":"calls","metadata":{"unresolvedName":"formName.trim"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:ProjectsPage","to":"external:formDescription.trim","kind":"calls","metadata":{"unresolvedName":"formDescription.trim"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:ProjectsPage","to":"/workspace/apps/web/src/api/projects.ts:createProject","kind":"calls","metadata":{"unresolvedName":"createProject"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:ProjectsPage","to":"/workspace/apps/web/src/pages/ProjectsPage.tsx:closeCreate","kind":"calls","metadata":{"unresolvedName":"closeCreate"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:ProjectsPage","to":"/workspace/apps/web/src/api/projects.ts:deleteProject","kind":"calls","metadata":{"unresolvedName":"deleteProject"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:ProjectsPage","to":"symbol-name:setDeleteConfirmId","kind":"calls","metadata":{"unresolvedName":"setDeleteConfirmId"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:ProjectsPage","to":"external:projects.map","kind":"calls","metadata":{"unresolvedName":"projects.map"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:ProjectsPage","to":"symbol-name:handleDelete","kind":"calls","metadata":{"unresolvedName":"handleDelete"}},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:","to":"/workspace/apps/web/src/api/projects.ts:listProjects","kind":"calls","metadata":{"unresolvedName":"listProjects"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:","to":"symbol-name:setProjects","kind":"calls","metadata":{"unresolvedName":"setProjects"}},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:loadProjects","kind":"calls","metadata":{"unresolvedName":"loadProjects"}},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.tsx:openCreate","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.tsx:openCreate","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:openCreate","to":"symbol-name:setFormName","kind":"calls","metadata":{"unresolvedName":"setFormName"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:openCreate","to":"symbol-name:setFormDescription","kind":"calls","metadata":{"unresolvedName":"setFormDescription"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:openCreate","to":"symbol-name:setFormError","kind":"calls","metadata":{"unresolvedName":"setFormError"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:openCreate","to":"symbol-name:setShowCreate","kind":"calls","metadata":{"unresolvedName":"setShowCreate"}},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.tsx:closeCreate","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.tsx:closeCreate","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:closeCreate","to":"symbol-name:setShowCreate","kind":"calls","metadata":{"unresolvedName":"setShowCreate"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:closeCreate","to":"symbol-name:setFormError","kind":"calls","metadata":{"unresolvedName":"setFormError"}},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.tsx:handleCreate","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.tsx:handleCreate","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:handleCreate","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:handleCreate","to":"symbol-name:setFormError","kind":"calls","metadata":{"unresolvedName":"setFormError"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:handleCreate","to":"external:formName.trim","kind":"calls","metadata":{"unresolvedName":"formName.trim"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:handleCreate","to":"external:formDescription.trim","kind":"calls","metadata":{"unresolvedName":"formDescription.trim"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:handleCreate","to":"/workspace/apps/web/src/api/projects.ts:createProject","kind":"calls","metadata":{"unresolvedName":"createProject"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:handleCreate","to":"/workspace/apps/web/src/pages/ProjectsPage.tsx:closeCreate","kind":"calls","metadata":{"unresolvedName":"closeCreate"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:handleCreate","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:loadProjects","kind":"calls","metadata":{"unresolvedName":"loadProjects"}},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.tsx:handleDelete","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ProjectsPage.tsx","to":"/workspace/apps/web/src/pages/ProjectsPage.tsx:handleDelete","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:handleDelete","to":"/workspace/apps/web/src/api/projects.ts:deleteProject","kind":"calls","metadata":{"unresolvedName":"deleteProject"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:handleDelete","to":"symbol-name:setDeleteConfirmId","kind":"calls","metadata":{"unresolvedName":"setDeleteConfirmId"}},{"from":"/workspace/apps/web/src/pages/ProjectsPage.tsx:handleDelete","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:loadProjects","kind":"calls","metadata":{"unresolvedName":"loadProjects"}},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"file:/workspace/apps/web/src/api/client.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"file:/workspace/apps/web/src/types/git-repository.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"file:/workspace/apps/web/src/types/tool-type.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"file:/workspace/apps/web/src/api/git-repositories.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"file:/workspace/apps/web/src/components/features/git","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"file:/workspace/apps/web/src/components/features/git/FileEditor.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"file:/workspace/apps/web/src/components/features/workspace/WorkspaceHeader.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"file:/workspace/apps/web/src/components/features/git/GitToolbar.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"file:/workspace/apps/web/src/components/ui","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"file:/workspace/apps/web/src/api/tool-types.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","kind":"defines"},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","to":"symbol-name:useParams","kind":"calls","metadata":{"unresolvedName":"useParams"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","to":"symbol-name:useSearchParams","kind":"calls","metadata":{"unresolvedName":"useSearchParams"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","to":"external:searchParams.get","kind":"calls","metadata":{"unresolvedName":"searchParams.get"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","to":"symbol-name:setProject","kind":"calls","metadata":{"unresolvedName":"setProject"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","to":"symbol-name:setRepositories","kind":"calls","metadata":{"unresolvedName":"setRepositories"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","to":"symbol-name:setSelectedRepoId","kind":"calls","metadata":{"unresolvedName":"setSelectedRepoId"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","to":"external:newParams.set","kind":"calls","metadata":{"unresolvedName":"newParams.set"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","to":"symbol-name:setSearchParams","kind":"calls","metadata":{"unresolvedName":"setSearchParams"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","to":"external:response.data.branches.map","kind":"calls","metadata":{"unresolvedName":"response.data.branches.map"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","to":"symbol-name:setBranches","kind":"calls","metadata":{"unresolvedName":"setBranches"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","to":"symbol-name:setCurrentBranch","kind":"calls","metadata":{"unresolvedName":"setCurrentBranch"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","to":"symbol-name:setGitStatus","kind":"calls","metadata":{"unresolvedName":"setGitStatus"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","to":"/workspace/apps/web/src/api/tool-types.ts:listToolTypes","kind":"calls","metadata":{"unresolvedName":"listToolTypes"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","to":"symbol-name:setToolTypes","kind":"calls","metadata":{"unresolvedName":"setToolTypes"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","to":"symbol-name:loadProject","kind":"calls","metadata":{"unresolvedName":"loadProject"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","to":"symbol-name:loadRepositories","kind":"calls","metadata":{"unresolvedName":"loadRepositories"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:loadToolTypes","kind":"calls","metadata":{"unresolvedName":"loadToolTypes"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","to":"/workspace/apps/web/src/components/create-session-form.tsx:loadBranches","kind":"calls","metadata":{"unresolvedName":"loadBranches"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","to":"symbol-name:loadGitStatus","kind":"calls","metadata":{"unresolvedName":"loadGitStatus"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","to":"external:newParams.delete","kind":"calls","metadata":{"unresolvedName":"newParams.delete"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","to":"external:repositories.find","kind":"calls","metadata":{"unresolvedName":"repositories.find"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","to":"symbol-name:Boolean","kind":"calls","metadata":{"unresolvedName":"Boolean"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","to":"external:window.dispatchEvent","kind":"calls","metadata":{"unresolvedName":"window.dispatchEvent"}},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","to":"symbol-name:setProject","kind":"calls","metadata":{"unresolvedName":"setProject"}},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","to":"symbol-name:setRepositories","kind":"calls","metadata":{"unresolvedName":"setRepositories"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","to":"symbol-name:setSelectedRepoId","kind":"calls","metadata":{"unresolvedName":"setSelectedRepoId"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","to":"external:newParams.set","kind":"calls","metadata":{"unresolvedName":"newParams.set"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","to":"symbol-name:setSearchParams","kind":"calls","metadata":{"unresolvedName":"setSearchParams"}},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","to":"external:response.data.branches.map","kind":"calls","metadata":{"unresolvedName":"response.data.branches.map"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","to":"symbol-name:setBranches","kind":"calls","metadata":{"unresolvedName":"setBranches"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","to":"symbol-name:setCurrentBranch","kind":"calls","metadata":{"unresolvedName":"setCurrentBranch"}},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","to":"symbol-name:setGitStatus","kind":"calls","metadata":{"unresolvedName":"setGitStatus"}},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","to":"/workspace/apps/web/src/api/tool-types.ts:listToolTypes","kind":"calls","metadata":{"unresolvedName":"listToolTypes"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","to":"symbol-name:setToolTypes","kind":"calls","metadata":{"unresolvedName":"setToolTypes"}},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","to":"symbol-name:loadProject","kind":"calls","metadata":{"unresolvedName":"loadProject"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","to":"symbol-name:loadRepositories","kind":"calls","metadata":{"unresolvedName":"loadRepositories"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:loadToolTypes","kind":"calls","metadata":{"unresolvedName":"loadToolTypes"}},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","to":"/workspace/apps/web/src/components/create-session-form.tsx:loadBranches","kind":"calls","metadata":{"unresolvedName":"loadBranches"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","to":"symbol-name:loadGitStatus","kind":"calls","metadata":{"unresolvedName":"loadGitStatus"}},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:handleRepoChange","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:handleRepoChange","kind":"defines"},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:handleRepoChange","to":"symbol-name:setSelectedRepoId","kind":"calls","metadata":{"unresolvedName":"setSelectedRepoId"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:handleRepoChange","to":"external:newParams.set","kind":"calls","metadata":{"unresolvedName":"newParams.set"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:handleRepoChange","to":"external:newParams.delete","kind":"calls","metadata":{"unresolvedName":"newParams.delete"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:handleRepoChange","to":"symbol-name:setSearchParams","kind":"calls","metadata":{"unresolvedName":"setSearchParams"}},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","to":"symbol-name:setCurrentBranch","kind":"calls","metadata":{"unresolvedName":"setCurrentBranch"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","to":"external:newParams.set","kind":"calls","metadata":{"unresolvedName":"newParams.set"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","to":"symbol-name:setSearchParams","kind":"calls","metadata":{"unresolvedName":"setSearchParams"}},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","to":"/workspace/apps/web/src/components/create-session-form.tsx:loadBranches","kind":"calls","metadata":{"unresolvedName":"loadBranches"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","to":"symbol-name:loadGitStatus","kind":"calls","metadata":{"unresolvedName":"loadGitStatus"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","to":"external:window.dispatchEvent","kind":"calls","metadata":{"unresolvedName":"window.dispatchEvent"}},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","to":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","to":"symbol-name:loadGitStatus","kind":"calls","metadata":{"unresolvedName":"loadGitStatus"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","to":"external:window.dispatchEvent","kind":"calls","metadata":{"unresolvedName":"window.dispatchEvent"}},{"from":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","to":"file:/workspace/apps/web/src/api/projects.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","to":"file:/workspace/apps/web/src/api/sessions.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","to":"file:/workspace/apps/web/src/api/tool-types.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","to":"file:/workspace/apps/web/src/api/settings.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","to":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","to":"file:/workspace/apps/web/src/components/ui","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","to":"file:/workspace/apps/web/src/components/features/session","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","to":"file:/workspace/apps/web/src/types/project.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","to":"file:/workspace/apps/web/src/types/session.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","to":"file:/workspace/apps/web/src/types/tool-type.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage","kind":"defines"},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage","to":"symbol-name:useNavigate","kind":"calls","metadata":{"unresolvedName":"useNavigate"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage","to":"external:Promise.all","kind":"calls","metadata":{"unresolvedName":"Promise.all"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage","to":"/workspace/apps/web/src/api/settings.ts:getUserConfig","kind":"calls","metadata":{"unresolvedName":"getUserConfig"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage","to":"symbol-name:setSessions","kind":"calls","metadata":{"unresolvedName":"setSessions"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage","to":"symbol-name:setLastSessionId","kind":"calls","metadata":{"unresolvedName":"setLastSessionId"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage","to":"symbol-name:loadSessions","kind":"calls","metadata":{"unresolvedName":"loadSessions"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage","to":"symbol-name:setProjects","kind":"calls","metadata":{"unresolvedName":"setProjects"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage","to":"/workspace/apps/web/src/api/projects.ts:listProjects","kind":"calls","metadata":{"unresolvedName":"listProjects"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:loadProjects","kind":"calls","metadata":{"unresolvedName":"loadProjects"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage","to":"symbol-name:setToolTypes","kind":"calls","metadata":{"unresolvedName":"setToolTypes"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage","to":"/workspace/apps/web/src/api/tool-types.ts:listToolTypes","kind":"calls","metadata":{"unresolvedName":"listToolTypes"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:loadToolTypes","kind":"calls","metadata":{"unresolvedName":"loadToolTypes"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage","to":"symbol-name:useMemo","kind":"calls","metadata":{"unresolvedName":"useMemo"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage","to":"external:sessions.filter","kind":"calls","metadata":{"unresolvedName":"sessions.filter"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage","to":"external:[\"running\", \"building\", \"pending\"].includes","kind":"calls","metadata":{"unresolvedName":"[\"running\", \"building\", \"pending\"].includes"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage","to":"external:sessions\n\t\t\t\t.filter((s) => [\"stopped\", \"error\"].includes(s.status))\n\t\t\t\t.slice","kind":"calls","metadata":{"unresolvedName":"sessions\n\t\t\t\t.filter((s) => [\"stopped\", \"error\"].includes(s.status))\n\t\t\t\t.slice"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage","to":"external:sessions\n\t\t\t\t.filter","kind":"calls","metadata":{"unresolvedName":"sessions\n\t\t\t\t.filter"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage","to":"external:[\"stopped\", \"error\"].includes","kind":"calls","metadata":{"unresolvedName":"[\"stopped\", \"error\"].includes"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage","to":"external:sessions.find","kind":"calls","metadata":{"unresolvedName":"sessions.find"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage","to":"external:session.tool_type_interfaces?.includes","kind":"calls","metadata":{"unresolvedName":"session.tool_type_interfaces?.includes"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage","to":"symbol-name:navigate","kind":"calls","metadata":{"unresolvedName":"navigate"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage","to":"external:projects.find","kind":"calls","metadata":{"unresolvedName":"projects.find"}},{"from":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:","to":"external:Promise.all","kind":"calls","metadata":{"unresolvedName":"Promise.all"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:","to":"/workspace/apps/web/src/api/settings.ts:getUserConfig","kind":"calls","metadata":{"unresolvedName":"getUserConfig"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:","to":"symbol-name:setSessions","kind":"calls","metadata":{"unresolvedName":"setSessions"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:","to":"symbol-name:setLastSessionId","kind":"calls","metadata":{"unresolvedName":"setLastSessionId"}},{"from":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:","to":"symbol-name:loadSessions","kind":"calls","metadata":{"unresolvedName":"loadSessions"}},{"from":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:","to":"symbol-name:setProjects","kind":"calls","metadata":{"unresolvedName":"setProjects"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:","to":"/workspace/apps/web/src/api/projects.ts:listProjects","kind":"calls","metadata":{"unresolvedName":"listProjects"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:loadProjects","kind":"calls","metadata":{"unresolvedName":"loadProjects"}},{"from":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:loadProjects","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:loadProjects","kind":"defines"},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:loadProjects","to":"symbol-name:setProjects","kind":"calls","metadata":{"unresolvedName":"setProjects"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:loadProjects","to":"/workspace/apps/web/src/api/projects.ts:listProjects","kind":"calls","metadata":{"unresolvedName":"listProjects"}},{"from":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:","to":"symbol-name:setToolTypes","kind":"calls","metadata":{"unresolvedName":"setToolTypes"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:","to":"/workspace/apps/web/src/api/tool-types.ts:listToolTypes","kind":"calls","metadata":{"unresolvedName":"listToolTypes"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:loadToolTypes","kind":"calls","metadata":{"unresolvedName":"loadToolTypes"}},{"from":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:loadToolTypes","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:loadToolTypes","kind":"defines"},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:loadToolTypes","to":"symbol-name:setToolTypes","kind":"calls","metadata":{"unresolvedName":"setToolTypes"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:loadToolTypes","to":"/workspace/apps/web/src/api/tool-types.ts:listToolTypes","kind":"calls","metadata":{"unresolvedName":"listToolTypes"}},{"from":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:","to":"external:session.tool_type_interfaces?.includes","kind":"calls","metadata":{"unresolvedName":"session.tool_type_interfaces?.includes"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:","to":"symbol-name:navigate","kind":"calls","metadata":{"unresolvedName":"navigate"}},{"from":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:","to":"external:projects.find","kind":"calls","metadata":{"unresolvedName":"projects.find"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:","to":"symbol-name:navigate","kind":"calls","metadata":{"unresolvedName":"navigate"}},{"from":"file:/workspace/apps/web/src/pages/SettingsPage.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/SettingsPage.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/SettingsPage.tsx","to":"file:/workspace/apps/web/src/api/settings.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/SettingsPage.tsx","to":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/SettingsPage.tsx","to":"/workspace/apps/web/src/pages/SettingsPage.tsx:SettingsPage","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/SettingsPage.tsx","to":"/workspace/apps/web/src/pages/SettingsPage.tsx:SettingsPage","kind":"defines"},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:SettingsPage","to":"symbol-name:useLocation","kind":"calls","metadata":{"unresolvedName":"useLocation"}},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:SettingsPage","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:SettingsPage","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:SettingsPage","to":"/workspace/apps/web/src/api/settings.ts:getUserConfig","kind":"calls","metadata":{"unresolvedName":"getUserConfig"}},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:SettingsPage","to":"symbol-name:setConfig","kind":"calls","metadata":{"unresolvedName":"setConfig"}},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:SettingsPage","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:SettingsPage","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:SettingsPage","to":"symbol-name:loadConfig","kind":"calls","metadata":{"unresolvedName":"loadConfig"}},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:SettingsPage","to":"symbol-name:setSaveStatus","kind":"calls","metadata":{"unresolvedName":"setSaveStatus"}},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:SettingsPage","to":"/workspace/apps/web/src/api/settings.ts:updateUserConfig","kind":"calls","metadata":{"unresolvedName":"updateUserConfig"}},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:SettingsPage","to":"external:document.documentElement.removeAttribute","kind":"calls","metadata":{"unresolvedName":"document.documentElement.removeAttribute"}},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:SettingsPage","to":"external:document.documentElement.setAttribute","kind":"calls","metadata":{"unresolvedName":"document.documentElement.setAttribute"}},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:SettingsPage","to":"external:window.setTimeout","kind":"calls","metadata":{"unresolvedName":"window.setTimeout"}},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:SettingsPage","to":"external:location.pathname.split(\"/\").filter","kind":"calls","metadata":{"unresolvedName":"location.pathname.split(\"/\").filter"}},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:SettingsPage","to":"external:location.pathname.split","kind":"calls","metadata":{"unresolvedName":"location.pathname.split"}},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:SettingsPage","to":"external:location.pathname.endsWith","kind":"calls","metadata":{"unresolvedName":"location.pathname.endsWith"}},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:SettingsPage","to":"external:TABS.map","kind":"calls","metadata":{"unresolvedName":"TABS.map"}},{"from":"file:/workspace/apps/web/src/pages/SettingsPage.tsx","to":"/workspace/apps/web/src/pages/SettingsPage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/SettingsPage.tsx","to":"/workspace/apps/web/src/pages/SettingsPage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:","to":"/workspace/apps/web/src/api/settings.ts:getUserConfig","kind":"calls","metadata":{"unresolvedName":"getUserConfig"}},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:","to":"symbol-name:setConfig","kind":"calls","metadata":{"unresolvedName":"setConfig"}},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"file:/workspace/apps/web/src/pages/SettingsPage.tsx","to":"/workspace/apps/web/src/pages/SettingsPage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/SettingsPage.tsx","to":"/workspace/apps/web/src/pages/SettingsPage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:","to":"symbol-name:loadConfig","kind":"calls","metadata":{"unresolvedName":"loadConfig"}},{"from":"file:/workspace/apps/web/src/pages/SettingsPage.tsx","to":"/workspace/apps/web/src/pages/SettingsPage.tsx:handleChange","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/SettingsPage.tsx","to":"/workspace/apps/web/src/pages/SettingsPage.tsx:handleChange","kind":"defines"},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:handleChange","to":"symbol-name:setConfig","kind":"calls","metadata":{"unresolvedName":"setConfig"}},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:handleChange","to":"symbol-name:setSaveStatus","kind":"calls","metadata":{"unresolvedName":"setSaveStatus"}},{"from":"file:/workspace/apps/web/src/pages/SettingsPage.tsx","to":"/workspace/apps/web/src/pages/SettingsPage.tsx:handleSave","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/SettingsPage.tsx","to":"/workspace/apps/web/src/pages/SettingsPage.tsx:handleSave","kind":"defines"},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:handleSave","to":"symbol-name:setSaveStatus","kind":"calls","metadata":{"unresolvedName":"setSaveStatus"}},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:handleSave","to":"/workspace/apps/web/src/api/settings.ts:updateUserConfig","kind":"calls","metadata":{"unresolvedName":"updateUserConfig"}},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:handleSave","to":"symbol-name:setConfig","kind":"calls","metadata":{"unresolvedName":"setConfig"}},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:handleSave","to":"external:document.documentElement.removeAttribute","kind":"calls","metadata":{"unresolvedName":"document.documentElement.removeAttribute"}},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:handleSave","to":"external:document.documentElement.setAttribute","kind":"calls","metadata":{"unresolvedName":"document.documentElement.setAttribute"}},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:handleSave","to":"external:window.setTimeout","kind":"calls","metadata":{"unresolvedName":"window.setTimeout"}},{"from":"file:/workspace/apps/web/src/pages/SettingsPage.tsx","to":"/workspace/apps/web/src/pages/SettingsPage.tsx:GeneralSettingsTab","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/SettingsPage.tsx","to":"/workspace/apps/web/src/pages/SettingsPage.tsx:GeneralSettingsTab","kind":"defines"},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:GeneralSettingsTab","to":"symbol-name:useOutletContext","kind":"calls","metadata":{"unresolvedName":"useOutletContext"}},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:GeneralSettingsTab","to":"symbol-name:handleChange","kind":"calls","metadata":{"unresolvedName":"handleChange"}},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:GeneralSettingsTab","to":"external:THEME_OPTIONS.map","kind":"calls","metadata":{"unresolvedName":"THEME_OPTIONS.map"}},{"from":"/workspace/apps/web/src/pages/SettingsPage.tsx:GeneralSettingsTab","to":"symbol-name:handleSave","kind":"calls","metadata":{"unresolvedName":"handleSave"}},{"from":"file:/workspace/apps/web/src/pages/SshKeysPage.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/SshKeysPage.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/SshKeysPage.tsx","to":"file:/workspace/apps/web/src/api/ssh-keys.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/SshKeysPage.tsx","to":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/SshKeysPage.tsx","to":"/workspace/apps/web/src/pages/SshKeysPage.tsx:SSHKeysPage","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/SshKeysPage.tsx","to":"/workspace/apps/web/src/pages/SshKeysPage.tsx:SSHKeysPage","kind":"defines"},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:SSHKeysPage","to":"symbol-name:useNavigate","kind":"calls","metadata":{"unresolvedName":"useNavigate"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:SSHKeysPage","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:SSHKeysPage","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:SSHKeysPage","to":"symbol-name:loadKeys","kind":"calls","metadata":{"unresolvedName":"loadKeys"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:SSHKeysPage","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:SSHKeysPage","to":"/workspace/apps/web/src/api/ssh-keys.ts:listSSHKeys","kind":"calls","metadata":{"unresolvedName":"listSSHKeys"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:SSHKeysPage","to":"symbol-name:setKeys","kind":"calls","metadata":{"unresolvedName":"setKeys"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:SSHKeysPage","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:SSHKeysPage","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:SSHKeysPage","to":"external:newKeyName.trim","kind":"calls","metadata":{"unresolvedName":"newKeyName.trim"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:SSHKeysPage","to":"symbol-name:setGenerating","kind":"calls","metadata":{"unresolvedName":"setGenerating"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:SSHKeysPage","to":"/workspace/apps/web/src/api/ssh-keys.ts:createSSHKey","kind":"calls","metadata":{"unresolvedName":"createSSHKey"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:SSHKeysPage","to":"symbol-name:setNewKeyName","kind":"calls","metadata":{"unresolvedName":"setNewKeyName"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:SSHKeysPage","to":"symbol-name:confirm","kind":"calls","metadata":{"unresolvedName":"confirm"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:SSHKeysPage","to":"/workspace/apps/web/src/api/ssh-keys.ts:deleteSSHKey","kind":"calls","metadata":{"unresolvedName":"deleteSSHKey"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:SSHKeysPage","to":"external:navigator.clipboard.writeText","kind":"calls","metadata":{"unresolvedName":"navigator.clipboard.writeText"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:SSHKeysPage","to":"symbol-name:navigate","kind":"calls","metadata":{"unresolvedName":"navigate"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:SSHKeysPage","to":"external:keys.map","kind":"calls","metadata":{"unresolvedName":"keys.map"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:SSHKeysPage","to":"symbol-name:handleDelete","kind":"calls","metadata":{"unresolvedName":"handleDelete"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:SSHKeysPage","to":"external:new Date(key.created_at).toLocaleDateString","kind":"calls","metadata":{"unresolvedName":"new Date(key.created_at).toLocaleDateString"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:SSHKeysPage","to":"external:key.public_key.substring","kind":"calls","metadata":{"unresolvedName":"key.public_key.substring"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:SSHKeysPage","to":"/workspace/apps/web/src/pages/SshKeysPage.tsx:copyToClipboard","kind":"calls","metadata":{"unresolvedName":"copyToClipboard"}},{"from":"file:/workspace/apps/web/src/pages/SshKeysPage.tsx","to":"/workspace/apps/web/src/pages/SshKeysPage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/SshKeysPage.tsx","to":"/workspace/apps/web/src/pages/SshKeysPage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:","to":"symbol-name:loadKeys","kind":"calls","metadata":{"unresolvedName":"loadKeys"}},{"from":"file:/workspace/apps/web/src/pages/SshKeysPage.tsx","to":"/workspace/apps/web/src/pages/SshKeysPage.tsx:loadKeys","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/SshKeysPage.tsx","to":"/workspace/apps/web/src/pages/SshKeysPage.tsx:loadKeys","kind":"defines"},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:loadKeys","to":"symbol-name:setLoading","kind":"calls","metadata":{"unresolvedName":"setLoading"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:loadKeys","to":"/workspace/apps/web/src/api/ssh-keys.ts:listSSHKeys","kind":"calls","metadata":{"unresolvedName":"listSSHKeys"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:loadKeys","to":"symbol-name:setKeys","kind":"calls","metadata":{"unresolvedName":"setKeys"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:loadKeys","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"file:/workspace/apps/web/src/pages/SshKeysPage.tsx","to":"/workspace/apps/web/src/pages/SshKeysPage.tsx:handleGenerate","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/SshKeysPage.tsx","to":"/workspace/apps/web/src/pages/SshKeysPage.tsx:handleGenerate","kind":"defines"},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:handleGenerate","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:handleGenerate","to":"external:newKeyName.trim","kind":"calls","metadata":{"unresolvedName":"newKeyName.trim"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:handleGenerate","to":"symbol-name:setGenerating","kind":"calls","metadata":{"unresolvedName":"setGenerating"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:handleGenerate","to":"/workspace/apps/web/src/api/ssh-keys.ts:createSSHKey","kind":"calls","metadata":{"unresolvedName":"createSSHKey"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:handleGenerate","to":"symbol-name:setNewKeyName","kind":"calls","metadata":{"unresolvedName":"setNewKeyName"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:handleGenerate","to":"symbol-name:loadKeys","kind":"calls","metadata":{"unresolvedName":"loadKeys"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:handleGenerate","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"file:/workspace/apps/web/src/pages/SshKeysPage.tsx","to":"/workspace/apps/web/src/pages/SshKeysPage.tsx:handleDelete","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/SshKeysPage.tsx","to":"/workspace/apps/web/src/pages/SshKeysPage.tsx:handleDelete","kind":"defines"},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:handleDelete","to":"symbol-name:confirm","kind":"calls","metadata":{"unresolvedName":"confirm"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:handleDelete","to":"/workspace/apps/web/src/api/ssh-keys.ts:deleteSSHKey","kind":"calls","metadata":{"unresolvedName":"deleteSSHKey"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:handleDelete","to":"symbol-name:loadKeys","kind":"calls","metadata":{"unresolvedName":"loadKeys"}},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:handleDelete","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"file:/workspace/apps/web/src/pages/SshKeysPage.tsx","to":"/workspace/apps/web/src/pages/SshKeysPage.tsx:copyToClipboard","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/SshKeysPage.tsx","to":"/workspace/apps/web/src/pages/SshKeysPage.tsx:copyToClipboard","kind":"defines"},{"from":"/workspace/apps/web/src/pages/SshKeysPage.tsx:copyToClipboard","to":"external:navigator.clipboard.writeText","kind":"calls","metadata":{"unresolvedName":"navigator.clipboard.writeText"}},{"from":"file:/workspace/apps/web/src/pages/TerminalPage.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/TerminalPage.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/TerminalPage.tsx","to":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/TerminalPage.tsx","to":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/TerminalPage.tsx","to":"/workspace/apps/web/src/pages/TerminalPage.tsx:TerminalPage","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/TerminalPage.tsx","to":"/workspace/apps/web/src/pages/TerminalPage.tsx:TerminalPage","kind":"defines"},{"from":"/workspace/apps/web/src/pages/TerminalPage.tsx:TerminalPage","to":"symbol-name:useParams","kind":"calls","metadata":{"unresolvedName":"useParams"}},{"from":"/workspace/apps/web/src/pages/TerminalPage.tsx:TerminalPage","to":"symbol-name:useNavigate","kind":"calls","metadata":{"unresolvedName":"useNavigate"}},{"from":"/workspace/apps/web/src/pages/TerminalPage.tsx:TerminalPage","to":"symbol-name:navigate","kind":"calls","metadata":{"unresolvedName":"navigate"}},{"from":"file:/workspace/apps/web/src/pages/ToolConfigsPage.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ToolConfigsPage.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ToolConfigsPage.tsx","to":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ToolConfigsPage.tsx","to":"file:/workspace/apps/web/src/types/tool-type.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ToolConfigsPage.tsx","to":"file:/workspace/apps/web/src/types/tool-config.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ToolConfigsPage.tsx","to":"file:/workspace/apps/web/src/api/tool-types.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ToolConfigsPage.tsx","to":"file:/workspace/apps/web/src/api/tool-configs.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ToolConfigsPage.tsx","to":"file:/workspace/apps/web/src/components/features/tool-configs","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ToolConfigsPage.tsx","to":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:ToolConfigsPage","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolConfigsPage.tsx","to":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:ToolConfigsPage","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:ToolConfigsPage","to":"symbol-name:useNavigate","kind":"calls","metadata":{"unresolvedName":"useNavigate"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:ToolConfigsPage","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:ToolConfigsPage","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:ToolConfigsPage","to":"external:Promise.all","kind":"calls","metadata":{"unresolvedName":"Promise.all"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:ToolConfigsPage","to":"/workspace/apps/web/src/api/tool-types.ts:listToolTypes","kind":"calls","metadata":{"unresolvedName":"listToolTypes"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:ToolConfigsPage","to":"/workspace/apps/web/src/api/tool-configs.ts:listToolConfigs","kind":"calls","metadata":{"unresolvedName":"listToolConfigs"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:ToolConfigsPage","to":"symbol-name:setToolTypes","kind":"calls","metadata":{"unresolvedName":"setToolTypes"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:ToolConfigsPage","to":"symbol-name:setConfigs","kind":"calls","metadata":{"unresolvedName":"setConfigs"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:ToolConfigsPage","to":"symbol-name:setSelectedToolType","kind":"calls","metadata":{"unresolvedName":"setSelectedToolType"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:ToolConfigsPage","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:ToolConfigsPage","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:ToolConfigsPage","to":"symbol-name:loadData","kind":"calls","metadata":{"unresolvedName":"loadData"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:ToolConfigsPage","to":"/workspace/apps/web/src/api/tool-configs.ts:updateToolConfig","kind":"calls","metadata":{"unresolvedName":"updateToolConfig"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:ToolConfigsPage","to":"/workspace/apps/web/src/api/tool-configs.ts:createToolConfig","kind":"calls","metadata":{"unresolvedName":"createToolConfig"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:ToolConfigsPage","to":"symbol-name:setShowForm","kind":"calls","metadata":{"unresolvedName":"setShowForm"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:ToolConfigsPage","to":"symbol-name:setEditingConfig","kind":"calls","metadata":{"unresolvedName":"setEditingConfig"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:ToolConfigsPage","to":"external:window.confirm","kind":"calls","metadata":{"unresolvedName":"window.confirm"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:ToolConfigsPage","to":"/workspace/apps/web/src/api/tool-configs.ts:deleteToolConfig","kind":"calls","metadata":{"unresolvedName":"deleteToolConfig"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:ToolConfigsPage","to":"external:toolTypes.find","kind":"calls","metadata":{"unresolvedName":"toolTypes.find"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:ToolConfigsPage","to":"external:configs.filter","kind":"calls","metadata":{"unresolvedName":"configs.filter"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:ToolConfigsPage","to":"symbol-name:navigate","kind":"calls","metadata":{"unresolvedName":"navigate"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:ToolConfigsPage","to":"external:toolTypes.map","kind":"calls","metadata":{"unresolvedName":"toolTypes.map"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:ToolConfigsPage","to":"external:selectedTool.interfaces?.join","kind":"calls","metadata":{"unresolvedName":"selectedTool.interfaces?.join"}},{"from":"file:/workspace/apps/web/src/pages/ToolConfigsPage.tsx","to":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolConfigsPage.tsx","to":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:","to":"external:Promise.all","kind":"calls","metadata":{"unresolvedName":"Promise.all"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:","to":"/workspace/apps/web/src/api/tool-types.ts:listToolTypes","kind":"calls","metadata":{"unresolvedName":"listToolTypes"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:","to":"/workspace/apps/web/src/api/tool-configs.ts:listToolConfigs","kind":"calls","metadata":{"unresolvedName":"listToolConfigs"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:","to":"symbol-name:setToolTypes","kind":"calls","metadata":{"unresolvedName":"setToolTypes"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:","to":"symbol-name:setConfigs","kind":"calls","metadata":{"unresolvedName":"setConfigs"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:","to":"symbol-name:setSelectedToolType","kind":"calls","metadata":{"unresolvedName":"setSelectedToolType"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"file:/workspace/apps/web/src/pages/ToolConfigsPage.tsx","to":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolConfigsPage.tsx","to":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:","to":"symbol-name:loadData","kind":"calls","metadata":{"unresolvedName":"loadData"}},{"from":"file:/workspace/apps/web/src/pages/ToolConfigsPage.tsx","to":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:handleSubmit","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolConfigsPage.tsx","to":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:handleSubmit","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:handleSubmit","to":"/workspace/apps/web/src/api/tool-configs.ts:updateToolConfig","kind":"calls","metadata":{"unresolvedName":"updateToolConfig"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:handleSubmit","to":"/workspace/apps/web/src/api/tool-configs.ts:createToolConfig","kind":"calls","metadata":{"unresolvedName":"createToolConfig"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:handleSubmit","to":"symbol-name:setShowForm","kind":"calls","metadata":{"unresolvedName":"setShowForm"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:handleSubmit","to":"symbol-name:setEditingConfig","kind":"calls","metadata":{"unresolvedName":"setEditingConfig"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:handleSubmit","to":"symbol-name:loadData","kind":"calls","metadata":{"unresolvedName":"loadData"}},{"from":"file:/workspace/apps/web/src/pages/ToolConfigsPage.tsx","to":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:handleEdit","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolConfigsPage.tsx","to":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:handleEdit","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:handleEdit","to":"symbol-name:setEditingConfig","kind":"calls","metadata":{"unresolvedName":"setEditingConfig"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:handleEdit","to":"symbol-name:setSelectedToolType","kind":"calls","metadata":{"unresolvedName":"setSelectedToolType"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:handleEdit","to":"symbol-name:setShowForm","kind":"calls","metadata":{"unresolvedName":"setShowForm"}},{"from":"file:/workspace/apps/web/src/pages/ToolConfigsPage.tsx","to":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:handleDelete","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolConfigsPage.tsx","to":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:handleDelete","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:handleDelete","to":"external:window.confirm","kind":"calls","metadata":{"unresolvedName":"window.confirm"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:handleDelete","to":"/workspace/apps/web/src/api/tool-configs.ts:deleteToolConfig","kind":"calls","metadata":{"unresolvedName":"deleteToolConfig"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:handleDelete","to":"symbol-name:loadData","kind":"calls","metadata":{"unresolvedName":"loadData"}},{"from":"file:/workspace/apps/web/src/pages/ToolConfigsPage.tsx","to":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolConfigsPage.tsx","to":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:","to":"symbol-name:setSelectedToolType","kind":"calls","metadata":{"unresolvedName":"setSelectedToolType"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:","to":"symbol-name:setShowForm","kind":"calls","metadata":{"unresolvedName":"setShowForm"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:","to":"symbol-name:setEditingConfig","kind":"calls","metadata":{"unresolvedName":"setEditingConfig"}},{"from":"file:/workspace/apps/web/src/pages/ToolConfigsPage.tsx","to":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolConfigsPage.tsx","to":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:","to":"symbol-name:setShowForm","kind":"calls","metadata":{"unresolvedName":"setShowForm"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:","to":"symbol-name:setEditingConfig","kind":"calls","metadata":{"unresolvedName":"setEditingConfig"}},{"from":"file:/workspace/apps/web/src/pages/ToolConfigsPage.tsx","to":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolConfigsPage.tsx","to":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:","to":"symbol-name:setShowForm","kind":"calls","metadata":{"unresolvedName":"setShowForm"}},{"from":"/workspace/apps/web/src/pages/ToolConfigsPage.tsx:","to":"symbol-name:setEditingConfig","kind":"calls","metadata":{"unresolvedName":"setEditingConfig"}},{"from":"file:/workspace/apps/web/src/pages/ToolTypesPage.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ToolTypesPage.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ToolTypesPage.tsx","to":"file:/workspace/apps/web/src/types/tool-type.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ToolTypesPage.tsx","to":"file:/workspace/apps/web/src/api/tool-types.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ToolTypesPage.tsx","to":"file:/workspace/apps/web/src/components/ui/Icon.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ToolTypesPage.tsx","to":"file:/workspace/apps/web/src/components/features/tool-types","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ToolTypesPage.tsx","to":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:ToolTypesPage","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolTypesPage.tsx","to":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:ToolTypesPage","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:ToolTypesPage","to":"symbol-name:useNavigate","kind":"calls","metadata":{"unresolvedName":"useNavigate"}},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:ToolTypesPage","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:ToolTypesPage","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:ToolTypesPage","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:ToolTypesPage","to":"/workspace/apps/web/src/api/tool-types.ts:listToolTypes","kind":"calls","metadata":{"unresolvedName":"listToolTypes"}},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:ToolTypesPage","to":"symbol-name:setToolTypes","kind":"calls","metadata":{"unresolvedName":"setToolTypes"}},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:ToolTypesPage","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:ToolTypesPage","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:loadToolTypes","kind":"calls","metadata":{"unresolvedName":"loadToolTypes"}},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:ToolTypesPage","to":"symbol-name:setEditingToolType","kind":"calls","metadata":{"unresolvedName":"setEditingToolType"}},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:ToolTypesPage","to":"symbol-name:setDialogMode","kind":"calls","metadata":{"unresolvedName":"setDialogMode"}},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:ToolTypesPage","to":"/workspace/apps/web/src/api/tool-types.ts:createToolType","kind":"calls","metadata":{"unresolvedName":"createToolType"}},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:ToolTypesPage","to":"/workspace/apps/web/src/api/tool-types.ts:updateToolType","kind":"calls","metadata":{"unresolvedName":"updateToolType"}},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:ToolTypesPage","to":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:closeDialog","kind":"calls","metadata":{"unresolvedName":"closeDialog"}},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:ToolTypesPage","to":"/workspace/apps/web/src/api/tool-types.ts:deleteToolType","kind":"calls","metadata":{"unresolvedName":"deleteToolType"}},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:ToolTypesPage","to":"symbol-name:setDeleteConfirmId","kind":"calls","metadata":{"unresolvedName":"setDeleteConfirmId"}},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:ToolTypesPage","to":"symbol-name:alert","kind":"calls","metadata":{"unresolvedName":"alert"}},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:ToolTypesPage","to":"symbol-name:navigate","kind":"calls","metadata":{"unresolvedName":"navigate"}},{"from":"file:/workspace/apps/web/src/pages/ToolTypesPage.tsx","to":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolTypesPage.tsx","to":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:","to":"/workspace/apps/web/src/api/tool-types.ts:listToolTypes","kind":"calls","metadata":{"unresolvedName":"listToolTypes"}},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:","to":"symbol-name:setToolTypes","kind":"calls","metadata":{"unresolvedName":"setToolTypes"}},{"from":"file:/workspace/apps/web/src/pages/ToolTypesPage.tsx","to":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolTypesPage.tsx","to":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:loadToolTypes","kind":"calls","metadata":{"unresolvedName":"loadToolTypes"}},{"from":"file:/workspace/apps/web/src/pages/ToolTypesPage.tsx","to":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:openCreate","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolTypesPage.tsx","to":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:openCreate","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:openCreate","to":"symbol-name:setEditingToolType","kind":"calls","metadata":{"unresolvedName":"setEditingToolType"}},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:openCreate","to":"symbol-name:setDialogMode","kind":"calls","metadata":{"unresolvedName":"setDialogMode"}},{"from":"file:/workspace/apps/web/src/pages/ToolTypesPage.tsx","to":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:openEdit","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolTypesPage.tsx","to":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:openEdit","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:openEdit","to":"symbol-name:setEditingToolType","kind":"calls","metadata":{"unresolvedName":"setEditingToolType"}},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:openEdit","to":"symbol-name:setDialogMode","kind":"calls","metadata":{"unresolvedName":"setDialogMode"}},{"from":"file:/workspace/apps/web/src/pages/ToolTypesPage.tsx","to":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:closeDialog","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolTypesPage.tsx","to":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:closeDialog","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:closeDialog","to":"symbol-name:setDialogMode","kind":"calls","metadata":{"unresolvedName":"setDialogMode"}},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:closeDialog","to":"symbol-name:setEditingToolType","kind":"calls","metadata":{"unresolvedName":"setEditingToolType"}},{"from":"file:/workspace/apps/web/src/pages/ToolTypesPage.tsx","to":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:handleSubmit","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolTypesPage.tsx","to":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:handleSubmit","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:handleSubmit","to":"/workspace/apps/web/src/api/tool-types.ts:createToolType","kind":"calls","metadata":{"unresolvedName":"createToolType"}},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:handleSubmit","to":"/workspace/apps/web/src/api/tool-types.ts:updateToolType","kind":"calls","metadata":{"unresolvedName":"updateToolType"}},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:handleSubmit","to":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:closeDialog","kind":"calls","metadata":{"unresolvedName":"closeDialog"}},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:handleSubmit","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:loadToolTypes","kind":"calls","metadata":{"unresolvedName":"loadToolTypes"}},{"from":"file:/workspace/apps/web/src/pages/ToolTypesPage.tsx","to":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:handleDelete","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolTypesPage.tsx","to":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:handleDelete","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:handleDelete","to":"/workspace/apps/web/src/api/tool-types.ts:deleteToolType","kind":"calls","metadata":{"unresolvedName":"deleteToolType"}},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:handleDelete","to":"symbol-name:setDeleteConfirmId","kind":"calls","metadata":{"unresolvedName":"setDeleteConfirmId"}},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:handleDelete","to":"/workspace/apps/web/src/pages/SessionsPage.tsx:loadToolTypes","kind":"calls","metadata":{"unresolvedName":"loadToolTypes"}},{"from":"/workspace/apps/web/src/pages/ToolTypesPage.tsx:handleDelete","to":"symbol-name:alert","kind":"calls","metadata":{"unresolvedName":"alert"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"external:@testing-library/react","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"external:vitest","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"file:/workspace/apps/web/src/api/tool-types.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"file:/workspace/apps/web/src/api/tool-configs.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"file:/workspace/apps/web/src/api/config-folders.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:cleanup","kind":"calls","metadata":{"unresolvedName":"cleanup"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.restoreAllMocks","kind":"calls","metadata":{"unresolvedName":"vi.restoreAllMocks"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:it","kind":"calls","metadata":{"unresolvedName":"it"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolTypesApi, \"listToolTypes\").mockImplementation","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"listToolTypes\").mockImplementation"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn","kind":"calls","metadata":{"unresolvedName":"vi.spyOn"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockImplementation","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockImplementation"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(configFoldersApi, \"listConfigFolders\").mockImplementation","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(configFoldersApi, \"listConfigFolders\").mockImplementation"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(/loading/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/loading/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"Custom Tool\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Custom Tool\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"OPENAI_API_KEY\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"OPENAI_API_KEY\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"advanced-config\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"advanced-config\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"my-dotfiles\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"my-dotfiles\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"project-configs\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"project-configs\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByLabelText(\"Name *\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByLabelText(\"Name *\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByLabelText","kind":"calls","metadata":{"unresolvedName":"screen.getByLabelText"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByLabelText(\"Display Name *\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByLabelText(\"Display Name *\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolTypesApi, \"createToolType\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"createToolType\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:fireEvent.change","kind":"calls","metadata":{"unresolvedName":"fireEvent.change"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(createMock).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(createMock).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect.objectContaining","kind":"calls","metadata":{"unresolvedName":"expect.objectContaining"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(/readiness probe command/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/readiness probe command/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(/timeout/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/timeout/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(/interval/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/interval/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByLabelText(/key/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByLabelText(/key/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByLabelText(/value/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByLabelText(/value/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolConfigsApi, \"createToolConfig\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolConfigsApi, \"createToolConfig\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(configsListMock).toHaveBeenCalledTimes","kind":"calls","metadata":{"unresolvedName":"expect(configsListMock).toHaveBeenCalledTimes"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByLabelText(\"Mount Path *\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByLabelText(\"Mount Path *\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(configFoldersApi, \"createConfigFolder\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(configFoldersApi, \"createConfigFolder\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(foldersListMock).toHaveBeenCalledTimes","kind":"calls","metadata":{"unresolvedName":"expect(foldersListMock).toHaveBeenCalledTimes"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"Active\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Active\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolTypesApi, \"listToolTypes\").mockRejectedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"listToolTypes\").mockRejectedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockRejectedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockRejectedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(configFoldersApi, \"listConfigFolders\").mockRejectedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(configFoldersApi, \"listConfigFolders\").mockRejectedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(/failed to load/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/failed to load/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByRole(\"button\", { name: /retry/i })).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByRole(\"button\", { name: /retry/i })).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolTypesApi, \"listToolTypes\")\n .mockRejectedValueOnce","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"listToolTypes\")\n .mockRejectedValueOnce"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolConfigsApi, \"listToolConfigs\")\n .mockRejectedValueOnce","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolConfigsApi, \"listToolConfigs\")\n .mockRejectedValueOnce"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(configFoldersApi, \"listConfigFolders\")\n .mockRejectedValueOnce","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(configFoldersApi, \"listConfigFolders\")\n .mockRejectedValueOnce"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(listMock).toHaveBeenCalledTimes","kind":"calls","metadata":{"unresolvedName":"expect(listMock).toHaveBeenCalledTimes"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolTypesApi, \"deleteToolType\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"deleteToolType\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText(\"Custom Tool\").closest","kind":"calls","metadata":{"unresolvedName":"screen.getByText(\"Custom Tool\").closest"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:within(customToolCard as HTMLElement).queryByRole","kind":"calls","metadata":{"unresolvedName":"within(customToolCard as HTMLElement).queryByRole"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:within","kind":"calls","metadata":{"unresolvedName":"within"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(window, \"confirm\").mockReturnValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(window, \"confirm\").mockReturnValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(deleteMock).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(deleteMock).toHaveBeenCalledWith"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolTypesApi, \"listToolTypes\").mockImplementation","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"listToolTypes\").mockImplementation"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn","kind":"calls","metadata":{"unresolvedName":"vi.spyOn"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockImplementation","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockImplementation"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(configFoldersApi, \"listConfigFolders\").mockImplementation","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(configFoldersApi, \"listConfigFolders\").mockImplementation"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(/loading/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/loading/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn","kind":"calls","metadata":{"unresolvedName":"vi.spyOn"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"Custom Tool\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Custom Tool\")).toBeInTheDocument"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn","kind":"calls","metadata":{"unresolvedName":"vi.spyOn"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"OPENAI_API_KEY\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"OPENAI_API_KEY\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"advanced-config\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"advanced-config\")).toBeInTheDocument"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"OPENAI_API_KEY\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"OPENAI_API_KEY\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn","kind":"calls","metadata":{"unresolvedName":"vi.spyOn"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"my-dotfiles\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"my-dotfiles\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"project-configs\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"project-configs\")).toBeInTheDocument"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"my-dotfiles\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"my-dotfiles\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn","kind":"calls","metadata":{"unresolvedName":"vi.spyOn"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByLabelText(\"Name *\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByLabelText(\"Name *\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByLabelText","kind":"calls","metadata":{"unresolvedName":"screen.getByLabelText"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByLabelText(\"Display Name *\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByLabelText(\"Display Name *\")).toBeInTheDocument"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn","kind":"calls","metadata":{"unresolvedName":"vi.spyOn"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolTypesApi, \"createToolType\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"createToolType\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:fireEvent.change","kind":"calls","metadata":{"unresolvedName":"fireEvent.change"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByLabelText","kind":"calls","metadata":{"unresolvedName":"screen.getByLabelText"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(createMock).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(createMock).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect.objectContaining","kind":"calls","metadata":{"unresolvedName":"expect.objectContaining"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(createMock).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(createMock).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect.objectContaining","kind":"calls","metadata":{"unresolvedName":"expect.objectContaining"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn","kind":"calls","metadata":{"unresolvedName":"vi.spyOn"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolTypesApi, \"createToolType\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"createToolType\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:fireEvent.change","kind":"calls","metadata":{"unresolvedName":"fireEvent.change"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByLabelText","kind":"calls","metadata":{"unresolvedName":"screen.getByLabelText"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(createMock).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(createMock).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect.objectContaining","kind":"calls","metadata":{"unresolvedName":"expect.objectContaining"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(createMock).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(createMock).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect.objectContaining","kind":"calls","metadata":{"unresolvedName":"expect.objectContaining"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn","kind":"calls","metadata":{"unresolvedName":"vi.spyOn"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(/readiness probe command/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/readiness probe command/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(/timeout/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/timeout/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(/interval/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/interval/i)).toBeInTheDocument"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn","kind":"calls","metadata":{"unresolvedName":"vi.spyOn"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"OPENAI_API_KEY\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"OPENAI_API_KEY\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByLabelText(/key/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByLabelText(/key/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByLabelText","kind":"calls","metadata":{"unresolvedName":"screen.getByLabelText"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByLabelText(/value/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByLabelText(/value/i)).toBeInTheDocument"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"OPENAI_API_KEY\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"OPENAI_API_KEY\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn","kind":"calls","metadata":{"unresolvedName":"vi.spyOn"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolConfigsApi, \"createToolConfig\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolConfigsApi, \"createToolConfig\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"OPENAI_API_KEY\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"OPENAI_API_KEY\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:fireEvent.change","kind":"calls","metadata":{"unresolvedName":"fireEvent.change"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByLabelText","kind":"calls","metadata":{"unresolvedName":"screen.getByLabelText"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(createMock).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(createMock).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect.objectContaining","kind":"calls","metadata":{"unresolvedName":"expect.objectContaining"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(configsListMock).toHaveBeenCalledTimes","kind":"calls","metadata":{"unresolvedName":"expect(configsListMock).toHaveBeenCalledTimes"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"OPENAI_API_KEY\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"OPENAI_API_KEY\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(createMock).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(createMock).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect.objectContaining","kind":"calls","metadata":{"unresolvedName":"expect.objectContaining"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn","kind":"calls","metadata":{"unresolvedName":"vi.spyOn"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"my-dotfiles\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"my-dotfiles\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByLabelText(\"Name *\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByLabelText(\"Name *\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByLabelText","kind":"calls","metadata":{"unresolvedName":"screen.getByLabelText"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByLabelText(\"Mount Path *\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByLabelText(\"Mount Path *\")).toBeInTheDocument"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"my-dotfiles\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"my-dotfiles\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn","kind":"calls","metadata":{"unresolvedName":"vi.spyOn"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(configFoldersApi, \"createConfigFolder\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(configFoldersApi, \"createConfigFolder\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"my-dotfiles\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"my-dotfiles\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:fireEvent.change","kind":"calls","metadata":{"unresolvedName":"fireEvent.change"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByLabelText","kind":"calls","metadata":{"unresolvedName":"screen.getByLabelText"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(createMock).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(createMock).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect.objectContaining","kind":"calls","metadata":{"unresolvedName":"expect.objectContaining"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(foldersListMock).toHaveBeenCalledTimes","kind":"calls","metadata":{"unresolvedName":"expect(foldersListMock).toHaveBeenCalledTimes"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"my-dotfiles\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"my-dotfiles\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(createMock).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(createMock).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect.objectContaining","kind":"calls","metadata":{"unresolvedName":"expect.objectContaining"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn","kind":"calls","metadata":{"unresolvedName":"vi.spyOn"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"my-dotfiles\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"my-dotfiles\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"Active\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Active\")).toBeInTheDocument"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"my-dotfiles\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"my-dotfiles\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolTypesApi, \"listToolTypes\").mockRejectedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"listToolTypes\").mockRejectedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn","kind":"calls","metadata":{"unresolvedName":"vi.spyOn"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockRejectedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockRejectedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(configFoldersApi, \"listConfigFolders\").mockRejectedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(configFoldersApi, \"listConfigFolders\").mockRejectedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(/failed to load/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/failed to load/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByRole(\"button\", { name: /retry/i })).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByRole(\"button\", { name: /retry/i })).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(/failed to load/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/failed to load/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolTypesApi, \"listToolTypes\")\n .mockRejectedValueOnce","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"listToolTypes\")\n .mockRejectedValueOnce"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn","kind":"calls","metadata":{"unresolvedName":"vi.spyOn"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolConfigsApi, \"listToolConfigs\")\n .mockRejectedValueOnce","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolConfigsApi, \"listToolConfigs\")\n .mockRejectedValueOnce"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(configFoldersApi, \"listConfigFolders\")\n .mockRejectedValueOnce","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(configFoldersApi, \"listConfigFolders\")\n .mockRejectedValueOnce"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(/failed to load/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/failed to load/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByRole","kind":"calls","metadata":{"unresolvedName":"screen.getByRole"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(listMock).toHaveBeenCalledTimes","kind":"calls","metadata":{"unresolvedName":"expect(listMock).toHaveBeenCalledTimes"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(/failed to load/i)).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(/failed to load/i)).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"VS Code Server\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"listToolTypes\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn","kind":"calls","metadata":{"unresolvedName":"vi.spyOn"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolTypesApi, \"deleteToolType\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolTypesApi, \"deleteToolType\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(toolConfigsApi, \"listToolConfigs\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(configFoldersApi, \"listConfigFolders\").mockResolvedValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:render","kind":"calls","metadata":{"unresolvedName":"render"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:waitFor","kind":"calls","metadata":{"unresolvedName":"waitFor"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"Custom Tool\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Custom Tool\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText(\"Custom Tool\").closest","kind":"calls","metadata":{"unresolvedName":"screen.getByText(\"Custom Tool\").closest"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:within(customToolCard as HTMLElement).queryByRole","kind":"calls","metadata":{"unresolvedName":"within(customToolCard as HTMLElement).queryByRole"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:within","kind":"calls","metadata":{"unresolvedName":"within"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:vi.spyOn(window, \"confirm\").mockReturnValue","kind":"calls","metadata":{"unresolvedName":"vi.spyOn(window, \"confirm\").mockReturnValue"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:fireEvent.click","kind":"calls","metadata":{"unresolvedName":"fireEvent.click"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(deleteMock).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(deleteMock).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(listMock).toHaveBeenCalledTimes","kind":"calls","metadata":{"unresolvedName":"expect(listMock).toHaveBeenCalledTimes"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(screen.getByText(\"Custom Tool\")).toBeInTheDocument","kind":"calls","metadata":{"unresolvedName":"expect(screen.getByText(\"Custom Tool\")).toBeInTheDocument"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:screen.getByText","kind":"calls","metadata":{"unresolvedName":"screen.getByText"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"external:expect(deleteMock).toHaveBeenCalledWith","kind":"calls","metadata":{"unresolvedName":"expect(deleteMock).toHaveBeenCalledWith"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.test.tsx:","to":"symbol-name:expect","kind":"calls","metadata":{"unresolvedName":"expect"}},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.tsx","to":"file:/workspace/apps/web/src/components/ui","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.tsx","to":"file:/workspace/apps/web/src/components/features/tool-workshop","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.tsx:ToolWorkshopPage","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.tsx","to":"/workspace/apps/web/src/pages/ToolWorkshopPage.tsx:ToolWorkshopPage","kind":"defines"},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.tsx:ToolWorkshopPage","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.tsx:ToolWorkshopPage","to":"external:([\"types\", \"configs\", \"folders\"] as Tab[]).map","kind":"calls","metadata":{"unresolvedName":"([\"types\", \"configs\", \"folders\"] as Tab[]).map"}},{"from":"/workspace/apps/web/src/pages/ToolWorkshopPage.tsx:ToolWorkshopPage","to":"symbol-name:setActiveTab","kind":"calls","metadata":{"unresolvedName":"setActiveTab"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"file:/workspace/apps/web/src/components/data-states.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"external:@/components/ui/Icon","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"file:/workspace/apps/web/src/hooks/use-mobile-viewport.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"file:/workspace/apps/web/src/utils/errors.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"file:/workspace/apps/web/src/components/mobile-list-view.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"file:/workspace/apps/web/src/components/mobile-detail-view.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"file:/workspace/apps/web/src/components/mobile-edit-view.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"file:/workspace/apps/web/src/components/mobile-fab.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"file:/workspace/apps/web/src/api/config_profiles.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"file:/workspace/apps/web/src/api/projects.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"file:/workspace/apps/web/src/types","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"external:@/api/tool-types","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"file:/workspace/apps/web/src/components/git-mount-editor.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"/workspace/apps/web/src/hooks/use-mobile-viewport.ts:useMobileViewport","kind":"calls","metadata":{"unresolvedName":"useMobileViewport"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:profiles.find","kind":"calls","metadata":{"unresolvedName":"profiles.find"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:Promise.all","kind":"calls","metadata":{"unresolvedName":"Promise.all"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"/workspace/apps/web/src/api/config_profiles.ts:listConfigProfiles","kind":"calls","metadata":{"unresolvedName":"listConfigProfiles"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"/workspace/apps/web/src/api/projects.ts:listProjects","kind":"calls","metadata":{"unresolvedName":"listProjects"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"/workspace/apps/web/src/api/tool-types.ts:listToolTypes","kind":"calls","metadata":{"unresolvedName":"listToolTypes"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"symbol-name:setProfiles","kind":"calls","metadata":{"unresolvedName":"setProfiles"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"symbol-name:setProjects","kind":"calls","metadata":{"unresolvedName":"setProjects"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"symbol-name:setToolTypes","kind":"calls","metadata":{"unresolvedName":"setToolTypes"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"symbol-name:loadData","kind":"calls","metadata":{"unresolvedName":"loadData"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"symbol-name:setFormData","kind":"calls","metadata":{"unresolvedName":"setFormData"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"symbol-name:setIncludedProfileIds","kind":"calls","metadata":{"unresolvedName":"setIncludedProfileIds"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"symbol-name:setSaveStatus","kind":"calls","metadata":{"unresolvedName":"setSaveStatus"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"symbol-name:setPreviewData","kind":"calls","metadata":{"unresolvedName":"setPreviewData"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:profile.includes.map","kind":"calls","metadata":{"unresolvedName":"profile.includes.map"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"symbol-name:setSelectedProfileId","kind":"calls","metadata":{"unresolvedName":"setSelectedProfileId"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"symbol-name:setIsCreating","kind":"calls","metadata":{"unresolvedName":"setIsCreating"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"/workspace/apps/web/src/pages/config-profiles.tsx:populateForm","kind":"calls","metadata":{"unresolvedName":"populateForm"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"symbol-name:resetForm","kind":"calls","metadata":{"unresolvedName":"resetForm"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:visited.has","kind":"calls","metadata":{"unresolvedName":"visited.has"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"symbol-name:getIncludedProfile","kind":"calls","metadata":{"unresolvedName":"getIncludedProfile"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:nextVisited.add","kind":"calls","metadata":{"unresolvedName":"nextVisited.add"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"/workspace/apps/web/src/pages/config-profiles.tsx:wouldCreateCycle","kind":"calls","metadata":{"unresolvedName":"wouldCreateCycle"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:profiles.filter","kind":"calls","metadata":{"unresolvedName":"profiles.filter"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:includedProfileIds.includes","kind":"calls","metadata":{"unresolvedName":"includedProfileIds.includes"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:prev.filter","kind":"calls","metadata":{"unresolvedName":"prev.filter"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:e.dataTransfer.setData","kind":"calls","metadata":{"unresolvedName":"e.dataTransfer.setData"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"symbol-name:String","kind":"calls","metadata":{"unresolvedName":"String"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"symbol-name:setDragOverIndex","kind":"calls","metadata":{"unresolvedName":"setDragOverIndex"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"symbol-name:Number","kind":"calls","metadata":{"unresolvedName":"Number"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:e.dataTransfer.getData","kind":"calls","metadata":{"unresolvedName":"e.dataTransfer.getData"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:newOrder.splice","kind":"calls","metadata":{"unresolvedName":"newOrder.splice"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:formData.name?.trim","kind":"calls","metadata":{"unresolvedName":"formData.name?.trim"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"/workspace/apps/web/src/api/config_profiles.ts:createConfigProfile","kind":"calls","metadata":{"unresolvedName":"createConfigProfile"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"/workspace/apps/web/src/api/config_profiles.ts:updateProfileIncludes","kind":"calls","metadata":{"unresolvedName":"updateProfileIncludes"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:(await listConfigProfiles()).find","kind":"calls","metadata":{"unresolvedName":"(await listConfigProfiles()).find"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"/workspace/apps/web/src/api/config_profiles.ts:updateConfigProfile","kind":"calls","metadata":{"unresolvedName":"updateConfigProfile"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"/workspace/apps/web/src/utils/errors.ts:extractErrorMessage","kind":"calls","metadata":{"unresolvedName":"extractErrorMessage"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:window.confirm","kind":"calls","metadata":{"unresolvedName":"window.confirm"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"/workspace/apps/web/src/api/config_profiles.ts:deleteConfigProfile","kind":"calls","metadata":{"unresolvedName":"deleteConfigProfile"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"symbol-name:alert","kind":"calls","metadata":{"unresolvedName":"alert"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"symbol-name:setPreviewingId","kind":"calls","metadata":{"unresolvedName":"setPreviewingId"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"/workspace/apps/web/src/api/config_profiles.ts:previewConfigProfile","kind":"calls","metadata":{"unresolvedName":"previewConfigProfile"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:mounts.splice","kind":"calls","metadata":{"unresolvedName":"mounts.splice"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:profiles.map","kind":"calls","metadata":{"unresolvedName":"profiles.map"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"/workspace/apps/web/src/pages/config-profiles.tsx:getScopeLabel","kind":"calls","metadata":{"unresolvedName":"getScopeLabel"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"symbol-name:setMobileView","kind":"calls","metadata":{"unresolvedName":"setMobileView"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"symbol-name:handleDelete","kind":"calls","metadata":{"unresolvedName":"handleDelete"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:Object.keys","kind":"calls","metadata":{"unresolvedName":"Object.keys"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:Object.entries(selectedProfile.env_vars)\n\t\t\t\t\t\t\t\t\t.map","kind":"calls","metadata":{"unresolvedName":"Object.entries(selectedProfile.env_vars)\n\t\t\t\t\t\t\t\t\t.map"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:Object.entries","kind":"calls","metadata":{"unresolvedName":"Object.entries"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:selectedProfile.mounts\n\t\t\t\t\t\t\t\t\t.map","kind":"calls","metadata":{"unresolvedName":"selectedProfile.mounts\n\t\t\t\t\t\t\t\t\t.map"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"symbol-name:handleSubmit","kind":"calls","metadata":{"unresolvedName":"handleSubmit"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"/workspace/apps/web/src/pages/config-profiles.tsx:updateFormField","kind":"calls","metadata":{"unresolvedName":"updateFormField"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:projects.map","kind":"calls","metadata":{"unresolvedName":"projects.map"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:toolTypes.map","kind":"calls","metadata":{"unresolvedName":"toolTypes.map"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:Object.entries(formData.env_vars || {}).map","kind":"calls","metadata":{"unresolvedName":"Object.entries(formData.env_vars || {}).map"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:(formData.mounts || []).map","kind":"calls","metadata":{"unresolvedName":"(formData.mounts || []).map"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:(formData.mounts || []).filter","kind":"calls","metadata":{"unresolvedName":"(formData.mounts || []).filter"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"/workspace/apps/web/src/pages/config-profiles.tsx:handleSelectProfile","kind":"calls","metadata":{"unresolvedName":"handleSelectProfile"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:e.stopPropagation","kind":"calls","metadata":{"unresolvedName":"e.stopPropagation"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:projects.find","kind":"calls","metadata":{"unresolvedName":"projects.find"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:toolTypes.find","kind":"calls","metadata":{"unresolvedName":"toolTypes.find"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"symbol-name:handlePreview","kind":"calls","metadata":{"unresolvedName":"handlePreview"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:includedProfileIds.map","kind":"calls","metadata":{"unresolvedName":"includedProfileIds.map"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDragStart","kind":"calls","metadata":{"unresolvedName":"handleDragStart"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDragOver","kind":"calls","metadata":{"unresolvedName":"handleDragOver"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDrop","kind":"calls","metadata":{"unresolvedName":"handleDrop"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"/workspace/apps/web/src/pages/config-profiles.tsx:removeInclude","kind":"calls","metadata":{"unresolvedName":"removeInclude"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"/workspace/apps/web/src/pages/config-profiles.tsx:availableProfilesForInclude","kind":"calls","metadata":{"unresolvedName":"availableProfilesForInclude"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"/workspace/apps/web/src/pages/config-profiles.tsx:addInclude","kind":"calls","metadata":{"unresolvedName":"addInclude"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:availableProfilesForInclude().map","kind":"calls","metadata":{"unresolvedName":"availableProfilesForInclude().map"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"symbol-name:updateEnvVar","kind":"calls","metadata":{"unresolvedName":"updateEnvVar"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"/workspace/apps/web/src/pages/config-profiles.tsx:removeEnvVar","kind":"calls","metadata":{"unresolvedName":"removeEnvVar"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:JSON.stringify","kind":"calls","metadata":{"unresolvedName":"JSON.stringify"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:JSON.parse","kind":"calls","metadata":{"unresolvedName":"JSON.parse"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:Object.entries(formData.files || {}).map","kind":"calls","metadata":{"unresolvedName":"Object.entries(formData.files || {}).map"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"/workspace/apps/web/src/pages/config-profiles.tsx:updateFile","kind":"calls","metadata":{"unresolvedName":"updateFile"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"/workspace/apps/web/src/pages/config-profiles.tsx:removeFile","kind":"calls","metadata":{"unresolvedName":"removeFile"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"symbol-name:updateMount","kind":"calls","metadata":{"unresolvedName":"updateMount"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"/workspace/apps/web/src/pages/config-profiles.tsx:removeMount","kind":"calls","metadata":{"unresolvedName":"removeMount"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"external:Object.entries(mount.files).map","kind":"calls","metadata":{"unresolvedName":"Object.entries(mount.files).map"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"/workspace/apps/web/src/pages/config-profiles.tsx:updateMountFile","kind":"calls","metadata":{"unresolvedName":"updateMountFile"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"/workspace/apps/web/src/pages/config-profiles.tsx:removeMountFile","kind":"calls","metadata":{"unresolvedName":"removeMountFile"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:ConfigProfilesPage","to":"/workspace/apps/web/src/pages/config-profiles.tsx:addMountFile","kind":"calls","metadata":{"unresolvedName":"addMountFile"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"symbol-name:setStatus","kind":"calls","metadata":{"unresolvedName":"setStatus"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"external:Promise.all","kind":"calls","metadata":{"unresolvedName":"Promise.all"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"/workspace/apps/web/src/api/config_profiles.ts:listConfigProfiles","kind":"calls","metadata":{"unresolvedName":"listConfigProfiles"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"/workspace/apps/web/src/api/projects.ts:listProjects","kind":"calls","metadata":{"unresolvedName":"listProjects"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"/workspace/apps/web/src/api/tool-types.ts:listToolTypes","kind":"calls","metadata":{"unresolvedName":"listToolTypes"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"symbol-name:setProfiles","kind":"calls","metadata":{"unresolvedName":"setProfiles"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"symbol-name:setProjects","kind":"calls","metadata":{"unresolvedName":"setProjects"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"symbol-name:setToolTypes","kind":"calls","metadata":{"unresolvedName":"setToolTypes"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"symbol-name:loadData","kind":"calls","metadata":{"unresolvedName":"loadData"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:resetForm","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:resetForm","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:resetForm","to":"symbol-name:setFormData","kind":"calls","metadata":{"unresolvedName":"setFormData"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:resetForm","to":"symbol-name:setIncludedProfileIds","kind":"calls","metadata":{"unresolvedName":"setIncludedProfileIds"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:resetForm","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:resetForm","to":"symbol-name:setSaveStatus","kind":"calls","metadata":{"unresolvedName":"setSaveStatus"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:resetForm","to":"symbol-name:setPreviewData","kind":"calls","metadata":{"unresolvedName":"setPreviewData"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:populateForm","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:populateForm","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:populateForm","to":"symbol-name:setFormData","kind":"calls","metadata":{"unresolvedName":"setFormData"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:populateForm","to":"symbol-name:setIncludedProfileIds","kind":"calls","metadata":{"unresolvedName":"setIncludedProfileIds"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:populateForm","to":"external:profile.includes.map","kind":"calls","metadata":{"unresolvedName":"profile.includes.map"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:populateForm","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:populateForm","to":"symbol-name:setSaveStatus","kind":"calls","metadata":{"unresolvedName":"setSaveStatus"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:populateForm","to":"symbol-name:setPreviewData","kind":"calls","metadata":{"unresolvedName":"setPreviewData"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:handleSelectProfile","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:handleSelectProfile","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleSelectProfile","to":"symbol-name:setSelectedProfileId","kind":"calls","metadata":{"unresolvedName":"setSelectedProfileId"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleSelectProfile","to":"symbol-name:setIsCreating","kind":"calls","metadata":{"unresolvedName":"setIsCreating"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleSelectProfile","to":"/workspace/apps/web/src/pages/config-profiles.tsx:populateForm","kind":"calls","metadata":{"unresolvedName":"populateForm"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:handleCreateNew","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:handleCreateNew","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleCreateNew","to":"symbol-name:setSelectedProfileId","kind":"calls","metadata":{"unresolvedName":"setSelectedProfileId"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleCreateNew","to":"symbol-name:setIsCreating","kind":"calls","metadata":{"unresolvedName":"setIsCreating"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleCreateNew","to":"symbol-name:resetForm","kind":"calls","metadata":{"unresolvedName":"resetForm"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:getScopeLabel","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:getScopeLabel","kind":"defines"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:wouldCreateCycle","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:wouldCreateCycle","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:wouldCreateCycle","to":"external:visited.has","kind":"calls","metadata":{"unresolvedName":"visited.has"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:wouldCreateCycle","to":"symbol-name:getIncludedProfile","kind":"calls","metadata":{"unresolvedName":"getIncludedProfile"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:wouldCreateCycle","to":"external:nextVisited.add","kind":"calls","metadata":{"unresolvedName":"nextVisited.add"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:wouldCreateCycle","to":"/workspace/apps/web/src/pages/config-profiles.tsx:wouldCreateCycle","kind":"calls","metadata":{"unresolvedName":"wouldCreateCycle"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:availableProfilesForInclude","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:availableProfilesForInclude","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:availableProfilesForInclude","to":"external:profiles.filter","kind":"calls","metadata":{"unresolvedName":"profiles.filter"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:availableProfilesForInclude","to":"external:includedProfileIds.includes","kind":"calls","metadata":{"unresolvedName":"includedProfileIds.includes"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:availableProfilesForInclude","to":"/workspace/apps/web/src/pages/config-profiles.tsx:wouldCreateCycle","kind":"calls","metadata":{"unresolvedName":"wouldCreateCycle"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"external:includedProfileIds.includes","kind":"calls","metadata":{"unresolvedName":"includedProfileIds.includes"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"/workspace/apps/web/src/pages/config-profiles.tsx:wouldCreateCycle","kind":"calls","metadata":{"unresolvedName":"wouldCreateCycle"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:addInclude","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:addInclude","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:addInclude","to":"symbol-name:setIncludedProfileIds","kind":"calls","metadata":{"unresolvedName":"setIncludedProfileIds"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:removeInclude","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:removeInclude","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:removeInclude","to":"symbol-name:setIncludedProfileIds","kind":"calls","metadata":{"unresolvedName":"setIncludedProfileIds"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:removeInclude","to":"external:prev.filter","kind":"calls","metadata":{"unresolvedName":"prev.filter"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDragStart","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDragStart","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDragStart","to":"external:e.dataTransfer.setData","kind":"calls","metadata":{"unresolvedName":"e.dataTransfer.setData"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDragStart","to":"symbol-name:String","kind":"calls","metadata":{"unresolvedName":"String"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDragOver","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDragOver","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDragOver","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDragOver","to":"symbol-name:setDragOverIndex","kind":"calls","metadata":{"unresolvedName":"setDragOverIndex"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDragLeave","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDragLeave","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDragLeave","to":"symbol-name:setDragOverIndex","kind":"calls","metadata":{"unresolvedName":"setDragOverIndex"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDrop","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDrop","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDrop","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDrop","to":"symbol-name:Number","kind":"calls","metadata":{"unresolvedName":"Number"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDrop","to":"external:e.dataTransfer.getData","kind":"calls","metadata":{"unresolvedName":"e.dataTransfer.getData"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDrop","to":"symbol-name:setDragOverIndex","kind":"calls","metadata":{"unresolvedName":"setDragOverIndex"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDrop","to":"symbol-name:setIncludedProfileIds","kind":"calls","metadata":{"unresolvedName":"setIncludedProfileIds"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDrop","to":"external:newOrder.splice","kind":"calls","metadata":{"unresolvedName":"newOrder.splice"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"external:newOrder.splice","kind":"calls","metadata":{"unresolvedName":"newOrder.splice"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:handleSubmit","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:handleSubmit","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleSubmit","to":"external:e.preventDefault","kind":"calls","metadata":{"unresolvedName":"e.preventDefault"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleSubmit","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleSubmit","to":"symbol-name:setSaveStatus","kind":"calls","metadata":{"unresolvedName":"setSaveStatus"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleSubmit","to":"external:formData.name?.trim","kind":"calls","metadata":{"unresolvedName":"formData.name?.trim"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleSubmit","to":"/workspace/apps/web/src/api/config_profiles.ts:createConfigProfile","kind":"calls","metadata":{"unresolvedName":"createConfigProfile"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleSubmit","to":"/workspace/apps/web/src/api/config_profiles.ts:updateProfileIncludes","kind":"calls","metadata":{"unresolvedName":"updateProfileIncludes"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleSubmit","to":"symbol-name:setIsCreating","kind":"calls","metadata":{"unresolvedName":"setIsCreating"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleSubmit","to":"symbol-name:setSelectedProfileId","kind":"calls","metadata":{"unresolvedName":"setSelectedProfileId"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleSubmit","to":"symbol-name:loadData","kind":"calls","metadata":{"unresolvedName":"loadData"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleSubmit","to":"external:(await listConfigProfiles()).find","kind":"calls","metadata":{"unresolvedName":"(await listConfigProfiles()).find"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleSubmit","to":"/workspace/apps/web/src/api/config_profiles.ts:listConfigProfiles","kind":"calls","metadata":{"unresolvedName":"listConfigProfiles"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleSubmit","to":"/workspace/apps/web/src/pages/config-profiles.tsx:populateForm","kind":"calls","metadata":{"unresolvedName":"populateForm"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleSubmit","to":"/workspace/apps/web/src/api/config_profiles.ts:updateConfigProfile","kind":"calls","metadata":{"unresolvedName":"updateConfigProfile"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleSubmit","to":"/workspace/apps/web/src/utils/errors.ts:extractErrorMessage","kind":"calls","metadata":{"unresolvedName":"extractErrorMessage"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDelete","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDelete","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDelete","to":"external:window.confirm","kind":"calls","metadata":{"unresolvedName":"window.confirm"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDelete","to":"/workspace/apps/web/src/api/config_profiles.ts:deleteConfigProfile","kind":"calls","metadata":{"unresolvedName":"deleteConfigProfile"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDelete","to":"symbol-name:setSelectedProfileId","kind":"calls","metadata":{"unresolvedName":"setSelectedProfileId"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDelete","to":"symbol-name:setIsCreating","kind":"calls","metadata":{"unresolvedName":"setIsCreating"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDelete","to":"symbol-name:resetForm","kind":"calls","metadata":{"unresolvedName":"resetForm"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDelete","to":"symbol-name:loadData","kind":"calls","metadata":{"unresolvedName":"loadData"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDelete","to":"symbol-name:alert","kind":"calls","metadata":{"unresolvedName":"alert"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:handlePreview","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:handlePreview","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handlePreview","to":"symbol-name:setPreviewingId","kind":"calls","metadata":{"unresolvedName":"setPreviewingId"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handlePreview","to":"/workspace/apps/web/src/api/config_profiles.ts:previewConfigProfile","kind":"calls","metadata":{"unresolvedName":"previewConfigProfile"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handlePreview","to":"symbol-name:setPreviewData","kind":"calls","metadata":{"unresolvedName":"setPreviewData"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:handlePreview","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:updateFormField","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:updateFormField","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:updateFormField","to":"symbol-name:setFormData","kind":"calls","metadata":{"unresolvedName":"setFormData"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:updateFormField","to":"symbol-name:setSaveStatus","kind":"calls","metadata":{"unresolvedName":"setSaveStatus"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:addEnvVar","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:addEnvVar","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:addEnvVar","to":"symbol-name:setFormData","kind":"calls","metadata":{"unresolvedName":"setFormData"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:addEnvVar","to":"symbol-name:setSaveStatus","kind":"calls","metadata":{"unresolvedName":"setSaveStatus"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:updateEnvVar","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:updateEnvVar","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:updateEnvVar","to":"symbol-name:setFormData","kind":"calls","metadata":{"unresolvedName":"setFormData"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:updateEnvVar","to":"symbol-name:setSaveStatus","kind":"calls","metadata":{"unresolvedName":"setSaveStatus"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:removeEnvVar","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:removeEnvVar","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:removeEnvVar","to":"symbol-name:setFormData","kind":"calls","metadata":{"unresolvedName":"setFormData"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:removeEnvVar","to":"symbol-name:setSaveStatus","kind":"calls","metadata":{"unresolvedName":"setSaveStatus"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:addFile","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:addFile","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:addFile","to":"symbol-name:setFormData","kind":"calls","metadata":{"unresolvedName":"setFormData"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:addFile","to":"symbol-name:setSaveStatus","kind":"calls","metadata":{"unresolvedName":"setSaveStatus"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:updateFile","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:updateFile","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:updateFile","to":"symbol-name:setFormData","kind":"calls","metadata":{"unresolvedName":"setFormData"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:updateFile","to":"symbol-name:setSaveStatus","kind":"calls","metadata":{"unresolvedName":"setSaveStatus"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:removeFile","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:removeFile","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:removeFile","to":"symbol-name:setFormData","kind":"calls","metadata":{"unresolvedName":"setFormData"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:removeFile","to":"symbol-name:setSaveStatus","kind":"calls","metadata":{"unresolvedName":"setSaveStatus"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:addMount","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:addMount","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:addMount","to":"symbol-name:setFormData","kind":"calls","metadata":{"unresolvedName":"setFormData"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:addMount","to":"symbol-name:setSaveStatus","kind":"calls","metadata":{"unresolvedName":"setSaveStatus"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:updateMount","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:updateMount","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:updateMount","to":"symbol-name:setFormData","kind":"calls","metadata":{"unresolvedName":"setFormData"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:updateMount","to":"symbol-name:setSaveStatus","kind":"calls","metadata":{"unresolvedName":"setSaveStatus"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:removeMount","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:removeMount","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:removeMount","to":"symbol-name:setFormData","kind":"calls","metadata":{"unresolvedName":"setFormData"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:removeMount","to":"external:mounts.splice","kind":"calls","metadata":{"unresolvedName":"mounts.splice"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:removeMount","to":"symbol-name:setSaveStatus","kind":"calls","metadata":{"unresolvedName":"setSaveStatus"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"external:mounts.splice","kind":"calls","metadata":{"unresolvedName":"mounts.splice"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:addMountFile","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:addMountFile","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:addMountFile","to":"symbol-name:setFormData","kind":"calls","metadata":{"unresolvedName":"setFormData"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:addMountFile","to":"symbol-name:setSaveStatus","kind":"calls","metadata":{"unresolvedName":"setSaveStatus"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:updateMountFile","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:updateMountFile","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:updateMountFile","to":"symbol-name:setFormData","kind":"calls","metadata":{"unresolvedName":"setFormData"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:updateMountFile","to":"symbol-name:setSaveStatus","kind":"calls","metadata":{"unresolvedName":"setSaveStatus"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:removeMountFile","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:removeMountFile","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:removeMountFile","to":"symbol-name:setFormData","kind":"calls","metadata":{"unresolvedName":"setFormData"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:removeMountFile","to":"symbol-name:setSaveStatus","kind":"calls","metadata":{"unresolvedName":"setSaveStatus"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"symbol-name:setSelectedProfileId","kind":"calls","metadata":{"unresolvedName":"setSelectedProfileId"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"symbol-name:setIsCreating","kind":"calls","metadata":{"unresolvedName":"setIsCreating"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"external:profiles.find","kind":"calls","metadata":{"unresolvedName":"profiles.find"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"/workspace/apps/web/src/pages/config-profiles.tsx:populateForm","kind":"calls","metadata":{"unresolvedName":"populateForm"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"symbol-name:setMobileView","kind":"calls","metadata":{"unresolvedName":"setMobileView"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"symbol-name:setIsCreating","kind":"calls","metadata":{"unresolvedName":"setIsCreating"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"symbol-name:setSelectedProfileId","kind":"calls","metadata":{"unresolvedName":"setSelectedProfileId"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"symbol-name:resetForm","kind":"calls","metadata":{"unresolvedName":"resetForm"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"symbol-name:setMobileView","kind":"calls","metadata":{"unresolvedName":"setMobileView"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"/workspace/apps/web/src/pages/config-profiles.tsx:populateForm","kind":"calls","metadata":{"unresolvedName":"populateForm"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"symbol-name:setIsCreating","kind":"calls","metadata":{"unresolvedName":"setIsCreating"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"symbol-name:setMobileView","kind":"calls","metadata":{"unresolvedName":"setMobileView"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"symbol-name:setMobileView","kind":"calls","metadata":{"unresolvedName":"setMobileView"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:preventDefault","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:preventDefault","kind":"defines"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"/workspace/apps/web/src/pages/config-profiles.tsx:updateFormField","kind":"calls","metadata":{"unresolvedName":"updateFormField"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"/workspace/apps/web/src/pages/config-profiles.tsx:updateFormField","kind":"calls","metadata":{"unresolvedName":"updateFormField"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"/workspace/apps/web/src/pages/config-profiles.tsx:updateFormField","kind":"calls","metadata":{"unresolvedName":"updateFormField"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"/workspace/apps/web/src/pages/config-profiles.tsx:updateFormField","kind":"calls","metadata":{"unresolvedName":"updateFormField"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"/workspace/apps/web/src/pages/config-profiles.tsx:updateFormField","kind":"calls","metadata":{"unresolvedName":"updateFormField"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"external:(formData.mounts || []).filter","kind":"calls","metadata":{"unresolvedName":"(formData.mounts || []).filter"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"/workspace/apps/web/src/pages/config-profiles.tsx:updateFormField","kind":"calls","metadata":{"unresolvedName":"updateFormField"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"symbol-name:setSelectedProfileId","kind":"calls","metadata":{"unresolvedName":"setSelectedProfileId"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"symbol-name:setIsCreating","kind":"calls","metadata":{"unresolvedName":"setIsCreating"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"external:profiles.find","kind":"calls","metadata":{"unresolvedName":"profiles.find"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"/workspace/apps/web/src/pages/config-profiles.tsx:populateForm","kind":"calls","metadata":{"unresolvedName":"populateForm"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"symbol-name:setMobileView","kind":"calls","metadata":{"unresolvedName":"setMobileView"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"symbol-name:setIsCreating","kind":"calls","metadata":{"unresolvedName":"setIsCreating"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"symbol-name:setSelectedProfileId","kind":"calls","metadata":{"unresolvedName":"setSelectedProfileId"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"symbol-name:resetForm","kind":"calls","metadata":{"unresolvedName":"resetForm"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"symbol-name:setMobileView","kind":"calls","metadata":{"unresolvedName":"setMobileView"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"external:e.stopPropagation","kind":"calls","metadata":{"unresolvedName":"e.stopPropagation"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"symbol-name:handleDelete","kind":"calls","metadata":{"unresolvedName":"handleDelete"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"symbol-name:getIncludedProfile","kind":"calls","metadata":{"unresolvedName":"getIncludedProfile"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDragStart","kind":"calls","metadata":{"unresolvedName":"handleDragStart"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDragOver","kind":"calls","metadata":{"unresolvedName":"handleDragOver"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"/workspace/apps/web/src/pages/config-profiles.tsx:handleDrop","kind":"calls","metadata":{"unresolvedName":"handleDrop"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"/workspace/apps/web/src/pages/config-profiles.tsx:getScopeLabel","kind":"calls","metadata":{"unresolvedName":"getScopeLabel"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"/workspace/apps/web/src/pages/config-profiles.tsx:removeInclude","kind":"calls","metadata":{"unresolvedName":"removeInclude"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"/workspace/apps/web/src/pages/config-profiles.tsx:addInclude","kind":"calls","metadata":{"unresolvedName":"addInclude"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"external:JSON.parse","kind":"calls","metadata":{"unresolvedName":"JSON.parse"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"/workspace/apps/web/src/pages/config-profiles.tsx:updateFormField","kind":"calls","metadata":{"unresolvedName":"updateFormField"}},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/config-profiles.tsx","to":"/workspace/apps/web/src/pages/config-profiles.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"symbol-name:resetForm","kind":"calls","metadata":{"unresolvedName":"resetForm"}},{"from":"/workspace/apps/web/src/pages/config-profiles.tsx:","to":"/workspace/apps/web/src/pages/config-profiles.tsx:populateForm","kind":"calls","metadata":{"unresolvedName":"populateForm"}},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"external:@/components/ui/Icon","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"file:/workspace/apps/web/src/hooks/use-workspaces.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"file:/workspace/apps/web/src/hooks/use-workspace-files.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"file:/workspace/apps/web/src/hooks/use-workspace-git.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"file:/workspace/apps/web/src/hooks/use-workspace-instances.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"file:/workspace/apps/web/src/hooks/use-mobile-viewport.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"file:/workspace/apps/web/src/components/tool-starter.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"file:/workspace/apps/web/src/api/workspace-files.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"file:/workspace/apps/web/src/types/workspace.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"/workspace/apps/web/src/pages/workspace-detail.tsx:WorkspaceDetailPage","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"/workspace/apps/web/src/pages/workspace-detail.tsx:WorkspaceDetailPage","kind":"defines"},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:WorkspaceDetailPage","to":"symbol-name:useParams","kind":"calls","metadata":{"unresolvedName":"useParams"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:WorkspaceDetailPage","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:WorkspaceDetailPage","to":"/workspace/apps/web/src/hooks/use-mobile-viewport.ts:useMobileViewport","kind":"calls","metadata":{"unresolvedName":"useMobileViewport"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:WorkspaceDetailPage","to":"/workspace/apps/web/src/hooks/use-workspaces.ts:useWorkspaces","kind":"calls","metadata":{"unresolvedName":"useWorkspaces"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:WorkspaceDetailPage","to":"external:workspaces.find","kind":"calls","metadata":{"unresolvedName":"workspaces.find"}},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"/workspace/apps/web/src/pages/workspace-detail.tsx:WorkspaceHeader","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"/workspace/apps/web/src/pages/workspace-detail.tsx:WorkspaceHeader","kind":"defines"},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"/workspace/apps/web/src/pages/workspace-detail.tsx:TabBar","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"/workspace/apps/web/src/pages/workspace-detail.tsx:TabBar","kind":"defines"},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:TabBar","to":"external:tabs.map","kind":"calls","metadata":{"unresolvedName":"tabs.map"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:TabBar","to":"symbol-name:onChange","kind":"calls","metadata":{"unresolvedName":"onChange"}},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"/workspace/apps/web/src/pages/workspace-detail.tsx:MobileTabBar","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"/workspace/apps/web/src/pages/workspace-detail.tsx:MobileTabBar","kind":"defines"},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:MobileTabBar","to":"external:tabs.map","kind":"calls","metadata":{"unresolvedName":"tabs.map"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:MobileTabBar","to":"symbol-name:onChange","kind":"calls","metadata":{"unresolvedName":"onChange"}},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"/workspace/apps/web/src/pages/workspace-detail.tsx:FilesTab","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"/workspace/apps/web/src/pages/workspace-detail.tsx:FilesTab","kind":"defines"},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:FilesTab","to":"/workspace/apps/web/src/hooks/use-workspace-files.ts:useWorkspaceFiles","kind":"calls","metadata":{"unresolvedName":"useWorkspaceFiles"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:FilesTab","to":"/workspace/apps/web/src/hooks/use-workspace-git.ts:useWorkspaceGit","kind":"calls","metadata":{"unresolvedName":"useWorkspaceGit"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:FilesTab","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:FilesTab","to":"symbol-name:setSelectedPath","kind":"calls","metadata":{"unresolvedName":"setSelectedPath"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:FilesTab","to":"symbol-name:setIsEditing","kind":"calls","metadata":{"unresolvedName":"setIsEditing"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:FilesTab","to":"symbol-name:setEditContent","kind":"calls","metadata":{"unresolvedName":"setEditContent"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:FilesTab","to":"symbol-name:loadFile","kind":"calls","metadata":{"unresolvedName":"loadFile"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:FilesTab","to":"symbol-name:saveFile","kind":"calls","metadata":{"unresolvedName":"saveFile"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:FilesTab","to":"symbol-name:setCommitMessage","kind":"calls","metadata":{"unresolvedName":"setCommitMessage"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:FilesTab","to":"symbol-name:commit","kind":"calls","metadata":{"unresolvedName":"commit"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:FilesTab","to":"external:entries.map","kind":"calls","metadata":{"unresolvedName":"entries.map"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:FilesTab","to":"symbol-name:handleSelect","kind":"calls","metadata":{"unresolvedName":"handleSelect"}},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"/workspace/apps/web/src/pages/workspace-detail.tsx:handleSelect","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"/workspace/apps/web/src/pages/workspace-detail.tsx:handleSelect","kind":"defines"},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:handleSelect","to":"symbol-name:setSelectedPath","kind":"calls","metadata":{"unresolvedName":"setSelectedPath"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:handleSelect","to":"symbol-name:setIsEditing","kind":"calls","metadata":{"unresolvedName":"setIsEditing"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:handleSelect","to":"symbol-name:setEditContent","kind":"calls","metadata":{"unresolvedName":"setEditContent"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:handleSelect","to":"symbol-name:loadFile","kind":"calls","metadata":{"unresolvedName":"loadFile"}},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"/workspace/apps/web/src/pages/workspace-detail.tsx:handleEdit","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"/workspace/apps/web/src/pages/workspace-detail.tsx:handleEdit","kind":"defines"},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:handleEdit","to":"symbol-name:setEditContent","kind":"calls","metadata":{"unresolvedName":"setEditContent"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:handleEdit","to":"symbol-name:setIsEditing","kind":"calls","metadata":{"unresolvedName":"setIsEditing"}},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"/workspace/apps/web/src/pages/workspace-detail.tsx:handleSave","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"/workspace/apps/web/src/pages/workspace-detail.tsx:handleSave","kind":"defines"},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:handleSave","to":"symbol-name:saveFile","kind":"calls","metadata":{"unresolvedName":"saveFile"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:handleSave","to":"symbol-name:setIsEditing","kind":"calls","metadata":{"unresolvedName":"setIsEditing"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:handleSave","to":"symbol-name:setCommitMessage","kind":"calls","metadata":{"unresolvedName":"setCommitMessage"}},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"/workspace/apps/web/src/pages/workspace-detail.tsx:GitTab","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"/workspace/apps/web/src/pages/workspace-detail.tsx:GitTab","kind":"defines"},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:GitTab","to":"/workspace/apps/web/src/hooks/use-workspace-git.ts:useWorkspaceGit","kind":"calls","metadata":{"unresolvedName":"useWorkspaceGit"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:GitTab","to":"symbol-name:checkout","kind":"calls","metadata":{"unresolvedName":"checkout"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:GitTab","to":"external:branches.map","kind":"calls","metadata":{"unresolvedName":"branches.map"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:GitTab","to":"external:history.map","kind":"calls","metadata":{"unresolvedName":"history.map"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:GitTab","to":"external:commit.hash.slice","kind":"calls","metadata":{"unresolvedName":"commit.hash.slice"}},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"/workspace/apps/web/src/pages/workspace-detail.tsx:ToolsTab","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"/workspace/apps/web/src/pages/workspace-detail.tsx:ToolsTab","kind":"defines"},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:ToolsTab","to":"/workspace/apps/web/src/hooks/use-workspace-instances.ts:useWorkspaceInstances","kind":"calls","metadata":{"unresolvedName":"useWorkspaceInstances"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:ToolsTab","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:ToolsTab","to":"symbol-name:setShowModal","kind":"calls","metadata":{"unresolvedName":"setShowModal"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:ToolsTab","to":"external:instances.map","kind":"calls","metadata":{"unresolvedName":"instances.map"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:ToolsTab","to":"external:e.stopPropagation","kind":"calls","metadata":{"unresolvedName":"e.stopPropagation"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:ToolsTab","to":"symbol-name:refresh","kind":"calls","metadata":{"unresolvedName":"refresh"}},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"/workspace/apps/web/src/pages/workspace-detail.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"/workspace/apps/web/src/pages/workspace-detail.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:","to":"symbol-name:setShowModal","kind":"calls","metadata":{"unresolvedName":"setShowModal"}},{"from":"/workspace/apps/web/src/pages/workspace-detail.tsx:","to":"symbol-name:refresh","kind":"calls","metadata":{"unresolvedName":"refresh"}},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"/workspace/apps/web/src/pages/workspace-detail.tsx:SettingsTab","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/workspace-detail.tsx","to":"/workspace/apps/web/src/pages/workspace-detail.tsx:SettingsTab","kind":"defines"},{"from":"file:/workspace/apps/web/src/pages/workspaces.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/workspaces.tsx","to":"external:@/components/ui/Icon","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/workspaces.tsx","to":"file:/workspace/apps/web/src/hooks/use-workspaces.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/workspaces.tsx","to":"file:/workspace/apps/web/src/hooks/use-workspace-actions.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/workspaces.tsx","to":"file:/workspace/apps/web/src/components/workspace-card.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/workspaces.tsx","to":"file:/workspace/apps/web/src/components/workspace-create-form.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/workspaces.tsx","to":"file:/workspace/apps/web/src/components/tool-starter.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/workspaces.tsx","to":"file:/workspace/apps/web/src/types/workspace.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/pages/workspaces.tsx","to":"/workspace/apps/web/src/pages/workspaces.tsx:WorkspacesPage","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/workspaces.tsx","to":"/workspace/apps/web/src/pages/workspaces.tsx:WorkspacesPage","kind":"defines"},{"from":"/workspace/apps/web/src/pages/workspaces.tsx:WorkspacesPage","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/pages/workspaces.tsx:WorkspacesPage","to":"/workspace/apps/web/src/hooks/use-workspaces.ts:useWorkspaces","kind":"calls","metadata":{"unresolvedName":"useWorkspaces"}},{"from":"/workspace/apps/web/src/pages/workspaces.tsx:WorkspacesPage","to":"/workspace/apps/web/src/hooks/use-workspace-actions.ts:useWorkspaceActions","kind":"calls","metadata":{"unresolvedName":"useWorkspaceActions"}},{"from":"/workspace/apps/web/src/pages/workspaces.tsx:WorkspacesPage","to":"external:actions.delete","kind":"calls","metadata":{"unresolvedName":"actions.delete"}},{"from":"/workspace/apps/web/src/pages/workspaces.tsx:WorkspacesPage","to":"external:actions.sync","kind":"calls","metadata":{"unresolvedName":"actions.sync"}},{"from":"/workspace/apps/web/src/pages/workspaces.tsx:WorkspacesPage","to":"symbol-name:setShowCreate","kind":"calls","metadata":{"unresolvedName":"setShowCreate"}},{"from":"/workspace/apps/web/src/pages/workspaces.tsx:WorkspacesPage","to":"symbol-name:refresh","kind":"calls","metadata":{"unresolvedName":"refresh"}},{"from":"/workspace/apps/web/src/pages/workspaces.tsx:WorkspacesPage","to":"external:workspaces.map","kind":"calls","metadata":{"unresolvedName":"workspaces.map"}},{"from":"/workspace/apps/web/src/pages/workspaces.tsx:WorkspacesPage","to":"symbol-name:setStartWorkspace","kind":"calls","metadata":{"unresolvedName":"setStartWorkspace"}},{"from":"/workspace/apps/web/src/pages/workspaces.tsx:WorkspacesPage","to":"external:e.stopPropagation","kind":"calls","metadata":{"unresolvedName":"e.stopPropagation"}},{"from":"file:/workspace/apps/web/src/pages/workspaces.tsx","to":"/workspace/apps/web/src/pages/workspaces.tsx:handleDelete","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/workspaces.tsx","to":"/workspace/apps/web/src/pages/workspaces.tsx:handleDelete","kind":"defines"},{"from":"/workspace/apps/web/src/pages/workspaces.tsx:handleDelete","to":"external:actions.delete","kind":"calls","metadata":{"unresolvedName":"actions.delete"}},{"from":"file:/workspace/apps/web/src/pages/workspaces.tsx","to":"/workspace/apps/web/src/pages/workspaces.tsx:handleSync","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/workspaces.tsx","to":"/workspace/apps/web/src/pages/workspaces.tsx:handleSync","kind":"defines"},{"from":"/workspace/apps/web/src/pages/workspaces.tsx:handleSync","to":"external:actions.sync","kind":"calls","metadata":{"unresolvedName":"actions.sync"}},{"from":"file:/workspace/apps/web/src/pages/workspaces.tsx","to":"/workspace/apps/web/src/pages/workspaces.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/workspaces.tsx","to":"/workspace/apps/web/src/pages/workspaces.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/workspaces.tsx:","to":"symbol-name:setShowCreate","kind":"calls","metadata":{"unresolvedName":"setShowCreate"}},{"from":"/workspace/apps/web/src/pages/workspaces.tsx:","to":"symbol-name:refresh","kind":"calls","metadata":{"unresolvedName":"refresh"}},{"from":"file:/workspace/apps/web/src/pages/workspaces.tsx","to":"/workspace/apps/web/src/pages/workspaces.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/pages/workspaces.tsx","to":"/workspace/apps/web/src/pages/workspaces.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/pages/workspaces.tsx:","to":"symbol-name:setStartWorkspace","kind":"calls","metadata":{"unresolvedName":"setStartWorkspace"}},{"from":"/workspace/apps/web/src/pages/workspaces.tsx:","to":"symbol-name:refresh","kind":"calls","metadata":{"unresolvedName":"refresh"}},{"from":"file:/workspace/apps/web/src/router.tsx","to":"external:react-router-dom","kind":"imports"},{"from":"file:/workspace/apps/web/src/router.tsx","to":"file:/workspace/apps/web/src/components/layout/AppShell.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/router.tsx","to":"file:/workspace/apps/web/src/components/ProtectedRoute.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/router.tsx","to":"file:/workspace/apps/web/src/pages/DashboardPage.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/router.tsx","to":"file:/workspace/apps/web/src/pages/PlaceholderPage.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/router.tsx","to":"file:/workspace/apps/web/src/pages/ProfilePage.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/router.tsx","to":"file:/workspace/apps/web/src/pages/ProjectsPage.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/router.tsx","to":"file:/workspace/apps/web/src/pages/GitRepositoriesPage.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/router.tsx","to":"file:/workspace/apps/web/src/pages/GitHistoryPage.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/router.tsx","to":"file:/workspace/apps/web/src/pages/ProjectSettingsPage.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/router.tsx","to":"file:/workspace/apps/web/src/pages/RepoWorkspacePage.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/router.tsx","to":"file:/workspace/apps/web/src/pages/SettingsPage.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/router.tsx","to":"file:/workspace/apps/web/src/pages/TerminalPage.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/router.tsx","to":"file:/workspace/apps/web/src/pages/ToolWorkshopPage.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/router.tsx","to":"file:/workspace/apps/web/src/pages/SshKeysPage.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/router.tsx","to":"file:/workspace/apps/web/src/pages/ToolConfigsPage.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/router.tsx","to":"file:/workspace/apps/web/src/pages/ToolTypesPage.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/router.tsx","to":"file:/workspace/apps/web/src/pages/SessionsPage.tsx","kind":"imports"},{"from":"file:/workspace/apps/web/src/router.tsx","to":"/workspace/apps/web/src/router.tsx:AppRouter","kind":"contains"},{"from":"file:/workspace/apps/web/src/router.tsx","to":"/workspace/apps/web/src/router.tsx:AppRouter","kind":"defines"},{"from":"file:/workspace/apps/web/src/state/auth.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/state/auth.tsx","to":"file:/workspace/apps/web/src/api/client.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/state/auth.tsx","to":"file:/workspace/apps/web/src/types","kind":"imports"},{"from":"file:/workspace/apps/web/src/state/auth.tsx","to":"/workspace/apps/web/src/state/auth.tsx:AuthProvider","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/auth.tsx","to":"/workspace/apps/web/src/state/auth.tsx:AuthProvider","kind":"defines"},{"from":"/workspace/apps/web/src/state/auth.tsx:AuthProvider","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/state/auth.tsx:AuthProvider","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/state/auth.tsx:AuthProvider","to":"symbol-name:setState","kind":"calls","metadata":{"unresolvedName":"setState"}},{"from":"/workspace/apps/web/src/state/auth.tsx:AuthProvider","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"/workspace/apps/web/src/state/auth.tsx:AuthProvider","to":"symbol-name:setUser","kind":"calls","metadata":{"unresolvedName":"setUser"}},{"from":"/workspace/apps/web/src/state/auth.tsx:AuthProvider","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"/workspace/apps/web/src/state/auth.tsx:AuthProvider","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/state/auth.tsx:AuthProvider","to":"symbol-name:refreshSession","kind":"calls","metadata":{"unresolvedName":"refreshSession"}},{"from":"/workspace/apps/web/src/state/auth.tsx:AuthProvider","to":"symbol-name:useMemo","kind":"calls","metadata":{"unresolvedName":"useMemo"}},{"from":"file:/workspace/apps/web/src/state/auth.tsx","to":"/workspace/apps/web/src/state/auth.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/auth.tsx","to":"/workspace/apps/web/src/state/auth.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/state/auth.tsx:","to":"symbol-name:setState","kind":"calls","metadata":{"unresolvedName":"setState"}},{"from":"/workspace/apps/web/src/state/auth.tsx:","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"/workspace/apps/web/src/state/auth.tsx:","to":"symbol-name:setUser","kind":"calls","metadata":{"unresolvedName":"setUser"}},{"from":"file:/workspace/apps/web/src/state/auth.tsx","to":"/workspace/apps/web/src/state/auth.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/auth.tsx","to":"/workspace/apps/web/src/state/auth.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/state/auth.tsx:","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"/workspace/apps/web/src/state/auth.tsx:","to":"symbol-name:setUser","kind":"calls","metadata":{"unresolvedName":"setUser"}},{"from":"/workspace/apps/web/src/state/auth.tsx:","to":"symbol-name:setState","kind":"calls","metadata":{"unresolvedName":"setState"}},{"from":"file:/workspace/apps/web/src/state/auth.tsx","to":"/workspace/apps/web/src/state/auth.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/auth.tsx","to":"/workspace/apps/web/src/state/auth.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/state/auth.tsx:","to":"symbol-name:refreshSession","kind":"calls","metadata":{"unresolvedName":"refreshSession"}},{"from":"file:/workspace/apps/web/src/state/auth.tsx","to":"/workspace/apps/web/src/state/auth.tsx:useAuth","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/auth.tsx","to":"/workspace/apps/web/src/state/auth.tsx:useAuth","kind":"defines"},{"from":"/workspace/apps/web/src/state/auth.tsx:useAuth","to":"symbol-name:useContext","kind":"calls","metadata":{"unresolvedName":"useContext"}},{"from":"file:/workspace/apps/web/src/state/events.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/state/events.tsx","to":"file:/workspace/apps/web/src/hooks/use-events.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/state/events.tsx","to":"file:/workspace/apps/web/src/types/events.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/state/events.tsx","to":"/workspace/apps/web/src/state/events.tsx:EventProvider","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/events.tsx","to":"/workspace/apps/web/src/state/events.tsx:EventProvider","kind":"defines"},{"from":"/workspace/apps/web/src/state/events.tsx:EventProvider","to":"/workspace/apps/web/src/hooks/use-events.ts:useEvents","kind":"calls","metadata":{"unresolvedName":"useEvents"}},{"from":"/workspace/apps/web/src/state/events.tsx:EventProvider","to":"symbol-name:useMemo","kind":"calls","metadata":{"unresolvedName":"useMemo"}},{"from":"file:/workspace/apps/web/src/state/events.tsx","to":"/workspace/apps/web/src/state/events.tsx:useEventContext","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/events.tsx","to":"/workspace/apps/web/src/state/events.tsx:useEventContext","kind":"defines"},{"from":"/workspace/apps/web/src/state/events.tsx:useEventContext","to":"symbol-name:useContext","kind":"calls","metadata":{"unresolvedName":"useContext"}},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"file:/workspace/apps/web/src/api/notifications.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"file:/workspace/apps/web/src/api/notifications.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","kind":"defines"},{"from":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","to":"symbol-name:useRef","kind":"calls","metadata":{"unresolvedName":"useRef"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","to":"/workspace/apps/web/src/api/notifications.ts:getUnreadCount","kind":"calls","metadata":{"unresolvedName":"getUnreadCount"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","to":"symbol-name:setUnreadCount","kind":"calls","metadata":{"unresolvedName":"setUnreadCount"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","to":"symbol-name:clearInterval","kind":"calls","metadata":{"unresolvedName":"clearInterval"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","to":"external:console.error","kind":"calls","metadata":{"unresolvedName":"console.error"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","to":"symbol-name:setIsLoading","kind":"calls","metadata":{"unresolvedName":"setIsLoading"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","to":"/workspace/apps/web/src/api/notifications.ts:getNotifications","kind":"calls","metadata":{"unresolvedName":"getNotifications"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","to":"symbol-name:setNotifications","kind":"calls","metadata":{"unresolvedName":"setNotifications"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","to":"symbol-name:fetchUnreadCount","kind":"calls","metadata":{"unresolvedName":"fetchUnreadCount"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","to":"symbol-name:setInterval","kind":"calls","metadata":{"unresolvedName":"setInterval"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","to":"symbol-name:fetchList","kind":"calls","metadata":{"unresolvedName":"fetchList"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","to":"symbol-name:stopPolling","kind":"calls","metadata":{"unresolvedName":"stopPolling"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","to":"symbol-name:startPolling","kind":"calls","metadata":{"unresolvedName":"startPolling"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","to":"external:document.addEventListener","kind":"calls","metadata":{"unresolvedName":"document.addEventListener"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","to":"external:document.removeEventListener","kind":"calls","metadata":{"unresolvedName":"document.removeEventListener"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","to":"external:currentNotifications.find","kind":"calls","metadata":{"unresolvedName":"currentNotifications.find"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","to":"external:currentNotifications.map","kind":"calls","metadata":{"unresolvedName":"currentNotifications.map"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","to":"external:new Date().toISOString","kind":"calls","metadata":{"unresolvedName":"new Date().toISOString"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","to":"external:Math.max","kind":"calls","metadata":{"unresolvedName":"Math.max"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","to":"/workspace/apps/web/src/api/notifications.ts:markNotificationRead","kind":"calls","metadata":{"unresolvedName":"markNotificationRead"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","to":"/workspace/apps/web/src/api/notifications.ts:markAllNotificationsRead","kind":"calls","metadata":{"unresolvedName":"markAllNotificationsRead"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","to":"external:currentNotifications.filter","kind":"calls","metadata":{"unresolvedName":"currentNotifications.filter"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","to":"/workspace/apps/web/src/api/notifications.ts:dismissNotification","kind":"calls","metadata":{"unresolvedName":"dismissNotification"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:NotificationProvider","to":"/workspace/apps/web/src/api/notifications.ts:clearAllNotifications","kind":"calls","metadata":{"unresolvedName":"clearAllNotifications"}},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"/workspace/apps/web/src/api/notifications.ts:getUnreadCount","kind":"calls","metadata":{"unresolvedName":"getUnreadCount"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:setUnreadCount","kind":"calls","metadata":{"unresolvedName":"setUnreadCount"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:clearInterval","kind":"calls","metadata":{"unresolvedName":"clearInterval"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"external:console.error","kind":"calls","metadata":{"unresolvedName":"console.error"}},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:setIsLoading","kind":"calls","metadata":{"unresolvedName":"setIsLoading"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"/workspace/apps/web/src/api/notifications.ts:getNotifications","kind":"calls","metadata":{"unresolvedName":"getNotifications"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:setNotifications","kind":"calls","metadata":{"unresolvedName":"setNotifications"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:clearInterval","kind":"calls","metadata":{"unresolvedName":"clearInterval"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"external:console.error","kind":"calls","metadata":{"unresolvedName":"console.error"}},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:fetchUnreadCount","kind":"calls","metadata":{"unresolvedName":"fetchUnreadCount"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:setInterval","kind":"calls","metadata":{"unresolvedName":"setInterval"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:fetchList","kind":"calls","metadata":{"unresolvedName":"fetchList"}},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:fetchUnreadCount","kind":"calls","metadata":{"unresolvedName":"fetchUnreadCount"}},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:fetchList","kind":"calls","metadata":{"unresolvedName":"fetchList"}},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:clearInterval","kind":"calls","metadata":{"unresolvedName":"clearInterval"}},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:stopPolling","kind":"calls","metadata":{"unresolvedName":"stopPolling"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:startPolling","kind":"calls","metadata":{"unresolvedName":"startPolling"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"external:document.addEventListener","kind":"calls","metadata":{"unresolvedName":"document.addEventListener"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"external:document.removeEventListener","kind":"calls","metadata":{"unresolvedName":"document.removeEventListener"}},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:handleVisibilityChange","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:handleVisibilityChange","kind":"defines"},{"from":"/workspace/apps/web/src/state/notifications.tsx:handleVisibilityChange","to":"symbol-name:stopPolling","kind":"calls","metadata":{"unresolvedName":"stopPolling"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:handleVisibilityChange","to":"symbol-name:startPolling","kind":"calls","metadata":{"unresolvedName":"startPolling"}},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"external:document.removeEventListener","kind":"calls","metadata":{"unresolvedName":"document.removeEventListener"}},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:clearInterval","kind":"calls","metadata":{"unresolvedName":"clearInterval"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:fetchList","kind":"calls","metadata":{"unresolvedName":"fetchList"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:setInterval","kind":"calls","metadata":{"unresolvedName":"setInterval"}},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:fetchList","kind":"calls","metadata":{"unresolvedName":"fetchList"}},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:startPolling","kind":"calls","metadata":{"unresolvedName":"startPolling"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:stopPolling","kind":"calls","metadata":{"unresolvedName":"stopPolling"}},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:stopPolling","kind":"calls","metadata":{"unresolvedName":"stopPolling"}},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"external:currentNotifications.find","kind":"calls","metadata":{"unresolvedName":"currentNotifications.find"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:setNotifications","kind":"calls","metadata":{"unresolvedName":"setNotifications"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"external:currentNotifications.map","kind":"calls","metadata":{"unresolvedName":"currentNotifications.map"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"external:new Date().toISOString","kind":"calls","metadata":{"unresolvedName":"new Date().toISOString"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:setUnreadCount","kind":"calls","metadata":{"unresolvedName":"setUnreadCount"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"external:Math.max","kind":"calls","metadata":{"unresolvedName":"Math.max"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"/workspace/apps/web/src/api/notifications.ts:markNotificationRead","kind":"calls","metadata":{"unresolvedName":"markNotificationRead"}},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:setNotifications","kind":"calls","metadata":{"unresolvedName":"setNotifications"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"external:currentNotifications.map","kind":"calls","metadata":{"unresolvedName":"currentNotifications.map"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"external:new Date().toISOString","kind":"calls","metadata":{"unresolvedName":"new Date().toISOString"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:setUnreadCount","kind":"calls","metadata":{"unresolvedName":"setUnreadCount"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"/workspace/apps/web/src/api/notifications.ts:markAllNotificationsRead","kind":"calls","metadata":{"unresolvedName":"markAllNotificationsRead"}},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"external:currentNotifications.find","kind":"calls","metadata":{"unresolvedName":"currentNotifications.find"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:setNotifications","kind":"calls","metadata":{"unresolvedName":"setNotifications"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"external:currentNotifications.filter","kind":"calls","metadata":{"unresolvedName":"currentNotifications.filter"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:setUnreadCount","kind":"calls","metadata":{"unresolvedName":"setUnreadCount"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"external:Math.max","kind":"calls","metadata":{"unresolvedName":"Math.max"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"/workspace/apps/web/src/api/notifications.ts:dismissNotification","kind":"calls","metadata":{"unresolvedName":"dismissNotification"}},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:fetchList","kind":"calls","metadata":{"unresolvedName":"fetchList"}},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/notifications.tsx","to":"/workspace/apps/web/src/state/notifications.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"external:currentNotifications.filter","kind":"calls","metadata":{"unresolvedName":"currentNotifications.filter"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:setNotifications","kind":"calls","metadata":{"unresolvedName":"setNotifications"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:setUnreadCount","kind":"calls","metadata":{"unresolvedName":"setUnreadCount"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"external:Math.max","kind":"calls","metadata":{"unresolvedName":"Math.max"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"symbol-name:setError","kind":"calls","metadata":{"unresolvedName":"setError"}},{"from":"/workspace/apps/web/src/state/notifications.tsx:","to":"/workspace/apps/web/src/api/notifications.ts:clearAllNotifications","kind":"calls","metadata":{"unresolvedName":"clearAllNotifications"}},{"from":"file:/workspace/apps/web/src/state/sessions.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/state/sessions.tsx","to":"file:/workspace/apps/web/src/types/session.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/state/sessions.tsx","to":"/workspace/apps/web/src/state/sessions.tsx:SessionsProvider","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/sessions.tsx","to":"/workspace/apps/web/src/state/sessions.tsx:SessionsProvider","kind":"defines"},{"from":"/workspace/apps/web/src/state/sessions.tsx:SessionsProvider","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/state/sessions.tsx:SessionsProvider","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/state/sessions.tsx:SessionsProvider","to":"symbol-name:setSessions","kind":"calls","metadata":{"unresolvedName":"setSessions"}},{"from":"file:/workspace/apps/web/src/state/sessions.tsx","to":"/workspace/apps/web/src/state/sessions.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/sessions.tsx","to":"/workspace/apps/web/src/state/sessions.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/state/sessions.tsx:","to":"symbol-name:setSessions","kind":"calls","metadata":{"unresolvedName":"setSessions"}},{"from":"file:/workspace/apps/web/src/state/sessions.tsx","to":"/workspace/apps/web/src/state/sessions.tsx:useSessions","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/sessions.tsx","to":"/workspace/apps/web/src/state/sessions.tsx:useSessions","kind":"defines"},{"from":"/workspace/apps/web/src/state/sessions.tsx:useSessions","to":"symbol-name:useContext","kind":"calls","metadata":{"unresolvedName":"useContext"}},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:ToastProvider","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:ToastProvider","kind":"defines"},{"from":"/workspace/apps/web/src/state/toast.tsx:ToastProvider","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/state/toast.tsx:ToastProvider","to":"symbol-name:useRef","kind":"calls","metadata":{"unresolvedName":"useRef"}},{"from":"/workspace/apps/web/src/state/toast.tsx:ToastProvider","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/state/toast.tsx:ToastProvider","to":"symbol-name:setToasts","kind":"calls","metadata":{"unresolvedName":"setToasts"}},{"from":"/workspace/apps/web/src/state/toast.tsx:ToastProvider","to":"external:prev.filter","kind":"calls","metadata":{"unresolvedName":"prev.filter"}},{"from":"/workspace/apps/web/src/state/toast.tsx:ToastProvider","to":"external:timersRef.current.get","kind":"calls","metadata":{"unresolvedName":"timersRef.current.get"}},{"from":"/workspace/apps/web/src/state/toast.tsx:ToastProvider","to":"symbol-name:clearTimeout","kind":"calls","metadata":{"unresolvedName":"clearTimeout"}},{"from":"/workspace/apps/web/src/state/toast.tsx:ToastProvider","to":"external:timersRef.current.delete","kind":"calls","metadata":{"unresolvedName":"timersRef.current.delete"}},{"from":"/workspace/apps/web/src/state/toast.tsx:ToastProvider","to":"external:Date.now","kind":"calls","metadata":{"unresolvedName":"Date.now"}},{"from":"/workspace/apps/web/src/state/toast.tsx:ToastProvider","to":"symbol-name:setTimeout","kind":"calls","metadata":{"unresolvedName":"setTimeout"}},{"from":"/workspace/apps/web/src/state/toast.tsx:ToastProvider","to":"symbol-name:removeToast","kind":"calls","metadata":{"unresolvedName":"removeToast"}},{"from":"/workspace/apps/web/src/state/toast.tsx:ToastProvider","to":"external:timersRef.current.set","kind":"calls","metadata":{"unresolvedName":"timersRef.current.set"}},{"from":"/workspace/apps/web/src/state/toast.tsx:ToastProvider","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/state/toast.tsx:ToastProvider","to":"symbol-name:addToast","kind":"calls","metadata":{"unresolvedName":"addToast"}},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/state/toast.tsx:","to":"symbol-name:setToasts","kind":"calls","metadata":{"unresolvedName":"setToasts"}},{"from":"/workspace/apps/web/src/state/toast.tsx:","to":"external:prev.filter","kind":"calls","metadata":{"unresolvedName":"prev.filter"}},{"from":"/workspace/apps/web/src/state/toast.tsx:","to":"external:timersRef.current.get","kind":"calls","metadata":{"unresolvedName":"timersRef.current.get"}},{"from":"/workspace/apps/web/src/state/toast.tsx:","to":"symbol-name:clearTimeout","kind":"calls","metadata":{"unresolvedName":"clearTimeout"}},{"from":"/workspace/apps/web/src/state/toast.tsx:","to":"external:timersRef.current.delete","kind":"calls","metadata":{"unresolvedName":"timersRef.current.delete"}},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/state/toast.tsx:","to":"external:Date.now","kind":"calls","metadata":{"unresolvedName":"Date.now"}},{"from":"/workspace/apps/web/src/state/toast.tsx:","to":"symbol-name:setToasts","kind":"calls","metadata":{"unresolvedName":"setToasts"}},{"from":"/workspace/apps/web/src/state/toast.tsx:","to":"symbol-name:setTimeout","kind":"calls","metadata":{"unresolvedName":"setTimeout"}},{"from":"/workspace/apps/web/src/state/toast.tsx:","to":"symbol-name:removeToast","kind":"calls","metadata":{"unresolvedName":"removeToast"}},{"from":"/workspace/apps/web/src/state/toast.tsx:","to":"external:timersRef.current.set","kind":"calls","metadata":{"unresolvedName":"timersRef.current.set"}},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/state/toast.tsx:","to":"symbol-name:removeToast","kind":"calls","metadata":{"unresolvedName":"removeToast"}},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/state/toast.tsx:","to":"symbol-name:addToast","kind":"calls","metadata":{"unresolvedName":"addToast"}},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:useToast","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:useToast","kind":"defines"},{"from":"/workspace/apps/web/src/state/toast.tsx:useToast","to":"symbol-name:useContext","kind":"calls","metadata":{"unresolvedName":"useContext"}},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:info","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:info","kind":"defines"},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:success","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:success","kind":"defines"},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:warning","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:warning","kind":"defines"},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:error","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:error","kind":"defines"},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:severityStyles","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:severityStyles","kind":"defines"},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:ToastContainer","kind":"contains"},{"from":"file:/workspace/apps/web/src/state/toast.tsx","to":"/workspace/apps/web/src/state/toast.tsx:ToastContainer","kind":"defines"},{"from":"/workspace/apps/web/src/state/toast.tsx:ToastContainer","to":"external:toasts.map","kind":"calls","metadata":{"unresolvedName":"toasts.map"}},{"from":"/workspace/apps/web/src/state/toast.tsx:ToastContainer","to":"/workspace/apps/web/src/state/toast.tsx:severityStyles","kind":"calls","metadata":{"unresolvedName":"severityStyles"}},{"from":"/workspace/apps/web/src/state/toast.tsx:ToastContainer","to":"symbol-name:onDismiss","kind":"calls","metadata":{"unresolvedName":"onDismiss"}},{"from":"file:/workspace/apps/web/src/test/setup.ts","to":"external:@testing-library/jest-dom/vitest","kind":"imports"},{"from":"file:/workspace/apps/web/src/utils/errors.ts","to":"/workspace/apps/web/src/utils/errors.ts:extractErrorMessage","kind":"contains"},{"from":"file:/workspace/apps/web/src/utils/errors.ts","to":"/workspace/apps/web/src/utils/errors.ts:extractErrorMessage","kind":"defines"},{"from":"/workspace/apps/web/src/utils/errors.ts:extractErrorMessage","to":"external:Array.isArray","kind":"calls","metadata":{"unresolvedName":"Array.isArray"}},{"from":"/workspace/apps/web/src/utils/errors.ts:extractErrorMessage","to":"external:detail.map((d) => typeof d === \"string\" ? d : d.msg || JSON.stringify(d)).join","kind":"calls","metadata":{"unresolvedName":"detail.map((d) => typeof d === \"string\" ? d : d.msg || JSON.stringify(d)).join"}},{"from":"/workspace/apps/web/src/utils/errors.ts:extractErrorMessage","to":"external:detail.map","kind":"calls","metadata":{"unresolvedName":"detail.map"}},{"from":"/workspace/apps/web/src/utils/errors.ts:extractErrorMessage","to":"external:JSON.stringify","kind":"calls","metadata":{"unresolvedName":"JSON.stringify"}},{"from":"file:/workspace/apps/web/src/utils/language.ts","to":"external:prismjs","kind":"imports"},{"from":"file:/workspace/apps/web/src/utils/language.ts","to":"/workspace/apps/web/src/utils/language.ts:detectLanguage","kind":"contains"},{"from":"file:/workspace/apps/web/src/utils/language.ts","to":"/workspace/apps/web/src/utils/language.ts:detectLanguage","kind":"defines"},{"from":"/workspace/apps/web/src/utils/language.ts:detectLanguage","to":"external:filename.split(\".\").pop()?.toLowerCase","kind":"calls","metadata":{"unresolvedName":"filename.split(\".\").pop()?.toLowerCase"}},{"from":"/workspace/apps/web/src/utils/language.ts:detectLanguage","to":"external:filename.split(\".\").pop","kind":"calls","metadata":{"unresolvedName":"filename.split(\".\").pop"}},{"from":"/workspace/apps/web/src/utils/language.ts:detectLanguage","to":"external:filename.split","kind":"calls","metadata":{"unresolvedName":"filename.split"}},{"from":"file:/workspace/apps/web/src/utils/language.ts","to":"/workspace/apps/web/src/utils/language.ts:highlightCode","kind":"contains"},{"from":"file:/workspace/apps/web/src/utils/language.ts","to":"/workspace/apps/web/src/utils/language.ts:highlightCode","kind":"defines"},{"from":"/workspace/apps/web/src/utils/language.ts:highlightCode","to":"external:Prism.highlight","kind":"calls","metadata":{"unresolvedName":"Prism.highlight"}},{"from":"file:/workspace/apps/web/src/utils/language.ts","to":"/workspace/apps/web/src/utils/language.ts:loadLanguage","kind":"contains"},{"from":"file:/workspace/apps/web/src/utils/language.ts","to":"/workspace/apps/web/src/utils/language.ts:loadLanguage","kind":"defines"},{"from":"/workspace/apps/web/src/utils/language.ts:loadLanguage","to":"symbol-name:import","kind":"calls","metadata":{"unresolvedName":"import"}},{"from":"file:/workspace/apps/web/src/utils/terminal-protocol.ts","to":"file:/workspace/apps/web/src/types/terminal.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/utils/terminal-protocol.ts","to":"/workspace/apps/web/src/utils/terminal-protocol.ts:encodeControlMessage","kind":"contains"},{"from":"file:/workspace/apps/web/src/utils/terminal-protocol.ts","to":"/workspace/apps/web/src/utils/terminal-protocol.ts:encodeControlMessage","kind":"defines"},{"from":"/workspace/apps/web/src/utils/terminal-protocol.ts:encodeControlMessage","to":"external:JSON.stringify","kind":"calls","metadata":{"unresolvedName":"JSON.stringify"}},{"from":"file:/workspace/apps/web/src/utils/terminal-protocol.ts","to":"/workspace/apps/web/src/utils/terminal-protocol.ts:decodeControlMessage","kind":"contains"},{"from":"file:/workspace/apps/web/src/utils/terminal-protocol.ts","to":"/workspace/apps/web/src/utils/terminal-protocol.ts:decodeControlMessage","kind":"defines"},{"from":"/workspace/apps/web/src/utils/terminal-protocol.ts:decodeControlMessage","to":"external:JSON.parse","kind":"calls","metadata":{"unresolvedName":"JSON.parse"}},{"from":"/workspace/apps/web/src/utils/terminal-protocol.ts:decodeControlMessage","to":"/workspace/apps/web/src/utils/terminal-protocol.ts:isServerControlMessage","kind":"calls","metadata":{"unresolvedName":"isServerControlMessage"}},{"from":"file:/workspace/apps/web/src/utils/terminal-protocol.ts","to":"/workspace/apps/web/src/utils/terminal-protocol.ts:isControlMessage","kind":"contains"},{"from":"file:/workspace/apps/web/src/utils/terminal-protocol.ts","to":"/workspace/apps/web/src/utils/terminal-protocol.ts:isControlMessage","kind":"defines"},{"from":"file:/workspace/apps/web/src/utils/terminal-protocol.ts","to":"/workspace/apps/web/src/utils/terminal-protocol.ts:encodeInputData","kind":"contains"},{"from":"file:/workspace/apps/web/src/utils/terminal-protocol.ts","to":"/workspace/apps/web/src/utils/terminal-protocol.ts:encodeInputData","kind":"defines"},{"from":"/workspace/apps/web/src/utils/terminal-protocol.ts:encodeInputData","to":"symbol-name:btoa","kind":"calls","metadata":{"unresolvedName":"btoa"}},{"from":"/workspace/apps/web/src/utils/terminal-protocol.ts:encodeInputData","to":"symbol-name:unescape","kind":"calls","metadata":{"unresolvedName":"unescape"}},{"from":"/workspace/apps/web/src/utils/terminal-protocol.ts:encodeInputData","to":"symbol-name:encodeURIComponent","kind":"calls","metadata":{"unresolvedName":"encodeURIComponent"}},{"from":"file:/workspace/apps/web/src/utils/terminal-protocol.ts","to":"/workspace/apps/web/src/utils/terminal-protocol.ts:decodeInputData","kind":"contains"},{"from":"file:/workspace/apps/web/src/utils/terminal-protocol.ts","to":"/workspace/apps/web/src/utils/terminal-protocol.ts:decodeInputData","kind":"defines"},{"from":"/workspace/apps/web/src/utils/terminal-protocol.ts:decodeInputData","to":"symbol-name:decodeURIComponent","kind":"calls","metadata":{"unresolvedName":"decodeURIComponent"}},{"from":"/workspace/apps/web/src/utils/terminal-protocol.ts:decodeInputData","to":"symbol-name:escape","kind":"calls","metadata":{"unresolvedName":"escape"}},{"from":"/workspace/apps/web/src/utils/terminal-protocol.ts:decodeInputData","to":"symbol-name:atob","kind":"calls","metadata":{"unresolvedName":"atob"}},{"from":"file:/workspace/apps/web/src/utils/terminal-protocol.ts","to":"/workspace/apps/web/src/utils/terminal-protocol.ts:isServerControlMessage","kind":"contains"},{"from":"file:/workspace/apps/web/src/utils/terminal-protocol.ts","to":"/workspace/apps/web/src/utils/terminal-protocol.ts:isServerControlMessage","kind":"defines"},{"from":"file:/workspace/apps/web/src/utils/time.ts","to":"/workspace/apps/web/src/utils/time.ts:formatRelativeTime","kind":"contains"},{"from":"file:/workspace/apps/web/src/utils/time.ts","to":"/workspace/apps/web/src/utils/time.ts:formatRelativeTime","kind":"defines"},{"from":"/workspace/apps/web/src/utils/time.ts:formatRelativeTime","to":"external:Math.max","kind":"calls","metadata":{"unresolvedName":"Math.max"}},{"from":"/workspace/apps/web/src/utils/time.ts:formatRelativeTime","to":"external:Math.floor","kind":"calls","metadata":{"unresolvedName":"Math.floor"}},{"from":"/workspace/apps/web/src/utils/time.ts:formatRelativeTime","to":"external:now.getTime","kind":"calls","metadata":{"unresolvedName":"now.getTime"}},{"from":"/workspace/apps/web/src/utils/time.ts:formatRelativeTime","to":"external:date.getTime","kind":"calls","metadata":{"unresolvedName":"date.getTime"}},{"from":"file:/workspace/apps/web/vite.config.ts","to":"external:path","kind":"imports"},{"from":"file:/workspace/apps/web/vite.config.ts","to":"external:vite","kind":"imports"},{"from":"file:/workspace/apps/web/vite.config.ts","to":"external:@vitejs/plugin-react","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"file:/workspace/apps/web/src/api/client.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"file:/workspace/apps/web/src/types/git-repository.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:parseGitUrl","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:parseGitUrl","kind":"defines"},{"from":"/workspace/apps/web/src/api/git-repositories.ts:parseGitUrl","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:listRepositories","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:listRepositories","kind":"defines"},{"from":"/workspace/apps/web/src/api/git-repositories.ts:listRepositories","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:listRepositoryBranches","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:listRepositoryBranches","kind":"defines"},{"from":"/workspace/apps/web/src/api/git-repositories.ts:listRepositoryBranches","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:createRepository","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:createRepository","kind":"defines"},{"from":"/workspace/apps/web/src/api/git-repositories.ts:createRepository","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:deleteRepository","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:deleteRepository","kind":"defines"},{"from":"/workspace/apps/web/src/api/git-repositories.ts:deleteRepository","to":"external:apiClient.delete","kind":"calls","metadata":{"unresolvedName":"apiClient.delete"}},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:getRepositoryHistory","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:getRepositoryHistory","kind":"defines"},{"from":"/workspace/apps/web/src/api/git-repositories.ts:getRepositoryHistory","to":"external:searchParams.set","kind":"calls","metadata":{"unresolvedName":"searchParams.set"}},{"from":"/workspace/apps/web/src/api/git-repositories.ts:getRepositoryHistory","to":"symbol-name:String","kind":"calls","metadata":{"unresolvedName":"String"}},{"from":"/workspace/apps/web/src/api/git-repositories.ts:getRepositoryHistory","to":"external:searchParams.toString","kind":"calls","metadata":{"unresolvedName":"searchParams.toString"}},{"from":"/workspace/apps/web/src/api/git-repositories.ts:getRepositoryHistory","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:getCommitDetail","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:getCommitDetail","kind":"defines"},{"from":"/workspace/apps/web/src/api/git-repositories.ts:getCommitDetail","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:getRepositoryStatus","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:getRepositoryStatus","kind":"defines"},{"from":"/workspace/apps/web/src/api/git-repositories.ts:getRepositoryStatus","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:createBranch","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:createBranch","kind":"defines"},{"from":"/workspace/apps/web/src/api/git-repositories.ts:createBranch","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:deleteBranch","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:deleteBranch","kind":"defines"},{"from":"/workspace/apps/web/src/api/git-repositories.ts:deleteBranch","to":"external:apiClient.delete","kind":"calls","metadata":{"unresolvedName":"apiClient.delete"}},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:checkoutBranch","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:checkoutBranch","kind":"defines"},{"from":"/workspace/apps/web/src/api/git-repositories.ts:checkoutBranch","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:commitChanges","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:commitChanges","kind":"defines"},{"from":"/workspace/apps/web/src/api/git-repositories.ts:commitChanges","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:fetchRepository","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:fetchRepository","kind":"defines"},{"from":"/workspace/apps/web/src/api/git-repositories.ts:fetchRepository","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:pullRepository","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:pullRepository","kind":"defines"},{"from":"/workspace/apps/web/src/api/git-repositories.ts:pullRepository","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:pushRepository","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:pushRepository","kind":"defines"},{"from":"/workspace/apps/web/src/api/git-repositories.ts:pushRepository","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:mergeBranches","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/git-repositories.ts","to":"/workspace/apps/web/src/api/git-repositories.ts:mergeBranches","kind":"defines"},{"from":"/workspace/apps/web/src/api/git-repositories.ts:mergeBranches","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"/workspace/apps/web/src/components/features/git/CommitPanel.tsx:CommitPanel","to":"/workspace/apps/web/src/api/git-repositories.ts:commitChanges","kind":"calls","metadata":{"unresolvedName":"commitChanges"}},{"from":"/workspace/apps/web/src/components/features/git/CommitPanel.tsx:handleCommit","to":"/workspace/apps/web/src/api/git-repositories.ts:commitChanges","kind":"calls","metadata":{"unresolvedName":"commitChanges"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:GitToolbar","to":"/workspace/apps/web/src/api/git-repositories.ts:getRepositoryStatus","kind":"calls","metadata":{"unresolvedName":"getRepositoryStatus"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:GitToolbar","to":"/workspace/apps/web/src/api/git-repositories.ts:fetchRepository","kind":"calls","metadata":{"unresolvedName":"fetchRepository"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:GitToolbar","to":"/workspace/apps/web/src/api/git-repositories.ts:pullRepository","kind":"calls","metadata":{"unresolvedName":"pullRepository"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:GitToolbar","to":"/workspace/apps/web/src/api/git-repositories.ts:pushRepository","kind":"calls","metadata":{"unresolvedName":"pushRepository"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:GitToolbar","to":"/workspace/apps/web/src/api/git-repositories.ts:checkoutBranch","kind":"calls","metadata":{"unresolvedName":"checkoutBranch"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:GitToolbar","to":"/workspace/apps/web/src/api/git-repositories.ts:createBranch","kind":"calls","metadata":{"unresolvedName":"createBranch"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:","to":"/workspace/apps/web/src/api/git-repositories.ts:getRepositoryStatus","kind":"calls","metadata":{"unresolvedName":"getRepositoryStatus"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handleFetch","to":"/workspace/apps/web/src/api/git-repositories.ts:fetchRepository","kind":"calls","metadata":{"unresolvedName":"fetchRepository"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handlePull","to":"/workspace/apps/web/src/api/git-repositories.ts:pullRepository","kind":"calls","metadata":{"unresolvedName":"pullRepository"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handlePush","to":"/workspace/apps/web/src/api/git-repositories.ts:pushRepository","kind":"calls","metadata":{"unresolvedName":"pushRepository"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handleCheckout","to":"/workspace/apps/web/src/api/git-repositories.ts:checkoutBranch","kind":"calls","metadata":{"unresolvedName":"checkoutBranch"}},{"from":"/workspace/apps/web/src/components/features/git/GitToolbar.tsx:handleCreateBranch","to":"/workspace/apps/web/src/api/git-repositories.ts:createBranch","kind":"calls","metadata":{"unresolvedName":"createBranch"}},{"from":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:MergeDialog","to":"/workspace/apps/web/src/api/git-repositories.ts:mergeBranches","kind":"calls","metadata":{"unresolvedName":"mergeBranches"}},{"from":"/workspace/apps/web/src/components/features/git/MergeDialog.tsx:handleMerge","to":"/workspace/apps/web/src/api/git-repositories.ts:mergeBranches","kind":"calls","metadata":{"unresolvedName":"mergeBranches"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:RepositoriesSettingsTab","to":"/workspace/apps/web/src/api/git-repositories.ts:listRepositories","kind":"calls","metadata":{"unresolvedName":"listRepositories"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:RepositoriesSettingsTab","to":"/workspace/apps/web/src/api/git-repositories.ts:deleteRepository","kind":"calls","metadata":{"unresolvedName":"deleteRepository"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:","to":"/workspace/apps/web/src/api/git-repositories.ts:listRepositories","kind":"calls","metadata":{"unresolvedName":"listRepositories"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoriesSettingsTab.tsx:handleDelete","to":"/workspace/apps/web/src/api/git-repositories.ts:deleteRepository","kind":"calls","metadata":{"unresolvedName":"deleteRepository"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:RepositoryCreateDialog","to":"/workspace/apps/web/src/api/git-repositories.ts:parseGitUrl","kind":"calls","metadata":{"unresolvedName":"parseGitUrl"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:RepositoryCreateDialog","to":"/workspace/apps/web/src/api/git-repositories.ts:createRepository","kind":"calls","metadata":{"unresolvedName":"createRepository"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:","to":"/workspace/apps/web/src/api/git-repositories.ts:parseGitUrl","kind":"calls","metadata":{"unresolvedName":"parseGitUrl"}},{"from":"/workspace/apps/web/src/components/features/project/RepositoryCreateDialog.tsx:handleSubmit","to":"/workspace/apps/web/src/api/git-repositories.ts:createRepository","kind":"calls","metadata":{"unresolvedName":"createRepository"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:CreateSessionForm","to":"/workspace/apps/web/src/api/git-repositories.ts:listRepositories","kind":"calls","metadata":{"unresolvedName":"listRepositories"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:","to":"/workspace/apps/web/src/api/git-repositories.ts:listRepositories","kind":"calls","metadata":{"unresolvedName":"listRepositories"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:loadRepos","to":"/workspace/apps/web/src/api/git-repositories.ts:listRepositories","kind":"calls","metadata":{"unresolvedName":"listRepositories"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:WorkspaceCreateForm","to":"/workspace/apps/web/src/api/git-repositories.ts:listRepositories","kind":"calls","metadata":{"unresolvedName":"listRepositories"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:","to":"/workspace/apps/web/src/api/git-repositories.ts:listRepositories","kind":"calls","metadata":{"unresolvedName":"listRepositories"}},{"from":"/workspace/apps/web/src/components/workspace-create-form.tsx:loadRepos","to":"/workspace/apps/web/src/api/git-repositories.ts:listRepositories","kind":"calls","metadata":{"unresolvedName":"listRepositories"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","to":"/workspace/apps/web/src/api/git-repositories.ts:getRepositoryStatus","kind":"calls","metadata":{"unresolvedName":"getRepositoryStatus"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","to":"/workspace/apps/web/src/api/git-repositories.ts:getRepositoryHistory","kind":"calls","metadata":{"unresolvedName":"getRepositoryHistory"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","to":"/workspace/apps/web/src/api/git-repositories.ts:getCommitDetail","kind":"calls","metadata":{"unresolvedName":"getCommitDetail"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","to":"/workspace/apps/web/src/api/git-repositories.ts:commitChanges","kind":"calls","metadata":{"unresolvedName":"commitChanges"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","to":"/workspace/apps/web/src/api/git-repositories.ts:pushRepository","kind":"calls","metadata":{"unresolvedName":"pushRepository"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","to":"/workspace/apps/web/src/api/git-repositories.ts:pullRepository","kind":"calls","metadata":{"unresolvedName":"pullRepository"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","to":"/workspace/apps/web/src/api/git-repositories.ts:fetchRepository","kind":"calls","metadata":{"unresolvedName":"fetchRepository"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","to":"/workspace/apps/web/src/api/git-repositories.ts:checkoutBranch","kind":"calls","metadata":{"unresolvedName":"checkoutBranch"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","to":"/workspace/apps/web/src/api/git-repositories.ts:createBranch","kind":"calls","metadata":{"unresolvedName":"createBranch"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","to":"/workspace/apps/web/src/api/git-repositories.ts:deleteBranch","kind":"calls","metadata":{"unresolvedName":"deleteBranch"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:useGitRepo","to":"/workspace/apps/web/src/api/git-repositories.ts:mergeBranches","kind":"calls","metadata":{"unresolvedName":"mergeBranches"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"/workspace/apps/web/src/api/git-repositories.ts:getRepositoryStatus","kind":"calls","metadata":{"unresolvedName":"getRepositoryStatus"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"/workspace/apps/web/src/api/git-repositories.ts:getRepositoryHistory","kind":"calls","metadata":{"unresolvedName":"getRepositoryHistory"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"/workspace/apps/web/src/api/git-repositories.ts:getCommitDetail","kind":"calls","metadata":{"unresolvedName":"getCommitDetail"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"/workspace/apps/web/src/api/git-repositories.ts:commitChanges","kind":"calls","metadata":{"unresolvedName":"commitChanges"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"/workspace/apps/web/src/api/git-repositories.ts:pushRepository","kind":"calls","metadata":{"unresolvedName":"pushRepository"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"/workspace/apps/web/src/api/git-repositories.ts:pullRepository","kind":"calls","metadata":{"unresolvedName":"pullRepository"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"/workspace/apps/web/src/api/git-repositories.ts:fetchRepository","kind":"calls","metadata":{"unresolvedName":"fetchRepository"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"/workspace/apps/web/src/api/git-repositories.ts:checkoutBranch","kind":"calls","metadata":{"unresolvedName":"checkoutBranch"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"/workspace/apps/web/src/api/git-repositories.ts:createBranch","kind":"calls","metadata":{"unresolvedName":"createBranch"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"/workspace/apps/web/src/api/git-repositories.ts:deleteBranch","kind":"calls","metadata":{"unresolvedName":"deleteBranch"}},{"from":"/workspace/apps/web/src/hooks/use-git-repo.ts:","to":"/workspace/apps/web/src/api/git-repositories.ts:mergeBranches","kind":"calls","metadata":{"unresolvedName":"mergeBranches"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:HomePage","to":"/workspace/apps/web/src/api/git-repositories.ts:listRepositories","kind":"calls","metadata":{"unresolvedName":"listRepositories"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:","to":"/workspace/apps/web/src/api/git-repositories.ts:listRepositories","kind":"calls","metadata":{"unresolvedName":"listRepositories"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:GitHistoryPage","to":"/workspace/apps/web/src/api/git-repositories.ts:getRepositoryHistory","kind":"calls","metadata":{"unresolvedName":"getRepositoryHistory"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:GitHistoryPage","to":"/workspace/apps/web/src/api/git-repositories.ts:getCommitDetail","kind":"calls","metadata":{"unresolvedName":"getCommitDetail"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:","to":"/workspace/apps/web/src/api/git-repositories.ts:getRepositoryHistory","kind":"calls","metadata":{"unresolvedName":"getRepositoryHistory"}},{"from":"/workspace/apps/web/src/pages/GitHistoryPage.tsx:handleCommitClick","to":"/workspace/apps/web/src/api/git-repositories.ts:getCommitDetail","kind":"calls","metadata":{"unresolvedName":"getCommitDetail"}},{"from":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:GitRepositoriesPage","to":"/workspace/apps/web/src/api/git-repositories.ts:listRepositories","kind":"calls","metadata":{"unresolvedName":"listRepositories"}},{"from":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:GitRepositoriesPage","to":"/workspace/apps/web/src/api/git-repositories.ts:deleteRepository","kind":"calls","metadata":{"unresolvedName":"deleteRepository"}},{"from":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:","to":"/workspace/apps/web/src/api/git-repositories.ts:listRepositories","kind":"calls","metadata":{"unresolvedName":"listRepositories"}},{"from":"/workspace/apps/web/src/pages/GitRepositoriesPage.tsx:handleDelete","to":"/workspace/apps/web/src/api/git-repositories.ts:deleteRepository","kind":"calls","metadata":{"unresolvedName":"deleteRepository"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","to":"/workspace/apps/web/src/api/git-repositories.ts:listRepositories","kind":"calls","metadata":{"unresolvedName":"listRepositories"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:RepoWorkspace","to":"/workspace/apps/web/src/api/git-repositories.ts:getRepositoryStatus","kind":"calls","metadata":{"unresolvedName":"getRepositoryStatus"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","to":"/workspace/apps/web/src/api/git-repositories.ts:listRepositories","kind":"calls","metadata":{"unresolvedName":"listRepositories"}},{"from":"/workspace/apps/web/src/pages/RepoWorkspacePage.tsx:","to":"/workspace/apps/web/src/api/git-repositories.ts:getRepositoryStatus","kind":"calls","metadata":{"unresolvedName":"getRepositoryStatus"}},{"from":"file:/workspace/apps/web/src/api/sessions.ts","to":"file:/workspace/apps/web/src/api/client.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/sessions.ts","to":"file:/workspace/apps/web/src/types/session.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/sessions.ts","to":"file:/workspace/apps/web/src/types/tool-instance.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/api/sessions.ts","to":"/workspace/apps/web/src/api/sessions.ts:listInstances","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/sessions.ts","to":"/workspace/apps/web/src/api/sessions.ts:listInstances","kind":"defines"},{"from":"/workspace/apps/web/src/api/sessions.ts:listInstances","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/sessions.ts","to":"/workspace/apps/web/src/api/sessions.ts:createInstance","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/sessions.ts","to":"/workspace/apps/web/src/api/sessions.ts:createInstance","kind":"defines"},{"from":"/workspace/apps/web/src/api/sessions.ts:createInstance","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/sessions.ts","to":"/workspace/apps/web/src/api/sessions.ts:startInstance","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/sessions.ts","to":"/workspace/apps/web/src/api/sessions.ts:startInstance","kind":"defines"},{"from":"/workspace/apps/web/src/api/sessions.ts:startInstance","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/sessions.ts","to":"/workspace/apps/web/src/api/sessions.ts:stopInstance","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/sessions.ts","to":"/workspace/apps/web/src/api/sessions.ts:stopInstance","kind":"defines"},{"from":"/workspace/apps/web/src/api/sessions.ts:stopInstance","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/sessions.ts","to":"/workspace/apps/web/src/api/sessions.ts:restartInstance","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/sessions.ts","to":"/workspace/apps/web/src/api/sessions.ts:restartInstance","kind":"defines"},{"from":"/workspace/apps/web/src/api/sessions.ts:restartInstance","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"file:/workspace/apps/web/src/api/sessions.ts","to":"/workspace/apps/web/src/api/sessions.ts:deleteInstance","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/sessions.ts","to":"/workspace/apps/web/src/api/sessions.ts:deleteInstance","kind":"defines"},{"from":"/workspace/apps/web/src/api/sessions.ts:deleteInstance","to":"external:apiClient.delete","kind":"calls","metadata":{"unresolvedName":"apiClient.delete"}},{"from":"file:/workspace/apps/web/src/api/sessions.ts","to":"/workspace/apps/web/src/api/sessions.ts:getUserSessions","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/sessions.ts","to":"/workspace/apps/web/src/api/sessions.ts:getUserSessions","kind":"defines"},{"from":"/workspace/apps/web/src/api/sessions.ts:getUserSessions","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/sessions.ts","to":"/workspace/apps/web/src/api/sessions.ts:checkInstanceHealth","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/sessions.ts","to":"/workspace/apps/web/src/api/sessions.ts:checkInstanceHealth","kind":"defines"},{"from":"/workspace/apps/web/src/api/sessions.ts:checkInstanceHealth","to":"external:apiClient.get","kind":"calls","metadata":{"unresolvedName":"apiClient.get"}},{"from":"file:/workspace/apps/web/src/api/sessions.ts","to":"/workspace/apps/web/src/api/sessions.ts:recreateInstanceTunnel","kind":"contains"},{"from":"file:/workspace/apps/web/src/api/sessions.ts","to":"/workspace/apps/web/src/api/sessions.ts:recreateInstanceTunnel","kind":"defines"},{"from":"/workspace/apps/web/src/api/sessions.ts:recreateInstanceTunnel","to":"external:apiClient.post","kind":"calls","metadata":{"unresolvedName":"apiClient.post"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"/workspace/apps/web/src/api/sessions.ts:createInstance","kind":"calls","metadata":{"unresolvedName":"createInstance"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:CreateSessionForm","to":"/workspace/apps/web/src/api/sessions.ts:startInstance","kind":"calls","metadata":{"unresolvedName":"startInstance"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:handleSubmit","to":"/workspace/apps/web/src/api/sessions.ts:createInstance","kind":"calls","metadata":{"unresolvedName":"createInstance"}},{"from":"/workspace/apps/web/src/components/create-session-form.tsx:handleSubmit","to":"/workspace/apps/web/src/api/sessions.ts:startInstance","kind":"calls","metadata":{"unresolvedName":"startInstance"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:CreateSessionForm","to":"/workspace/apps/web/src/api/sessions.ts:createInstance","kind":"calls","metadata":{"unresolvedName":"createInstance"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:CreateSessionForm","to":"/workspace/apps/web/src/api/sessions.ts:startInstance","kind":"calls","metadata":{"unresolvedName":"startInstance"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:handleCreate","to":"/workspace/apps/web/src/api/sessions.ts:createInstance","kind":"calls","metadata":{"unresolvedName":"createInstance"}},{"from":"/workspace/apps/web/src/components/features/session/CreateSessionForm.tsx:handleCreate","to":"/workspace/apps/web/src/api/sessions.ts:startInstance","kind":"calls","metadata":{"unresolvedName":"startInstance"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"/workspace/apps/web/src/api/sessions.ts:listInstances","kind":"calls","metadata":{"unresolvedName":"listInstances"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"/workspace/apps/web/src/api/sessions.ts:checkInstanceHealth","kind":"calls","metadata":{"unresolvedName":"checkInstanceHealth"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"/workspace/apps/web/src/api/sessions.ts:createInstance","kind":"calls","metadata":{"unresolvedName":"createInstance"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"/workspace/apps/web/src/api/sessions.ts:startInstance","kind":"calls","metadata":{"unresolvedName":"startInstance"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"/workspace/apps/web/src/api/sessions.ts:stopInstance","kind":"calls","metadata":{"unresolvedName":"stopInstance"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"/workspace/apps/web/src/api/sessions.ts:restartInstance","kind":"calls","metadata":{"unresolvedName":"restartInstance"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"/workspace/apps/web/src/api/sessions.ts:deleteInstance","kind":"calls","metadata":{"unresolvedName":"deleteInstance"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:InstanceList","to":"/workspace/apps/web/src/api/sessions.ts:recreateInstanceTunnel","kind":"calls","metadata":{"unresolvedName":"recreateInstanceTunnel"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:","to":"/workspace/apps/web/src/api/sessions.ts:listInstances","kind":"calls","metadata":{"unresolvedName":"listInstances"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:","to":"/workspace/apps/web/src/api/sessions.ts:checkInstanceHealth","kind":"calls","metadata":{"unresolvedName":"checkInstanceHealth"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:checkHealth","to":"/workspace/apps/web/src/api/sessions.ts:checkInstanceHealth","kind":"calls","metadata":{"unresolvedName":"checkInstanceHealth"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleCreate","to":"/workspace/apps/web/src/api/sessions.ts:createInstance","kind":"calls","metadata":{"unresolvedName":"createInstance"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleStart","to":"/workspace/apps/web/src/api/sessions.ts:startInstance","kind":"calls","metadata":{"unresolvedName":"startInstance"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleStop","to":"/workspace/apps/web/src/api/sessions.ts:stopInstance","kind":"calls","metadata":{"unresolvedName":"stopInstance"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleRestart","to":"/workspace/apps/web/src/api/sessions.ts:restartInstance","kind":"calls","metadata":{"unresolvedName":"restartInstance"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleDelete","to":"/workspace/apps/web/src/api/sessions.ts:deleteInstance","kind":"calls","metadata":{"unresolvedName":"deleteInstance"}},{"from":"/workspace/apps/web/src/components/features/session/InstanceList.tsx:handleRecreateTunnel","to":"/workspace/apps/web/src/api/sessions.ts:recreateInstanceTunnel","kind":"calls","metadata":{"unresolvedName":"recreateInstanceTunnel"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:SessionList","to":"/workspace/apps/web/src/api/sessions.ts:checkInstanceHealth","kind":"calls","metadata":{"unresolvedName":"checkInstanceHealth"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:SessionList","to":"/workspace/apps/web/src/api/sessions.ts:stopInstance","kind":"calls","metadata":{"unresolvedName":"stopInstance"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:SessionList","to":"/workspace/apps/web/src/api/sessions.ts:deleteInstance","kind":"calls","metadata":{"unresolvedName":"deleteInstance"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:SessionList","to":"/workspace/apps/web/src/api/sessions.ts:recreateInstanceTunnel","kind":"calls","metadata":{"unresolvedName":"recreateInstanceTunnel"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","to":"/workspace/apps/web/src/api/sessions.ts:checkInstanceHealth","kind":"calls","metadata":{"unresolvedName":"checkInstanceHealth"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:checkHealth","to":"/workspace/apps/web/src/api/sessions.ts:checkInstanceHealth","kind":"calls","metadata":{"unresolvedName":"checkInstanceHealth"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","to":"/workspace/apps/web/src/api/sessions.ts:stopInstance","kind":"calls","metadata":{"unresolvedName":"stopInstance"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","to":"/workspace/apps/web/src/api/sessions.ts:deleteInstance","kind":"calls","metadata":{"unresolvedName":"deleteInstance"}},{"from":"/workspace/apps/web/src/components/features/session/SessionList.tsx:","to":"/workspace/apps/web/src/api/sessions.ts:recreateInstanceTunnel","kind":"calls","metadata":{"unresolvedName":"recreateInstanceTunnel"}},{"from":"/workspace/apps/web/src/components/layout/AppShell.tsx:AppShell","to":"/workspace/apps/web/src/api/sessions.ts:getUserSessions","kind":"calls","metadata":{"unresolvedName":"getUserSessions"}},{"from":"/workspace/apps/web/src/components/layout/AppShell.tsx:","to":"/workspace/apps/web/src/api/sessions.ts:getUserSessions","kind":"calls","metadata":{"unresolvedName":"getUserSessions"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"/workspace/apps/web/src/api/sessions.ts:createInstance","kind":"calls","metadata":{"unresolvedName":"createInstance"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:ToolStarter","to":"/workspace/apps/web/src/api/sessions.ts:startInstance","kind":"calls","metadata":{"unresolvedName":"startInstance"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:","to":"/workspace/apps/web/src/api/sessions.ts:createInstance","kind":"calls","metadata":{"unresolvedName":"createInstance"}},{"from":"/workspace/apps/web/src/components/tool-starter.tsx:","to":"/workspace/apps/web/src/api/sessions.ts:startInstance","kind":"calls","metadata":{"unresolvedName":"startInstance"}},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:useDashboardActions","to":"/workspace/apps/web/src/api/sessions.ts:createInstance","kind":"calls","metadata":{"unresolvedName":"createInstance"}},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:useDashboardActions","to":"/workspace/apps/web/src/api/sessions.ts:startInstance","kind":"calls","metadata":{"unresolvedName":"startInstance"}},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:useDashboardActions","to":"/workspace/apps/web/src/api/sessions.ts:stopInstance","kind":"calls","metadata":{"unresolvedName":"stopInstance"}},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:useDashboardActions","to":"/workspace/apps/web/src/api/sessions.ts:deleteInstance","kind":"calls","metadata":{"unresolvedName":"deleteInstance"}},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:useDashboardActions","to":"/workspace/apps/web/src/api/sessions.ts:recreateInstanceTunnel","kind":"calls","metadata":{"unresolvedName":"recreateInstanceTunnel"}},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:","to":"/workspace/apps/web/src/api/sessions.ts:createInstance","kind":"calls","metadata":{"unresolvedName":"createInstance"}},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:","to":"/workspace/apps/web/src/api/sessions.ts:startInstance","kind":"calls","metadata":{"unresolvedName":"startInstance"}},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:","to":"/workspace/apps/web/src/api/sessions.ts:stopInstance","kind":"calls","metadata":{"unresolvedName":"stopInstance"}},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:","to":"/workspace/apps/web/src/api/sessions.ts:deleteInstance","kind":"calls","metadata":{"unresolvedName":"deleteInstance"}},{"from":"/workspace/apps/web/src/hooks/use-dashboard-actions.ts:","to":"/workspace/apps/web/src/api/sessions.ts:recreateInstanceTunnel","kind":"calls","metadata":{"unresolvedName":"recreateInstanceTunnel"}},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:useInstanceActions","to":"/workspace/apps/web/src/api/sessions.ts:startInstance","kind":"calls","metadata":{"unresolvedName":"startInstance"}},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:useInstanceActions","to":"/workspace/apps/web/src/api/sessions.ts:stopInstance","kind":"calls","metadata":{"unresolvedName":"stopInstance"}},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:useInstanceActions","to":"/workspace/apps/web/src/api/sessions.ts:deleteInstance","kind":"calls","metadata":{"unresolvedName":"deleteInstance"}},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:useInstanceActions","to":"/workspace/apps/web/src/api/sessions.ts:recreateInstanceTunnel","kind":"calls","metadata":{"unresolvedName":"recreateInstanceTunnel"}},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","to":"/workspace/apps/web/src/api/sessions.ts:startInstance","kind":"calls","metadata":{"unresolvedName":"startInstance"}},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","to":"/workspace/apps/web/src/api/sessions.ts:stopInstance","kind":"calls","metadata":{"unresolvedName":"stopInstance"}},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","to":"/workspace/apps/web/src/api/sessions.ts:deleteInstance","kind":"calls","metadata":{"unresolvedName":"deleteInstance"}},{"from":"/workspace/apps/web/src/hooks/use-instance-actions.ts:","to":"/workspace/apps/web/src/api/sessions.ts:recreateInstanceTunnel","kind":"calls","metadata":{"unresolvedName":"recreateInstanceTunnel"}},{"from":"/workspace/apps/web/src/hooks/use-start-tool.ts:useStartTool","to":"/workspace/apps/web/src/api/sessions.ts:createInstance","kind":"calls","metadata":{"unresolvedName":"createInstance"}},{"from":"/workspace/apps/web/src/hooks/use-start-tool.ts:useStartTool","to":"/workspace/apps/web/src/api/sessions.ts:startInstance","kind":"calls","metadata":{"unresolvedName":"startInstance"}},{"from":"/workspace/apps/web/src/hooks/use-start-tool.ts:","to":"/workspace/apps/web/src/api/sessions.ts:createInstance","kind":"calls","metadata":{"unresolvedName":"createInstance"}},{"from":"/workspace/apps/web/src/hooks/use-start-tool.ts:","to":"/workspace/apps/web/src/api/sessions.ts:startInstance","kind":"calls","metadata":{"unresolvedName":"startInstance"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:HomePage","to":"/workspace/apps/web/src/api/sessions.ts:getUserSessions","kind":"calls","metadata":{"unresolvedName":"getUserSessions"}},{"from":"/workspace/apps/web/src/pages/DashboardPage.tsx:","to":"/workspace/apps/web/src/api/sessions.ts:getUserSessions","kind":"calls","metadata":{"unresolvedName":"getUserSessions"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:SessionsPage","to":"/workspace/apps/web/src/api/sessions.ts:getUserSessions","kind":"calls","metadata":{"unresolvedName":"getUserSessions"}},{"from":"/workspace/apps/web/src/pages/SessionsPage.tsx:","to":"/workspace/apps/web/src/api/sessions.ts:getUserSessions","kind":"calls","metadata":{"unresolvedName":"getUserSessions"}},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"external:react","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"external:xterm","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"external:xterm-addon-fit","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"external:xterm-addon-serialize","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"external:xterm-addon-web-links","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"external:xterm/css/xterm.css","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"file:/workspace/apps/web/src/hooks/use-terminal-connection.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"file:/workspace/apps/web/src/types/terminal.ts","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.module.css","kind":"imports"},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:getStatusText","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:getStatusText","kind":"defines"},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:TerminalComponent","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:TerminalComponent","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:TerminalComponent","to":"symbol-name:useRef","kind":"calls","metadata":{"unresolvedName":"useRef"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:TerminalComponent","to":"symbol-name:useState","kind":"calls","metadata":{"unresolvedName":"useState"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:TerminalComponent","to":"external:document.documentElement.getAttribute","kind":"calls","metadata":{"unresolvedName":"document.documentElement.getAttribute"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:TerminalComponent","to":"external:window.matchMedia","kind":"calls","metadata":{"unresolvedName":"window.matchMedia"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:TerminalComponent","to":"symbol-name:useCallback","kind":"calls","metadata":{"unresolvedName":"useCallback"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:TerminalComponent","to":"external:xtermRef.current?.write","kind":"calls","metadata":{"unresolvedName":"xtermRef.current?.write"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:TerminalComponent","to":"external:serializeAddonRef.current?.serialize","kind":"calls","metadata":{"unresolvedName":"serializeAddonRef.current?.serialize"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:TerminalComponent","to":"symbol-name:setSessionEnded","kind":"calls","metadata":{"unresolvedName":"setSessionEnded"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:TerminalComponent","to":"/workspace/apps/web/src/hooks/use-terminal-connection.ts:useTerminalConnection","kind":"calls","metadata":{"unresolvedName":"useTerminalConnection"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:TerminalComponent","to":"symbol-name:useEffect","kind":"calls","metadata":{"unresolvedName":"useEffect"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:TerminalComponent","to":"external:term.loadAddon","kind":"calls","metadata":{"unresolvedName":"term.loadAddon"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:TerminalComponent","to":"external:term.open","kind":"calls","metadata":{"unresolvedName":"term.open"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:TerminalComponent","to":"external:fitAddon.fit","kind":"calls","metadata":{"unresolvedName":"fitAddon.fit"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:TerminalComponent","to":"external:term.onData","kind":"calls","metadata":{"unresolvedName":"term.onData"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:TerminalComponent","to":"symbol-name:sendInput","kind":"calls","metadata":{"unresolvedName":"sendInput"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:TerminalComponent","to":"symbol-name:clearTimeout","kind":"calls","metadata":{"unresolvedName":"clearTimeout"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:TerminalComponent","to":"symbol-name:setTimeout","kind":"calls","metadata":{"unresolvedName":"setTimeout"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:TerminalComponent","to":"external:Math.abs","kind":"calls","metadata":{"unresolvedName":"Math.abs"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:TerminalComponent","to":"symbol-name:sendResize","kind":"calls","metadata":{"unresolvedName":"sendResize"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:TerminalComponent","to":"external:resizeObserver.observe","kind":"calls","metadata":{"unresolvedName":"resizeObserver.observe"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:TerminalComponent","to":"external:disposable.dispose","kind":"calls","metadata":{"unresolvedName":"disposable.dispose"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:TerminalComponent","to":"external:resizeObserver.disconnect","kind":"calls","metadata":{"unresolvedName":"resizeObserver.disconnect"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:TerminalComponent","to":"external:term.dispose","kind":"calls","metadata":{"unresolvedName":"term.dispose"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:TerminalComponent","to":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:getStatusText","kind":"calls","metadata":{"unresolvedName":"getStatusText"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:TerminalComponent","to":"symbol-name:reconnect","kind":"calls","metadata":{"unresolvedName":"reconnect"}},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","kind":"defines"},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"external:xtermRef.current?.write","kind":"calls","metadata":{"unresolvedName":"xtermRef.current?.write"}},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"external:serializeAddonRef.current?.serialize","kind":"calls","metadata":{"unresolvedName":"serializeAddonRef.current?.serialize"}},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"external:xtermRef.current?.write","kind":"calls","metadata":{"unresolvedName":"xtermRef.current?.write"}},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"symbol-name:setSessionEnded","kind":"calls","metadata":{"unresolvedName":"setSessionEnded"}},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"external:term.loadAddon","kind":"calls","metadata":{"unresolvedName":"term.loadAddon"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"external:term.open","kind":"calls","metadata":{"unresolvedName":"term.open"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"external:fitAddon.fit","kind":"calls","metadata":{"unresolvedName":"fitAddon.fit"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"external:term.onData","kind":"calls","metadata":{"unresolvedName":"term.onData"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"symbol-name:sendInput","kind":"calls","metadata":{"unresolvedName":"sendInput"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"symbol-name:clearTimeout","kind":"calls","metadata":{"unresolvedName":"clearTimeout"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"symbol-name:setTimeout","kind":"calls","metadata":{"unresolvedName":"setTimeout"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"external:Math.abs","kind":"calls","metadata":{"unresolvedName":"Math.abs"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"symbol-name:sendResize","kind":"calls","metadata":{"unresolvedName":"sendResize"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"external:resizeObserver.observe","kind":"calls","metadata":{"unresolvedName":"resizeObserver.observe"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"external:disposable.dispose","kind":"calls","metadata":{"unresolvedName":"disposable.dispose"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"external:resizeObserver.disconnect","kind":"calls","metadata":{"unresolvedName":"resizeObserver.disconnect"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"external:term.dispose","kind":"calls","metadata":{"unresolvedName":"term.dispose"}},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"symbol-name:sendInput","kind":"calls","metadata":{"unresolvedName":"sendInput"}},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"symbol-name:clearTimeout","kind":"calls","metadata":{"unresolvedName":"clearTimeout"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"symbol-name:setTimeout","kind":"calls","metadata":{"unresolvedName":"setTimeout"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"external:Math.abs","kind":"calls","metadata":{"unresolvedName":"Math.abs"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"external:fitAddon.fit","kind":"calls","metadata":{"unresolvedName":"fitAddon.fit"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"symbol-name:sendResize","kind":"calls","metadata":{"unresolvedName":"sendResize"}},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"external:Math.abs","kind":"calls","metadata":{"unresolvedName":"Math.abs"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"external:fitAddon.fit","kind":"calls","metadata":{"unresolvedName":"fitAddon.fit"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"symbol-name:sendResize","kind":"calls","metadata":{"unresolvedName":"sendResize"}},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"symbol-name:clearTimeout","kind":"calls","metadata":{"unresolvedName":"clearTimeout"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"external:disposable.dispose","kind":"calls","metadata":{"unresolvedName":"disposable.dispose"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"external:resizeObserver.disconnect","kind":"calls","metadata":{"unresolvedName":"resizeObserver.disconnect"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"external:term.dispose","kind":"calls","metadata":{"unresolvedName":"term.dispose"}},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"symbol-name:sendResize","kind":"calls","metadata":{"unresolvedName":"sendResize"}},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","kind":"contains"},{"from":"file:/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx","to":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","kind":"defines"},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"symbol-name:setSessionEnded","kind":"calls","metadata":{"unresolvedName":"setSessionEnded"}},{"from":"/workspace/apps/web/src/components/features/terminal/TerminalComponent.tsx:","to":"symbol-name:reconnect","kind":"calls","metadata":{"unresolvedName":"reconnect"}}]} \ No newline at end of file diff --git a/apps/web/package-lock.json b/apps/web/package-lock.json index 53e9cdf..8c3190d 100644 --- a/apps/web/package-lock.json +++ b/apps/web/package-lock.json @@ -19,8 +19,8 @@ "tailwindcss": "^3.3.0", "xterm": "^5.3.0", "xterm-addon-fit": "^0.8.0", - "xterm-addon-web-links": "^0.9.0", - "xterm-addon-webgl": "^0.16.0" + "xterm-addon-serialize": "^0.11.0", + "xterm-addon-web-links": "^0.9.0" }, "devDependencies": { "@testing-library/jest-dom": "^6.9.1", @@ -1391,6 +1391,9 @@ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1408,6 +1411,9 @@ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1425,6 +1431,9 @@ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1442,6 +1451,9 @@ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1459,6 +1471,9 @@ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1476,6 +1491,9 @@ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1654,6 +1672,9 @@ "arm" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1668,6 +1689,9 @@ "arm" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1682,6 +1706,9 @@ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1696,6 +1723,9 @@ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1710,6 +1740,9 @@ "loong64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1724,6 +1757,9 @@ "loong64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1738,6 +1774,9 @@ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1752,6 +1791,9 @@ "ppc64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1766,6 +1808,9 @@ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1780,6 +1825,9 @@ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1794,6 +1842,9 @@ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1808,6 +1859,9 @@ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1822,6 +1876,9 @@ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -4304,6 +4361,9 @@ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -4325,6 +4385,9 @@ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -4346,6 +4409,9 @@ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -4367,6 +4433,9 @@ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -6304,21 +6373,21 @@ "xterm": "^5.0.0" } }, - "node_modules/xterm-addon-web-links": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/xterm-addon-web-links/-/xterm-addon-web-links-0.9.0.tgz", - "integrity": "sha512-LIzi4jBbPlrKMZF3ihoyqayWyTXAwGfu4yprz1aK2p71e9UKXN6RRzVONR0L+Zd+Ik5tPVI9bwp9e8fDTQh49Q==", - "deprecated": "This package is now deprecated. Move to @xterm/addon-web-links instead.", + "node_modules/xterm-addon-serialize": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/xterm-addon-serialize/-/xterm-addon-serialize-0.11.0.tgz", + "integrity": "sha512-2CNDnmLdLkNWfsxNFkGsI5FE9W/BbsMzeOrbu59yNqH9L6k1gmL+Ab6VXxEp2NQUJSzaiqi6t0nFR5k5EDkVIg==", + "deprecated": "This package is now deprecated. Move to @xterm/addon-serialize instead.", "license": "MIT", "peerDependencies": { "xterm": "^5.0.0" } }, - "node_modules/xterm-addon-webgl": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0.tgz", - "integrity": "sha512-E8cq1AiqNOv0M/FghPT+zPAEnvIQRDbAbkb04rRYSxUym69elPWVJ4sv22FCLBqM/3LcrmBLl/pELnBebVFKgA==", - "deprecated": "This package is now deprecated. Move to @xterm/addon-webgl instead.", + "node_modules/xterm-addon-web-links": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/xterm-addon-web-links/-/xterm-addon-web-links-0.9.0.tgz", + "integrity": "sha512-LIzi4jBbPlrKMZF3ihoyqayWyTXAwGfu4yprz1aK2p71e9UKXN6RRzVONR0L+Zd+Ik5tPVI9bwp9e8fDTQh49Q==", + "deprecated": "This package is now deprecated. Move to @xterm/addon-web-links instead.", "license": "MIT", "peerDependencies": { "xterm": "^5.0.0" diff --git a/apps/web/package.json b/apps/web/package.json index 9c081ac..a8332f3 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -22,8 +22,8 @@ "tailwindcss": "^3.3.0", "xterm": "^5.3.0", "xterm-addon-fit": "^0.8.0", - "xterm-addon-web-links": "^0.9.0", - "xterm-addon-webgl": "^0.16.0" + "xterm-addon-serialize": "^0.11.0", + "xterm-addon-web-links": "^0.9.0" }, "devDependencies": { "@testing-library/jest-dom": "^6.9.1", diff --git a/apps/web/scripts/check-structure.js b/apps/web/scripts/check-structure.js new file mode 100644 index 0000000..eae68ec --- /dev/null +++ b/apps/web/scripts/check-structure.js @@ -0,0 +1,83 @@ +#!/usr/bin/env node +/* eslint-disable */ +/** + * Verifies repository structure conventions. + * Run with: node scripts/check-structure.js + */ + +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const SRC_DIR = path.join(__dirname, "..", "src"); + +let errors = 0; +let warnings = 0; + +// Known acceptable deviations — documented in naming.md +const OVERSIZE_ALLOWLIST = [ + // Form-heavy admin tabs: 15+ fields each, splitting would create micro-components + "components/features/tool-workshop/ToolTypesTab.tsx", + "components/features/tool-workshop/ToolConfigsTab.tsx", + // Complex terminal hook: WS lifecycle + ping-pong + echo + resize debouncing + "hooks/use-terminal-connection.ts", + // Terminal component: xterm lifecycle + resize observer + overlay UI + "components/features/terminal/TerminalComponent.tsx", + // Instance list with health polling + inline confirmations + "components/features/session/InstanceList.tsx", + // Dialog with form validation + SSH key handling + "components/features/project/RepositoryCreateDialog.tsx", + // Test files: complex test coverage + "hooks/use-terminal-connection.test.ts", + "pages/ToolWorkshopPage.test.tsx", + // Global utility CSS: will be further split in future iteration + "styles/utilities.css", +]; + +function checkFileSize(filePath, maxLines = 300) { + const content = fs.readFileSync(filePath, "utf-8"); + const lines = content.split("\n").length; + const relative = path.relative(SRC_DIR, filePath); + if (lines > maxLines) { + if (OVERSIZE_ALLOWLIST.includes(relative)) { + console.warn(`⚠️ OVERSIZED (${lines} lines, allowlisted): ${relative}`); + warnings++; + } else { + console.error(`❌ OVERSIZED (${lines} lines): ${relative}`); + errors++; + } + } +} + +function walk(dir, callback) { + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + const fullPath = path.join(dir, entry.name); + if (entry.isDirectory()) { + if (entry.name === "node_modules" || entry.name.startsWith(".")) continue; + walk(fullPath, callback); + } else { + callback(fullPath); + } + } +} + +console.log("Checking file sizes...\n"); +walk(SRC_DIR, (filePath) => { + const ext = path.extname(filePath); + if ([".ts", ".tsx", ".py", ".css"].includes(ext)) { + checkFileSize(filePath); + } +}); + +console.log("\n---"); +if (errors === 0 && warnings === 0) { + console.log("✅ All checks passed!"); + process.exit(0); +} else if (errors === 0) { + console.log(`✅ All checks passed with ${warnings} warning(s)`); + process.exit(0); +} else { + console.log(`❌ ${errors} error(s), ${warnings} warning(s)`); + process.exit(1); +} diff --git a/apps/web/src/api/config-folders.test.ts b/apps/web/src/api/config-folders.test.ts new file mode 100644 index 0000000..7b5645a --- /dev/null +++ b/apps/web/src/api/config-folders.test.ts @@ -0,0 +1,131 @@ +import { describe, expect, it, vi } from "vitest"; + +import { + createConfigFolder, + deleteConfigFolder, + listConfigFolders, + updateConfigFolder, +} from "../api/config-folders"; + +const mockGet = vi.fn(); +const mockPost = vi.fn(); +const mockPut = vi.fn(); +const mockDelete = vi.fn(); + +vi.mock("../api/client", () => ({ + apiClient: { + get: (...args: unknown[]) => mockGet(...args), + post: (...args: unknown[]) => mockPost(...args), + put: (...args: unknown[]) => mockPut(...args), + delete: (...args: unknown[]) => mockDelete(...args), + interceptors: { + response: { + use: vi.fn(), + }, + }, + }, + shouldSkipAuthRedirect: vi.fn(() => false), +})); + +describe("config_folders API", () => { + describe("listConfigFolders", () => { + it("returns folders with files and overrides", async () => { + const mockResponse = { + data: [ + { + id: "folder-1", + name: "my-dotfiles", + description: "My personal config files", + mount_path: "/home/user", + files: { ".zshrc": "export ZSH=\"$HOME/.oh-my-zsh\"" }, + project_overrides: {}, + is_active: true, + user_id: "user-1", + created_at: "2024-01-01T00:00:00Z", + updated_at: "2024-01-01T00:00:00Z", + }, + ], + }; + mockGet.mockResolvedValue(mockResponse); + + const result = await listConfigFolders(); + + expect(result[0].name).toBe("my-dotfiles"); + expect(result[0].files).toEqual({ ".zshrc": "export ZSH=\"$HOME/.oh-my-zsh\"" }); + expect(mockGet).toHaveBeenCalledWith("/config-folders"); + }); + }); + + describe("createConfigFolder", () => { + it("creates folder with files", async () => { + const mockResponse = { + data: { + id: "folder-new", + name: "new-folder", + mount_path: "/workspace", + files: { ".env": "API_URL=http://localhost" }, + is_active: true, + user_id: "user-1", + created_at: "2024-01-01T00:00:00Z", + updated_at: "2024-01-01T00:00:00Z", + }, + }; + mockPost.mockResolvedValue(mockResponse); + + const result = await createConfigFolder({ + name: "new-folder", + mount_path: "/workspace", + files: { ".env": "API_URL=http://localhost" }, + }); + + expect(result.name).toBe("new-folder"); + expect(mockPost).toHaveBeenCalledWith( + "/config-folders", + expect.objectContaining({ + name: "new-folder", + mount_path: "/workspace", + }) + ); + }); + }); + + describe("updateConfigFolder", () => { + it("updates folder files", async () => { + const mockResponse = { + data: { + id: "folder-1", + name: "updated-folder", + mount_path: "/home/user", + files: { ".bashrc": "alias ll='ls -la'" }, + is_active: true, + user_id: "user-1", + created_at: "2024-01-01T00:00:00Z", + updated_at: "2024-01-01T00:00:00Z", + }, + }; + mockPut.mockResolvedValue(mockResponse); + + const result = await updateConfigFolder("folder-1", { + files: { ".bashrc": "alias ll='ls -la'" }, + }); + + expect(result.files).toEqual({ ".bashrc": "alias ll='ls -la'" }); + expect(mockPut).toHaveBeenCalledWith( + "/config-folders/folder-1", + expect.objectContaining({ + files: { ".bashrc": "alias ll='ls -la'" }, + }) + ); + }); + }); + + describe("deleteConfigFolder", () => { + it("deletes folder", async () => { + mockDelete.mockResolvedValue({ data: undefined }); + + await deleteConfigFolder("folder-1"); + + expect(mockDelete).toHaveBeenCalledWith("/config-folders/folder-1"); + }); + }); +}); diff --git a/apps/web/src/api/config-folders.ts b/apps/web/src/api/config-folders.ts new file mode 100644 index 0000000..890111a --- /dev/null +++ b/apps/web/src/api/config-folders.ts @@ -0,0 +1,77 @@ +import { apiClient } from "./client"; +import type { + ConfigFolder, + CreateConfigFolderRequest, + UpdateConfigFolderRequest, + ProjectOverrideRequest, +} from "../types/config-folder"; + +export type { + ConfigFolder, + CreateConfigFolderRequest, + UpdateConfigFolderRequest, + ProjectOverrideRequest, +} from "../types/config-folder"; + +export const listConfigFolders = async (): Promise => { + const response = await apiClient.get("/config-folders"); + return response.data; +}; + +export const getConfigFolder = async (id: string): Promise => { + const response = await apiClient.get(`/config-folders/${id}`); + return response.data; +}; + +export const createConfigFolder = async ( + data: CreateConfigFolderRequest, +): Promise => { + const response = await apiClient.post("/config-folders", data); + return response.data; +}; + +export const updateConfigFolder = async ( + id: string, + data: UpdateConfigFolderRequest, +): Promise => { + const response = await apiClient.put( + `/config-folders/${id}`, + data, + ); + return response.data; +}; + +export const deleteConfigFolder = async (id: string): Promise => { + await apiClient.delete(`/config-folders/${id}`); +}; + +export const addProjectOverride = async ( + id: string, + projectId: string, + data: ProjectOverrideRequest, +): Promise => { + const response = await apiClient.post( + `/config-folders/${id}/overrides/${projectId}`, + data, + ); + return response.data; +}; + +export const updateProjectOverride = async ( + id: string, + projectId: string, + data: ProjectOverrideRequest, +): Promise => { + const response = await apiClient.put( + `/config-folders/${id}/overrides/${projectId}`, + data, + ); + return response.data; +}; + +export const deleteProjectOverride = async ( + id: string, + projectId: string, +): Promise => { + await apiClient.delete(`/config-folders/${id}/overrides/${projectId}`); +}; diff --git a/apps/web/src/api/git-repositories.ts b/apps/web/src/api/git-repositories.ts new file mode 100644 index 0000000..494fa7d --- /dev/null +++ b/apps/web/src/api/git-repositories.ts @@ -0,0 +1,212 @@ +import { apiClient } from "./client"; +import type { + CommitDetail, + CommitHistoryResponse, + CommitResponse, + GitRepository, + GitRepositoryCreate, + GitStatus, + MergeResponse, + URLParseResult, +} from "../types/git-repository"; + +export type { + CommitDetail, + CommitHistoryEntry, + CommitHistoryResponse, + CommitResponse, + GitRepository, + GitRepositoryCreate, + GitStatus, + MergeResponse, + URLParseResult, +} from "../types/git-repository"; + +export interface Branch { + name: string; + is_default: boolean; + last_commit: string | null; +} + +export interface BranchesResponse { + branches: Branch[]; + default_branch: string; +} + +export async function parseGitUrl(url: string): Promise { + const response = await apiClient.post("/projects/repositories/parse-url", { + url, + }); + return response.data; +} + +export async function listRepositories( + projectId: string, +): Promise { + const response = await apiClient.get(`/projects/${projectId}/repositories`); + return response.data; +} + +export async function listRepositoryBranches( + projectId: string, + repoId: string, +): Promise { + const response = await apiClient.get( + `/projects/${projectId}/repositories/${repoId}/branches`, + ); + return response.data; +} + +export async function createRepository( + projectId: string, + data: GitRepositoryCreate, +): Promise { + const response = await apiClient.post( + `/projects/${projectId}/repositories`, + data, + ); + return response.data; +} + +export async function deleteRepository( + projectId: string, + repoId: string, +): Promise { + await apiClient.delete(`/projects/${projectId}/repositories/${repoId}`); +} + +export async function getRepositoryHistory( + projectId: string, + repoId: string, + branch?: string, + limit?: number, +): Promise { + const searchParams = new URLSearchParams(); + if (branch) searchParams.set("branch", branch); + if (limit) searchParams.set("limit", String(limit)); + const queryString = searchParams.toString(); + const params = queryString ? `?${queryString}` : ""; + const response = await apiClient.get( + `/projects/${projectId}/repositories/${repoId}/history${params}`, + ); + return response.data; +} + +export async function getCommitDetail( + projectId: string, + repoId: string, + commitHash: string, +): Promise { + const response = await apiClient.get( + `/projects/${projectId}/repositories/${repoId}/commits/${commitHash}`, + ); + return response.data; +} + +export async function getRepositoryStatus( + projectId: string, + repoId: string, +): Promise { + const response = await apiClient.get( + `/projects/${projectId}/repositories/${repoId}/status`, + ); + return response.data; +} + +export async function createBranch( + projectId: string, + repoId: string, + name: string, + baseBranch: string = "HEAD", +): Promise<{ message: string; branch: string }> { + const response = await apiClient.post( + `/projects/${projectId}/repositories/${repoId}/branches`, + { name, base_branch: baseBranch }, + ); + return response.data; +} + +export async function deleteBranch( + projectId: string, + repoId: string, + branchName: string, + force: boolean = false, +): Promise<{ message: string }> { + const response = await apiClient.delete( + `/projects/${projectId}/repositories/${repoId}/branches/${branchName}?force=${force}`, + ); + return response.data; +} + +export async function checkoutBranch( + projectId: string, + repoId: string, + branch: string, +): Promise<{ message: string; branch: string }> { + const response = await apiClient.post( + `/projects/${projectId}/repositories/${repoId}/checkout`, + { branch }, + ); + return response.data; +} + +export async function commitChanges( + projectId: string, + repoId: string, + message: string, + files?: string[], +): Promise { + const response = await apiClient.post( + `/projects/${projectId}/repositories/${repoId}/commit`, + { message, files }, + ); + return response.data; +} + +export async function fetchRepository( + projectId: string, + repoId: string, +): Promise<{ message: string }> { + const response = await apiClient.post( + `/projects/${projectId}/repositories/${repoId}/fetch`, + ); + return response.data; +} + +export async function pullRepository( + projectId: string, + repoId: string, + branch?: string, +): Promise<{ message: string }> { + const params = branch ? `?branch=${branch}` : ""; + const response = await apiClient.post( + `/projects/${projectId}/repositories/${repoId}/pull${params}`, + ); + return response.data; +} + +export async function pushRepository( + projectId: string, + repoId: string, + branch?: string, +): Promise<{ message: string }> { + const params = branch ? `?branch=${branch}` : ""; + const response = await apiClient.post( + `/projects/${projectId}/repositories/${repoId}/push${params}`, + ); + return response.data; +} + +export async function mergeBranches( + projectId: string, + repoId: string, + sourceBranch: string, + targetBranch?: string, + message?: string, +): Promise { + const response = await apiClient.post( + `/projects/${projectId}/repositories/${repoId}/merge`, + { source_branch: sourceBranch, target_branch: targetBranch, message }, + ); + return response.data; +} diff --git a/apps/web/src/api/git_repositories.ts b/apps/web/src/api/git_repositories.ts deleted file mode 100644 index 580bb61..0000000 --- a/apps/web/src/api/git_repositories.ts +++ /dev/null @@ -1,299 +0,0 @@ -import { apiClient } from "./client"; - -export interface GitRepository { - id: string; - name: string; - path: string; - project_id: string; - owner_id: string; - is_mirror: boolean; - remote_url: string | null; - ssh_key_id: string | null; - last_push: string | null; - created_at: string | null; -} - -export interface GitRepositoryCreate { - name: string; - remote_url?: string; - force_original_url?: boolean; - ssh_key_id?: string; -} - -export interface URLParseResult { - original_url: string; - base_url: string | null; - is_valid_clone_url: boolean; - needs_parsing: boolean; - host: string | null; - message: string; - error_code: string | null; -} - -export async function parseGitUrl(url: string): Promise { - const response = await apiClient.post("/repositories/parse-url", { url }); - return response.data; -} - -export async function listRepositories(projectId?: string): Promise { - if (projectId) { - const response = await apiClient.get( - `/projects/${projectId}/repositories` - ); - return response.data; - } - // List all user repositories (including external) - const response = await apiClient.get("/repositories"); - return response.data; -} - -export async function listAllUserRepositories(): Promise { - const response = await apiClient.get("/repositories"); - return response.data; -} - -export async function createRepository( - projectId: string, - data: GitRepositoryCreate -): Promise { - const response = await apiClient.post(`/projects/${projectId}/repositories`, data); - return response.data; -} - -export async function createExternalRepository( - data: GitRepositoryCreate -): Promise { - const response = await apiClient.post("/repositories", data); - return response.data; -} - -export async function deleteRepository(projectId: string, repoId: string): Promise { - await apiClient.delete(`/projects/${projectId}/repositories/${repoId}`); -} - -export async function updateRepositorySshKey( - projectId: string, - repoId: string, - sshKeyId: string | null -): Promise { - const response = await apiClient.patch( - `/projects/${projectId}/repositories/${repoId}/ssh-key`, - { ssh_key_id: sshKeyId } - ); - return response.data; -} - -export interface Branch { - name: string; - is_default: boolean; - last_commit: string | null; -} - -export interface BranchesResponse { - branches: Branch[]; - default_branch: string; -} - -export async function listRepositoryBranches( - projectId: string, - repoId: string -): Promise { - const response = await apiClient.get( - `/projects/${projectId}/repositories/${repoId}/branches` - ); - return response.data; -} - -export interface CommitHistoryEntry { - hash: string; - short_hash: string; - message: string; - author_name: string; - author_email: string; - author_date: string; - refs: string[]; - graph_symbol: string; - graph_depth: number; -} - -export interface CommitHistoryResponse { - commits: CommitHistoryEntry[]; - branches: string[]; - tags: string[]; -} - -export async function getRepositoryHistory( - projectId: string, - repoId: string, - branch?: string, - limit?: number -): Promise { - const searchParams = new URLSearchParams(); - if (branch) searchParams.set("branch", branch); - if (limit) searchParams.set("limit", String(limit)); - const queryString = searchParams.toString(); - const params = queryString ? `?${queryString}` : ""; - const response = await apiClient.get(`/projects/${projectId}/repositories/${repoId}/history${params}`); - return response.data; -} - -export interface CommitDetail { - hash: string; - short_hash: string; - message: string; - author_name: string; - author_email: string; - author_date: string; - committer_name: string; - committer_email: string; - committer_date: string; - stats: { - additions: number; - deletions: number; - files_changed: number; - }; - diff: string; - parents: string[]; -} - -export async function getCommitDetail( - projectId: string, - repoId: string, - commitHash: string -): Promise { - const response = await apiClient.get( - `/projects/${projectId}/repositories/${repoId}/commits/${commitHash}` - ); - return response.data; -} - -// Git Control API - -export interface GitStatus { - branch: string; - modified: string[]; - added: string[]; - deleted: string[]; - untracked: string[]; - renamed: string[]; - ahead: number; - behind: number; -} - -export async function getRepositoryStatus( - projectId: string, - repoId: string -): Promise { - const response = await apiClient.get( - `/projects/${projectId}/repositories/${repoId}/status` - ); - return response.data; -} - -export async function createBranch( - projectId: string, - repoId: string, - name: string, - baseBranch: string = "HEAD" -): Promise<{ message: string; branch: string }> { - const response = await apiClient.post( - `/projects/${projectId}/repositories/${repoId}/branches`, - { name, base_branch: baseBranch } - ); - return response.data; -} - -export async function deleteBranch( - projectId: string, - repoId: string, - branchName: string, - force: boolean = false -): Promise<{ message: string }> { - const response = await apiClient.delete( - `/projects/${projectId}/repositories/${repoId}/branches/${branchName}?force=${force}` - ); - return response.data; -} - -export async function checkoutBranch( - projectId: string, - repoId: string, - branch: string -): Promise<{ message: string; branch: string }> { - const response = await apiClient.post( - `/projects/${projectId}/repositories/${repoId}/checkout`, - { branch } - ); - return response.data; -} - -export interface CommitResponse { - commit_hash: string; - message: string; -} - -export async function commitChanges( - projectId: string, - repoId: string, - message: string, - files?: string[] -): Promise { - const response = await apiClient.post( - `/projects/${projectId}/repositories/${repoId}/commit`, - { message, files } - ); - return response.data; -} - -export async function fetchRepository( - projectId: string, - repoId: string -): Promise<{ message: string }> { - const response = await apiClient.post( - `/projects/${projectId}/repositories/${repoId}/fetch` - ); - return response.data; -} - -export async function pullRepository( - projectId: string, - repoId: string, - branch?: string -): Promise<{ message: string }> { - const params = branch ? `?branch=${branch}` : ""; - const response = await apiClient.post( - `/projects/${projectId}/repositories/${repoId}/pull${params}` - ); - return response.data; -} - -export async function pushRepository( - projectId: string, - repoId: string, - branch?: string -): Promise<{ message: string }> { - const params = branch ? `?branch=${branch}` : ""; - const response = await apiClient.post( - `/projects/${projectId}/repositories/${repoId}/push${params}` - ); - return response.data; -} - -export interface MergeResponse { - commit_hash: string; - message: string; -} - -export async function mergeBranches( - projectId: string, - repoId: string, - sourceBranch: string, - targetBranch?: string, - message?: string -): Promise { - const response = await apiClient.post( - `/projects/${projectId}/repositories/${repoId}/merge`, - { source_branch: sourceBranch, target_branch: targetBranch, message } - ); - return response.data; -} diff --git a/apps/web/src/api/sessions.ts b/apps/web/src/api/sessions.ts index 751bf6e..37e1445 100644 --- a/apps/web/src/api/sessions.ts +++ b/apps/web/src/api/sessions.ts @@ -1,38 +1,20 @@ -import { AxiosError } from "axios"; import { apiClient } from "./client"; +import type { Session } from "../types/session"; +import type { ToolInstance } from "../types/tool-instance"; -export interface ToolInstance { - id: string; - name: string; - display_name: string; - tool_type_id: string; - tool_type_name: string; - tool_type_interfaces: string[]; - status: string; - url: string | null; - port: number | null; - selected_config_profile_id: string | null; - ssh_key_ids: string[]; - created_at: string; -} +export type { Session } from "../types/session"; +export type { ToolInstance } from "../types/tool-instance"; -export interface Session { - id: string; - display_name: string; - tool_type_name: string; - tool_icon: string; - tool_type_interfaces: string[]; - repository_name: string; - repository_id: string; - project_name: string; - project_id: string; - status: string; - url: string | null; - container_status?: string; - probe_status?: string; - clone_mode?: string; - branch?: string | null; - created_at?: string; +export interface InstanceHealth { + healthy: boolean; + container_status: string; + container_health: string | null; + container_exit_code: number | null; + tunnel_status: string; + tunnel_status_code: number | null; + probe_status: string; + last_probe_output: string | null; + error: string | null; } export async function listInstances( @@ -50,11 +32,11 @@ export async function createInstance( repoId: string, toolTypeId: string, displayName?: string, - cloneMode?: string, - branch?: string, - newBranch?: string, + _cloneMode?: string, + _branch?: string, + _newBranch?: string, configProfileId?: string, - sshKeyIds?: string[], + _sshKeyIds?: string[], workspaceId?: string, ): Promise { const response = await apiClient.post( @@ -63,11 +45,7 @@ export async function createInstance( tool_type_id: toolTypeId, display_name: displayName, workspace_id: workspaceId || undefined, - clone_mode: cloneMode || "mount", - branch: branch || undefined, - new_branch: newBranch || undefined, - config_profile_id: configProfileId, - ssh_key_ids: sshKeyIds || [], + config_profile_id: configProfileId || undefined, }, ); return response.data; @@ -78,31 +56,16 @@ export async function startInstance( repoId: string, instanceId: string, configProfileId?: string, - sshKeyIds?: string[], - retries = 2, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _sshKeyIds?: string[], + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _retries?: number, ): Promise<{ status: string; url?: string }> { - try { - const response = await apiClient.post( - `/projects/${projectId}/repositories/${repoId}/instances/${instanceId}/start`, - { config_profile_id: configProfileId, ssh_key_ids: sshKeyIds || [] }, - ); - return response.data; - } catch (error) { - // Retry on network errors (e.g. Docker creating network interfaces) - const axiosError = error as AxiosError; - if (retries > 0 && !axiosError.response) { - await new Promise((r) => setTimeout(r, 1500)); - return startInstance( - projectId, - repoId, - instanceId, - configProfileId, - sshKeyIds, - retries - 1, - ); - } - throw error; - } + const response = await apiClient.post( + `/projects/${projectId}/repositories/${repoId}/instances/${instanceId}/start`, + { config_profile_id: configProfileId }, + ); + return response.data; } export async function stopInstance( @@ -120,32 +83,11 @@ export async function restartInstance( projectId: string, repoId: string, instanceId: string, - configProfileId?: string, - sshKeyIds?: string[], - retries = 2, ): Promise<{ status: string; url?: string }> { - try { - const response = await apiClient.post( - `/projects/${projectId}/repositories/${repoId}/instances/${instanceId}/restart`, - { config_profile_id: configProfileId, ssh_key_ids: sshKeyIds || [] }, - ); - return response.data; - } catch (error) { - // Retry on network errors (e.g. Docker creating network interfaces) - const axiosError = error as AxiosError; - if (retries > 0 && !axiosError.response) { - await new Promise((r) => setTimeout(r, 1500)); - return restartInstance( - projectId, - repoId, - instanceId, - configProfileId, - sshKeyIds, - retries - 1, - ); - } - throw error; - } + const response = await apiClient.post( + `/projects/${projectId}/repositories/${repoId}/instances/${instanceId}/restart`, + ); + return response.data; } export async function deleteInstance( @@ -154,10 +96,10 @@ export async function deleteInstance( instanceId: string, force?: boolean, ): Promise { - await apiClient.delete( - `/projects/${projectId}/repositories/${repoId}/instances/${instanceId}`, - { params: { force } }, - ); + const url = force + ? `/projects/${projectId}/repositories/${repoId}/instances/${instanceId}?force=true` + : `/projects/${projectId}/repositories/${repoId}/instances/${instanceId}`; + await apiClient.delete(url); } export async function getUserSessions(): Promise { @@ -165,23 +107,11 @@ export async function getUserSessions(): Promise { return response.data.sessions; } -export interface InstanceHealth { - healthy: boolean; - container_status: string; - container_health: string | null; - container_exit_code: number | null; - tunnel_status: string; - tunnel_status_code: number | null; - probe_status: string; - last_probe_output: string | null; - error: string | null; -} - export async function checkInstanceHealth( projectId: string, repoId: string, instanceId: string, -): Promise { +): Promise<{ healthy: boolean; status_code: number | null; error?: string }> { const response = await apiClient.get( `/projects/${projectId}/repositories/${repoId}/instances/${instanceId}/health`, ); diff --git a/apps/web/src/api/ssh_keys.ts b/apps/web/src/api/ssh-keys.ts similarity index 100% rename from apps/web/src/api/ssh_keys.ts rename to apps/web/src/api/ssh-keys.ts diff --git a/apps/web/src/api/tool-configs.ts b/apps/web/src/api/tool-configs.ts new file mode 100644 index 0000000..3974f30 --- /dev/null +++ b/apps/web/src/api/tool-configs.ts @@ -0,0 +1,52 @@ +import { apiClient } from "./client"; +import type { ToolConfig, CreateToolConfigRequest } from "../types/tool-config"; + +export type { ToolConfig, CreateToolConfigRequest } from "../types/tool-config"; + +export const listToolConfigs = async ( + tool_type_id?: string, + project_id?: string, +): Promise => { + const params = new URLSearchParams(); + if (tool_type_id) params.append("tool_type_id", tool_type_id); + if (project_id) params.append("project_id", project_id); + + const response = await apiClient.get<{ configs: ToolConfig[] }>( + `/tool-configs?${params.toString()}`, + ); + return response.data.configs; +}; + +export const createToolConfig = async ( + data: CreateToolConfigRequest, +): Promise => { + const response = await apiClient.post<{ configs: ToolConfig[] }>( + "/tool-configs", + data, + ); + return response.data.configs[0]; +}; + +export const updateToolConfig = async ( + id: string, + data: CreateToolConfigRequest, +): Promise => { + const response = await apiClient.put<{ configs: ToolConfig[] }>( + `/tool-configs/${id}`, + data, + ); + return response.data.configs[0]; +}; + +export const deleteToolConfig = async (id: string): Promise => { + await apiClient.delete(`/tool-configs/${id}`); +}; + +export const getToolConfigDefaults = async ( + toolTypeId: string, +): Promise => { + const response = await apiClient.get( + `/tool-configs/defaults/${toolTypeId}`, + ); + return response.data; +}; diff --git a/apps/web/src/api/tool_types.test.ts b/apps/web/src/api/tool-types.test.ts similarity index 99% rename from apps/web/src/api/tool_types.test.ts rename to apps/web/src/api/tool-types.test.ts index 8609907..0acc59c 100644 --- a/apps/web/src/api/tool_types.test.ts +++ b/apps/web/src/api/tool-types.test.ts @@ -6,7 +6,7 @@ import { listToolTypes, updateToolType, validateToolType, -} from "../api/tool_types"; +} from "../api/tool-types"; const mockGet = vi.fn(); const mockPost = vi.fn(); diff --git a/apps/web/src/api/tool-types.ts b/apps/web/src/api/tool-types.ts new file mode 100644 index 0000000..54d3fa7 --- /dev/null +++ b/apps/web/src/api/tool-types.ts @@ -0,0 +1,51 @@ +import { apiClient } from "./client"; +import type { + ToolType, + CreateToolTypeRequest, + UpdateToolTypeRequest, +} from "../types/tool-type"; + +export type { + ReadinessProbe, + ToolType, + CreateToolTypeRequest, + UpdateToolTypeRequest, +} from "../types/tool-type"; + +export const listToolTypes = async (): Promise => { + const response = await apiClient.get("/tool-types"); + return response.data; +}; + +export const getToolType = async (id: string): Promise => { + const response = await apiClient.get(`/tool-types/${id}`); + return response.data; +}; + +export const createToolType = async ( + data: CreateToolTypeRequest, +): Promise => { + const response = await apiClient.post("/tool-types", data); + return response.data; +}; + +export const updateToolType = async ( + id: string, + data: UpdateToolTypeRequest, +): Promise => { + const response = await apiClient.put(`/tool-types/${id}`, data); + return response.data; +}; + +export const deleteToolType = async (id: string): Promise => { + await apiClient.delete(`/tool-types/${id}`); +}; + +export const validateToolType = async ( + id: string, +): Promise<{ valid: boolean; errors?: string[] }> => { + const response = await apiClient.get<{ valid: boolean; errors?: string[] }>( + `/tool-types/${id}/validate`, + ); + return response.data; +}; diff --git a/apps/web/src/api/tool_types.ts b/apps/web/src/api/tool_types.ts deleted file mode 100644 index 0c3b381..0000000 --- a/apps/web/src/api/tool_types.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { apiClient } from "./client"; - -export interface ReadinessProbe { - command: string; - timeout: number; - interval: number; -} - -export interface ToolType { - id: string; - name: string; - display_name: string; - description: string | null; - category: string; - interface_type: string; - requires_port: boolean; - default_port: number | null; - definition_type: "compose" | "dockerfile" | "manifest"; - manifest_id: string | null; - compose_template: string | null; - dockerfile_template: string | null; - build_context: Record | null; - readiness_probe: ReadinessProbe | null; - startup_command: string | null; - required_variables: string[]; - created_by_id: string | null; - created_at: string; - updated_at: string; -} - -export interface CreateToolTypeRequest { - name: string; - display_name: string; - description?: string; - category?: string; - interface_type?: string; - requires_port?: boolean; - default_port: number; - definition_type?: "compose" | "dockerfile" | "manifest"; - manifest_id?: string; - compose_template?: string; - dockerfile_template?: string; - build_context?: Record; - readiness_probe?: ReadinessProbe; - startup_command?: string; - required_variables: string[]; -} - -export interface UpdateToolTypeRequest { - display_name?: string; - description?: string; - category?: string; - interface_type?: string; - requires_port?: boolean; - default_port?: number; - definition_type?: "compose" | "dockerfile" | "manifest"; - manifest_id?: string; - compose_template?: string; - dockerfile_template?: string; - build_context?: Record; - readiness_probe?: ReadinessProbe; - startup_command?: string; - required_variables?: string[]; -} - -export const listToolTypes = async (): Promise => { - const response = await apiClient.get("/tool-types"); - return response.data; -}; - -export const getToolType = async (id: string): Promise => { - const response = await apiClient.get(`/tool-types/${id}`); - return response.data; -}; - -export const createToolType = async ( - data: CreateToolTypeRequest, -): Promise => { - const response = await apiClient.post("/tool-types", data); - return response.data; -}; - -export const updateToolType = async ( - id: string, - data: UpdateToolTypeRequest, -): Promise => { - const response = await apiClient.put(`/tool-types/${id}`, data); - return response.data; -}; - -export const deleteToolType = async (id: string): Promise => { - await apiClient.delete(`/tool-types/${id}`); -}; - -export const validateToolType = async ( - id: string, -): Promise<{ valid: boolean; errors?: string[] }> => { - const response = await apiClient.get<{ valid: boolean; errors?: string[] }>( - `/tool-types/${id}/validate`, - ); - return response.data; -}; diff --git a/apps/web/src/components/protected-route.test.tsx b/apps/web/src/components/ProtectedRoute.test.tsx similarity index 96% rename from apps/web/src/components/protected-route.test.tsx rename to apps/web/src/components/ProtectedRoute.test.tsx index d2fd135..a0194d5 100644 --- a/apps/web/src/components/protected-route.test.tsx +++ b/apps/web/src/components/ProtectedRoute.test.tsx @@ -2,7 +2,7 @@ import { render, screen } from "@testing-library/react"; import { MemoryRouter, Route, Routes } from "react-router-dom"; import { describe, expect, it, vi } from "vitest"; -import { ProtectedRoute } from "./protected-route"; +import { ProtectedRoute } from "./ProtectedRoute"; const mockUseAuth = vi.fn(); diff --git a/apps/web/src/components/protected-route.tsx b/apps/web/src/components/ProtectedRoute.tsx similarity index 100% rename from apps/web/src/components/protected-route.tsx rename to apps/web/src/components/ProtectedRoute.tsx diff --git a/apps/web/src/components/app-shell.tsx b/apps/web/src/components/app-shell.tsx deleted file mode 100644 index 2fbd949..0000000 --- a/apps/web/src/components/app-shell.tsx +++ /dev/null @@ -1,192 +0,0 @@ -import { useCallback, useEffect } from "react"; -import { Link, NavLink, Outlet, useLocation } from "react-router-dom"; - -import { getUserSessions } from "../api/sessions"; -import type { Session } from "../api/sessions"; -import { useTheme } from "../hooks/use-theme"; -import { useAuth } from "../state/auth"; -import { useSessions } from "../state/sessions"; -import { useMobileViewport } from "../hooks/use-mobile-viewport"; -import { EventProvider } from "../state/events"; -import { ToastProvider } from "../state/toast"; -import { NotificationProvider } from "../state/notifications"; -import { EventToastBridge } from "./event-toast-bridge"; -import { NotificationCenter } from "./notification-center"; -import { Icon } from "./icon"; -import { MobileNav } from "./mobile-nav"; -import { StartToolFAB } from "./start-tool-fab"; -import type { IconName } from "../utils/icons"; - -const NAV_ITEMS: { - to: string; - label: string; - icon: IconName; - badge?: "sessions"; -}[] = [ - { to: "/", label: "Home", icon: "dashboard" }, - { to: "/sessions", label: "Sessions", icon: "terminal", badge: "sessions" }, - { to: "/workspaces", label: "Workspaces", icon: "folder" }, - { to: "/projects", label: "Projects", icon: "projects" }, - { to: "/tool-workshop", label: "Tool Workshop", icon: "settings" }, - { to: "/config-profiles", label: "Config Profiles", icon: "folder" }, - { to: "/settings", label: "Settings", icon: "settings" }, -]; - -const SessionItem = ({ session }: { session: Session }) => { - const isRunning = session.status === "running"; - - // Determine the link target: - // - Web tools open their tunnel URL - // - Terminal tools open the terminal page - // - Everything else falls back to the project page - const hasTerminal = session.tool_type_interfaces.includes("terminal"); - const hasWeb = session.tool_type_interfaces.includes("web"); - const href = session.url && hasWeb - ? session.url - : hasTerminal - ? `/instances/${session.id}/terminal` - : `/projects/${session.project_id}`; - - return ( - - - - {session.display_name} - - ); -}; - -export const AppShell = () => { - useTheme(); - const { user, logout } = useAuth(); - const { sessions, setAllSessions } = useSessions(); - const location = useLocation(); - const isMobile = useMobileViewport(); - const isMobileTerminal = - isMobile && - location.pathname.includes("/instances/") && - location.pathname.includes("/terminal"); - - const loadSessions = useCallback(async () => { - try { - const data = await getUserSessions(); - setAllSessions(data); - } catch { - // Silently fail - sessions are optional - } - }, [setAllSessions]); - - useEffect(() => { - void loadSessions(); - // Poll every 30 seconds (reduced from 10s to avoid ERR_NETWORK_CHANGED from Docker network changes) - const interval = setInterval(() => { - void loadSessions(); - }, 30000); - return () => clearInterval(interval); - }, [loadSessions]); - - if (isMobileTerminal) { - return ( - - - - -
- -
-
-
-
- ); - } - - return ( - - - - -
-
- - Headquarter - -
- - - {user?.name ?? "User"} - - -
-
- -
- {!isMobile && ( - - )} - -
- -
-
- - {isMobile && ( - s.status === "running").length - } - /> - )} - -
-
-
-
- ); -}; diff --git a/apps/web/src/components/commit-panel.tsx b/apps/web/src/components/commit-panel.tsx deleted file mode 100644 index 9c96cda..0000000 --- a/apps/web/src/components/commit-panel.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import { useState } from "react"; - -import { commitChanges } from "../api/git_repositories"; - -interface CommitPanelProps { - projectId: string; - repoId: string; - modified: string[]; - added: string[]; - deleted: string[]; - untracked: string[]; - onCommit: () => void; -} - -export const CommitPanel = ({ - projectId, - repoId, - modified, - added, - deleted, - untracked, - onCommit, -}: CommitPanelProps) => { - const [message, setMessage] = useState(""); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(null); - - const allFiles = [...modified, ...added, ...deleted, ...untracked]; - const hasChanges = allFiles.length > 0; - - const handleCommit = async () => { - if (!message.trim()) { - setError("Please enter a commit message"); - return; - } - setLoading(true); - setError(null); - try { - await commitChanges(projectId, repoId, message); - setMessage(""); - onCommit(); - } catch { - setError("Commit failed. Please try again."); - } finally { - setLoading(false); - } - }; - - if (!hasChanges) return null; - - return ( -
-

Changes

- -
- {modified.map((file) => ( -
- M - {file} -
- ))} - {added.map((file) => ( -
- A - {file} -
- ))} - {deleted.map((file) => ( -
- D - {file} -
- ))} - {untracked.map((file) => ( -
- ? - {file} -
- ))} -
- -
-