diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml
index e9208ff..a82b82a 100644
--- a/.github/workflows/cd.yml
+++ b/.github/workflows/cd.yml
@@ -17,7 +17,7 @@ jobs:
build:
name: Build
- runs-on: ubuntu-latest
+ runs-on: uipath-ubuntu-latest
needs:
- lint
@@ -30,15 +30,15 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- name: Setup uv
- uses: astral-sh/setup-uv@v5
+ uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
with:
enable-cache: true
- name: Setup Python
- uses: actions/setup-python@v5
+ uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version-file: ".python-version"
@@ -49,14 +49,14 @@ jobs:
run: uv build
- name: Upload artifacts
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: release-dists
path: dist/
pypi-publish:
name: Upload release to PyPI
- runs-on: ubuntu-latest
+ runs-on: uipath-ubuntu-latest
environment: pypi
needs:
@@ -67,10 +67,10 @@ jobs:
steps:
- name: Retrieve release distributions
- uses: actions/download-artifact@v4
+ uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: release-dists
path: dist/
- name: Publish package distributions to PyPI
- uses: pypa/gh-action-pypi-publish@release/v1
+ uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml
index 8562e45..d88c491 100644
--- a/.github/workflows/commitlint.yml
+++ b/.github/workflows/commitlint.yml
@@ -6,18 +6,18 @@ on:
jobs:
commitlint:
name: Commit Lint
- runs-on: ubuntu-latest
+ runs-on: uipath-ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
fetch-depth: 0
- name: Setup Node
- uses: actions/setup-node@v3
+ uses: actions/setup-node@3235b876344d2a9aa001b8d1453c930bba69e610 # v3.9.1
with:
node-version: 22
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index bd659a3..b06b51d 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -1,31 +1,31 @@
-name: Deploy
-
-on:
- push:
- branches: [ "main" ]
-
-permissions:
- contents: read
-
-jobs:
- deploy:
- environment: website-prod
- runs-on: ubuntu-latest
-
- steps:
- - uses: actions/checkout@v4
-
- - name: Azure login
- uses: azure/login@v2
- with:
- creds: ${{ secrets.AZURE_CREDENTIALS }}
-
- - name: Install Azure CLI
- uses: pietrobolcato/install-azure-cli-action@main
-
- - name: Run Deploy.ps1
- shell: pwsh
- run: ./Deploy.ps1 `
- -resourceGroupName ${{ secrets.AZURE_RESOURCE_GROUP }} `
- -appName ${{ secrets.AZURE_APP_NAME }} `
- -acrName ${{ secrets.AZURE_ACR_NAME }}
+name: Deploy
+
+on:
+ push:
+ branches: [ "main" ]
+
+permissions:
+ contents: read
+
+jobs:
+ deploy:
+ environment: website-prod
+ runs-on: uipath-ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
+
+ - name: Azure login
+ uses: azure/login@7184910d9eb2b1c5e48f7073824a90609bb9b6d6 # v2.3.1
+ with:
+ creds: ${{ secrets.AZURE_CREDENTIALS }}
+
+ - name: Install Azure CLI
+ uses: pietrobolcato/install-azure-cli-action@161e654d6c82d50576f1b32adbeea9433bf2a0ea # main
+
+ - name: Run Deploy.ps1
+ shell: pwsh
+ run: ./Deploy.ps1 `
+ -resourceGroupName ${{ secrets.AZURE_RESOURCE_GROUP }} `
+ -appName ${{ secrets.AZURE_APP_NAME }} `
+ -acrName ${{ secrets.AZURE_ACR_NAME }}
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index d259d94..00568c0 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -6,21 +6,21 @@ on:
jobs:
lint:
name: Lint
- runs-on: ubuntu-latest
+ runs-on: uipath-ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- name: Setup uv
- uses: astral-sh/setup-uv@v5
+ uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
with:
enable-cache: true
- name: Setup Python
- uses: actions/setup-python@v5
+ uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version-file: ".python-version"
diff --git a/.github/workflows/publish-dev.yml b/.github/workflows/publish-dev.yml
index ae0907d..4d712df 100644
--- a/.github/workflows/publish-dev.yml
+++ b/.github/workflows/publish-dev.yml
@@ -6,7 +6,7 @@ on:
jobs:
publish-dev:
- runs-on: ubuntu-latest
+ runs-on: uipath-ubuntu-latest
permissions:
contents: read
pull-requests: write
@@ -16,15 +16,15 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- name: Setup uv
- uses: astral-sh/setup-uv@v5
+ uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
with:
enable-cache: true
- name: Setup Python
- uses: actions/setup-python@v5
+ uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version-file: ".python-version"
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 96de3f5..6644878 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -6,10 +6,14 @@ on:
jobs:
test:
name: Test
- runs-on: ${{ matrix.os }}
+ runs-on: ${{ matrix.os == 'windows-latest' && 'uipath-windows-latest' || 'uipath-ubuntu-latest' }}
strategy:
matrix:
python-version: ["3.11", "3.12", "3.13"]
+ # Stock labels on purpose: GitHub builds the check-run name from these
+ # matrix values, and branch protection requires the stock names
+ # ("Test (3.11, ubuntu-latest)"). runs-on below maps them onto the
+ # uipath- pool, so the jobs run on uipath runners either way.
os: [ubuntu-latest, windows-latest]
permissions:
@@ -17,13 +21,13 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- name: Setup uv
- uses: astral-sh/setup-uv@v5
+ uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
- name: Setup Python
- uses: actions/setup-python@v5
+ uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: ${{ matrix.python-version }}
diff --git a/pyproject.toml b/pyproject.toml
index c1cbcb6..dcdedca 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,15 +1,19 @@
[project]
name = "uipath-dev"
-version = "0.0.59"
+version = "0.0.85"
description = "UiPath Developer Console"
readme = { file = "README.md", content-type = "text/markdown" }
requires-python = ">=3.11"
dependencies = [
- "uipath-runtime>=0.9.0, <0.10.0",
+ "uipath-runtime>=0.13.0, <0.14.0",
"textual>=7.5.0, <8.0.0",
"pyperclip>=1.11.0, <2.0.0",
"fastapi>=0.128.8",
"uvicorn[standard]>=0.40.0",
+ "uipath>=2.14.1, <2.15.0",
+ "aiosqlite>=0.20.0",
+ "pywinpty>=2.0.0; sys_platform == 'win32'",
+ "mcp[cli]>=1.0.0",
]
classifiers = [
"Intended Audience :: Developers",
@@ -31,6 +35,7 @@ Documentation = "https://uipath.github.io/uipath-python/"
[project.scripts]
uipath-dev = "uipath.dev.__mock__:main"
uipath-dev-server = "uipath.dev.__mock_server__:main"
+uipath-dev-mcp = "uipath.dev.mcp:main"
[build-system]
requires = ["hatchling"]
@@ -57,6 +62,7 @@ dev = [
[tool.hatch.build.targets.wheel]
packages = ["src/uipath"]
artifacts = ["src/uipath/dev/server/static/**"]
+exclude = ["src/uipath/dev/server/frontend/**"]
[tool.hatch.build.targets.sdist]
exclude = ["demo"]
@@ -99,6 +105,10 @@ no_implicit_reexport = true
disallow_untyped_defs = false
+[[tool.mypy.overrides]]
+module = "uipath.dev.services.cli_agent.pty_session"
+warn_unused_ignores = false
+
[tool.pydantic-mypy]
init_forbid_extra = true
init_typed = true
diff --git a/src/uipath/dev/mcp/__init__.py b/src/uipath/dev/mcp/__init__.py
new file mode 100644
index 0000000..c391fab
--- /dev/null
+++ b/src/uipath/dev/mcp/__init__.py
@@ -0,0 +1,350 @@
+"""UiPath Dev MCP Server — exposes dev server run tools via stdio."""
+
+from __future__ import annotations
+
+import json
+import os
+from typing import Any
+
+import httpx
+import websockets
+from mcp.server.fastmcp import Context, FastMCP
+
+mcp = FastMCP(
+ "uipath-dev",
+ instructions=(
+ "This MCP server is the ONLY way to run, test, or execute agents, "
+ "functions, and projects. Do NOT use shell commands like "
+ "`uipath run`, `langgraph dev`, `python -m`, or any subprocess. "
+ "Always use the tools provided here instead."
+ ),
+)
+
+
+def _get_port() -> str:
+ """Read port from .uipath/dev-server.port if present, else fall back to env var."""
+ from pathlib import Path
+
+ port_file = Path(".uipath") / "dev-server.port"
+ if port_file.exists():
+ return port_file.read_text().strip()
+ return os.environ.get("UIPATH_DEV_SERVER_PORT", "8080")
+
+
+def _base_url() -> str:
+ host = os.environ.get("UIPATH_DEV_SERVER_HOST", "localhost")
+ return f"http://{host}:{_get_port()}"
+
+
+def _ws_url() -> str:
+ host = os.environ.get("UIPATH_DEV_SERVER_HOST", "localhost")
+ return f"ws://{host}:{_get_port()}/ws"
+
+
+def _api_url(path: str) -> str:
+ return f"{_base_url()}/api{path}"
+
+
+async def _report_tool_call(tool: str, args: dict[str, Any] | None = None) -> None:
+ """Notify the dev server that an MCP tool was invoked."""
+ try:
+ async with httpx.AsyncClient() as client:
+ await client.post(
+ _api_url("/mcp/events"),
+ json={"tool": tool, "args": args or {}},
+ timeout=5,
+ )
+ except Exception:
+ pass # Best-effort — don't fail the tool if reporting fails
+
+
+@mcp.tool()
+async def list_entrypoints() -> list[dict[str, Any]]:
+ """List all runnable agents, functions, and projects.
+
+ Call this first to discover what is available to run.
+ Use the returned names with get_entrypoint_schema or run_entrypoint.
+ """
+ await _report_tool_call("list_entrypoints")
+ async with httpx.AsyncClient() as client:
+ resp = await client.get(_api_url("/entrypoints"), timeout=10)
+ resp.raise_for_status()
+ return resp.json()
+
+
+@mcp.tool()
+async def get_entrypoint_schema(entrypoint: str) -> dict[str, Any]:
+ """Get the input/output schema for an entrypoint.
+
+ Args:
+ entrypoint: Name of the entrypoint (from list_entrypoints).
+
+ Returns the JSON schema describing the entrypoint's expected
+ input and output. Use this to construct input_data for run_entrypoint.
+ """
+ await _report_tool_call("get_entrypoint_schema", {"entrypoint": entrypoint})
+ async with httpx.AsyncClient() as client:
+ resp = await client.get(
+ _api_url(f"/entrypoints/{entrypoint}/schema"),
+ timeout=30,
+ )
+ resp.raise_for_status()
+ return resp.json()
+
+
+@mcp.tool()
+async def run_entrypoint(
+ entrypoint: str,
+ ctx: Context, # type: ignore[type-arg]
+ input_data: dict[str, Any] | None = None,
+) -> dict[str, Any]:
+ """Run an agent, function, or project.
+
+ This is the ONLY correct way to execute code in this project — never
+ use shell commands.
+
+ Args:
+ entrypoint: Name of the entrypoint (from list_entrypoints).
+ input_data: Input data matching the entrypoint's input schema.
+ Use get_entrypoint_schema to see what fields are expected.
+ Defaults to empty dict if not provided.
+
+ Executes on the dev server with real-time graph visualization in the
+ browser. Streams progress as nodes execute, then returns the result.
+ """
+ await _report_tool_call(
+ "run_entrypoint", {"entrypoint": entrypoint, "input_data": input_data}
+ )
+ async with httpx.AsyncClient() as client:
+ resp = await client.post(
+ _api_url("/runs"),
+ json={
+ "entrypoint": entrypoint,
+ "input_data": input_data or {},
+ "mode": "run",
+ },
+ timeout=10,
+ )
+ resp.raise_for_status()
+ run: dict[str, Any] = resp.json()
+ run_id = run["id"]
+
+ await ctx.log("info", f"Run {run_id} created — streaming updates...")
+
+ # Connect to WebSocket and subscribe to run events
+ terminal_statuses = {"completed", "failed", "suspended"}
+ final_status = None
+
+ async with websockets.connect(_ws_url()) as ws:
+ # Subscribe to this run's events
+ await ws.send(json.dumps({"type": "subscribe", "payload": {"run_id": run_id}}))
+
+ async for raw in ws:
+ msg = json.loads(raw)
+ msg_type = msg.get("type", "")
+ payload = msg.get("payload", {})
+
+ if msg_type == "run.updated" and payload.get("id") == run_id:
+ status = payload.get("status", "")
+ await ctx.report_progress(
+ progress=1 if status in terminal_statuses else 0,
+ total=1,
+ message=f"Run status: {status}",
+ )
+ if status in terminal_statuses:
+ final_status = status
+ break
+
+ elif msg_type == "state" and payload.get("run_id") == run_id:
+ node = payload.get("node_name", "")
+ phase = payload.get("phase", "")
+ if phase == "started":
+ await ctx.log("info", f"Node '{node}' started")
+ elif phase == "completed":
+ await ctx.log("info", f"Node '{node}' completed")
+
+ elif msg_type == "log" and payload.get("run_id") == run_id:
+ level = payload.get("level", "info").lower()
+ message = payload.get("message", "")
+ await ctx.log(level, message)
+
+ # Fetch final run result
+ async with httpx.AsyncClient() as client:
+ resp = await client.get(_api_url(f"/runs/{run_id}"), timeout=10)
+ resp.raise_for_status()
+ result: dict[str, Any] = resp.json()
+
+ if final_status == "failed":
+ error = result.get("error", {})
+ await ctx.log("error", f"Run failed: {error.get('detail', 'unknown error')}")
+ else:
+ await ctx.log("info", f"Run {final_status}: {run_id}")
+
+ return result
+
+
+@mcp.tool()
+async def get_run_status(run_id: str) -> dict[str, Any]:
+ """Get current status and details of a run.
+
+ Args:
+ run_id: The run ID returned by run_entrypoint.
+
+ Returns full run details including status, output, traces, and logs.
+ """
+ await _report_tool_call("get_run_status", {"run_id": run_id})
+ async with httpx.AsyncClient() as client:
+ resp = await client.get(_api_url(f"/runs/{run_id}"), timeout=10)
+ resp.raise_for_status()
+ return resp.json()
+
+
+def _summarize_eval_run(result: dict[str, Any]) -> dict[str, Any]:
+ """Return only scores and justifications per item — drop everything else."""
+ items = [
+ {
+ "name": item.get("name"),
+ "status": item.get("status"),
+ "scores": item.get("scores", {}),
+ "justifications": item.get("justifications", {}),
+ "overall_score": item.get("overall_score"),
+ "error": (item["error"][:100] + "...")
+ if item.get("error") and len(item["error"]) > 100
+ else item.get("error"),
+ }
+ for item in result.get("results", [])
+ ]
+ return {
+ "id": result.get("id"),
+ "status": result.get("status"),
+ "overall_score": result.get("overall_score"),
+ "evaluator_scores": result.get("evaluator_scores", {}),
+ "results": items,
+ }
+
+
+@mcp.tool()
+async def list_eval_sets() -> list[dict[str, Any]]:
+ """List all evaluation sets.
+
+ Returns the available eval sets with their IDs, names, item counts,
+ and attached evaluator IDs. Use the returned IDs with run_eval_set.
+ """
+ await _report_tool_call("list_eval_sets")
+ async with httpx.AsyncClient() as client:
+ resp = await client.get(_api_url("/eval-sets"), timeout=10)
+ resp.raise_for_status()
+ return resp.json()
+
+
+@mcp.tool()
+async def get_eval_set(eval_set_id: str) -> dict[str, Any]:
+ """Get full details of an evaluation set including all items.
+
+ Args:
+ eval_set_id: ID of the eval set (from list_eval_sets).
+
+ Returns the eval set with all items, their inputs, expected outputs,
+ and evaluation criteria.
+ """
+ await _report_tool_call("get_eval_set", {"eval_set_id": eval_set_id})
+ async with httpx.AsyncClient() as client:
+ resp = await client.get(_api_url(f"/eval-sets/{eval_set_id}"), timeout=10)
+ resp.raise_for_status()
+ return resp.json()
+
+
+@mcp.tool()
+async def run_eval_set(
+ eval_set_id: str,
+ ctx: Context, # type: ignore[type-arg]
+) -> dict[str, Any]:
+ """Run an evaluation set against the agent.
+
+ Args:
+ eval_set_id: ID of the eval set (from list_eval_sets).
+
+ Starts the eval run and streams progress as each item completes.
+ Returns the full run result with per-item scores and overall score.
+ """
+ await _report_tool_call("run_eval_set", {"eval_set_id": eval_set_id})
+ async with httpx.AsyncClient() as client:
+ resp = await client.post(_api_url(f"/eval-sets/{eval_set_id}/runs"), timeout=30)
+ resp.raise_for_status()
+ run: dict[str, Any] = resp.json()
+ run_id = run["id"]
+
+ await ctx.log("info", f"Eval run {run_id} created — streaming progress...")
+
+ async with websockets.connect(_ws_url()) as ws:
+ async for raw in ws:
+ msg = json.loads(raw)
+ msg_type = msg.get("type", "")
+ payload = msg.get("payload", {})
+
+ if msg_type == "eval_run.progress" and payload.get("run_id") == run_id:
+ completed = payload.get("completed", 0)
+ total = payload.get("total", 0)
+ item = payload.get("item_result")
+ if item:
+ name = item.get("name", "")
+ status = item.get("status", "")
+ score = item.get("overall_score")
+ score_str = f" — score: {score:.0%}" if score is not None else ""
+ await ctx.log(
+ "info", f" [{completed}/{total}] {name}: {status}{score_str}"
+ )
+ await ctx.report_progress(progress=completed, total=total)
+
+ elif msg_type == "eval_run.completed" and payload.get("run_id") == run_id:
+ overall = payload.get("overall_score")
+ if overall is not None:
+ await ctx.log(
+ "info", f"Eval run completed — overall score: {overall:.0%}"
+ )
+ break
+
+ # Fetch final run detail
+ async with httpx.AsyncClient() as client:
+ resp = await client.get(_api_url(f"/eval-runs/{run_id}"), timeout=10)
+ resp.raise_for_status()
+ result = resp.json()
+
+ return _summarize_eval_run(result)
+
+
+@mcp.tool()
+async def list_eval_runs() -> list[dict[str, Any]]:
+ """List all evaluation runs.
+
+ Returns summaries of all eval runs with their status, scores,
+ and progress. Use run IDs with get_eval_run for full details.
+ """
+ await _report_tool_call("list_eval_runs")
+ async with httpx.AsyncClient() as client:
+ resp = await client.get(_api_url("/eval-runs"), timeout=10)
+ resp.raise_for_status()
+ return resp.json()
+
+
+@mcp.tool()
+async def get_eval_run(eval_run_id: str) -> dict[str, Any]:
+ """Get full details of an evaluation run including per-item results.
+
+ Args:
+ eval_run_id: ID of the eval run.
+
+ Returns per-item evaluator scores and justifications.
+ """
+ await _report_tool_call("get_eval_run", {"eval_run_id": eval_run_id})
+ async with httpx.AsyncClient() as client:
+ resp = await client.get(_api_url(f"/eval-runs/{eval_run_id}"), timeout=10)
+ resp.raise_for_status()
+ result = resp.json()
+
+ return _summarize_eval_run(result)
+
+
+def main() -> None:
+ """Entry point for the uipath-dev-mcp CLI command."""
+ mcp.run(transport="stdio")
diff --git a/src/uipath/dev/models/chat.py b/src/uipath/dev/models/chat.py
index ef14139..920fb96 100644
--- a/src/uipath/dev/models/chat.py
+++ b/src/uipath/dev/models/chat.py
@@ -40,7 +40,6 @@ def add(
role=self.get_role(event),
content_parts=[],
tool_calls=[],
- interrupts=[],
created_at=self.get_timestamp(event),
updated_at=self.get_timestamp(event),
)
@@ -205,7 +204,6 @@ def get_user_message(user_text: str) -> UiPathConversationMessage:
)
],
tool_calls=[],
- interrupts=[],
role="user",
)
diff --git a/src/uipath/dev/models/eval_data.py b/src/uipath/dev/models/eval_data.py
new file mode 100644
index 0000000..8fda331
--- /dev/null
+++ b/src/uipath/dev/models/eval_data.py
@@ -0,0 +1,132 @@
+"""Data models for evaluation runs."""
+
+from __future__ import annotations
+
+import uuid
+from dataclasses import dataclass, field
+from datetime import datetime, timezone
+from typing import Any
+
+
+@dataclass
+class EvalSetInfo:
+ """Summary of a discovered evaluation set."""
+
+ id: str
+ name: str
+ eval_count: int
+ evaluator_ids: list[str]
+
+
+@dataclass
+class EvalItemResult:
+ """Result of evaluating a single item."""
+
+ name: str
+ inputs: dict[str, Any] = field(default_factory=dict)
+ expected_output: Any = None
+ scores: dict[str, float] = field(default_factory=dict)
+ overall_score: float = 0.0
+ output: Any = None
+ justifications: dict[str, str] = field(default_factory=dict)
+ duration_ms: float | None = None
+ status: str = "pending" # pending | running | completed | failed
+ error: str | None = None
+ traces: list[dict[str, Any]] = field(default_factory=list)
+
+
+@dataclass
+class EvalRunState:
+ """Full state of an eval run."""
+
+ id: str = field(default_factory=lambda: str(uuid.uuid4()))
+ eval_set_id: str = ""
+ eval_set_name: str = ""
+ status: str = "pending" # pending | running | completed | failed
+ progress_completed: int = 0
+ progress_total: int = 0
+ overall_score: float | None = None
+ evaluator_scores: dict[str, float] = field(default_factory=dict)
+ results: list[EvalItemResult] = field(default_factory=list)
+ start_time: datetime | None = None
+ end_time: datetime | None = None
+
+ def to_summary(self) -> dict[str, Any]:
+ """Serialize to summary dict (no per-item results)."""
+ return {
+ "id": self.id,
+ "eval_set_id": self.eval_set_id,
+ "eval_set_name": self.eval_set_name,
+ "status": self.status,
+ "progress_completed": self.progress_completed,
+ "progress_total": self.progress_total,
+ "overall_score": self.overall_score,
+ "evaluator_scores": self.evaluator_scores,
+ "start_time": self.start_time.isoformat() if self.start_time else None,
+ "end_time": self.end_time.isoformat() if self.end_time else None,
+ }
+
+ def to_detail(self) -> dict[str, Any]:
+ """Serialize to detail dict (includes per-item results)."""
+ base = self.to_summary()
+ base["results"] = [
+ {
+ "name": r.name,
+ "inputs": r.inputs,
+ "expected_output": r.expected_output,
+ "scores": r.scores,
+ "overall_score": r.overall_score,
+ "output": str(r.output)
+ if isinstance(r.output, Exception)
+ else r.output,
+ "justifications": r.justifications,
+ "duration_ms": r.duration_ms,
+ "status": r.status,
+ "error": r.error,
+ "traces": r.traces,
+ }
+ for r in self.results
+ ]
+ return base
+
+ def start(self) -> None:
+ """Mark run as started."""
+ self.status = "running"
+ self.start_time = datetime.now(timezone.utc)
+
+ def complete(self) -> None:
+ """Mark run as completed, computing final scores."""
+ self.status = "completed"
+ self.end_time = datetime.now(timezone.utc)
+ self._compute_scores()
+
+ def fail(self) -> None:
+ """Mark run as failed."""
+ self.status = "failed"
+ self.end_time = datetime.now(timezone.utc)
+
+ def _compute_scores(self) -> None:
+ """Compute overall and per-evaluator scores from item results."""
+ scored = [r for r in self.results if r.status in ("completed", "failed")]
+ if not scored:
+ self.overall_score = 0.0
+ return
+
+ # Per-evaluator averages (failed items count as 0 for each evaluator)
+ evaluator_totals: dict[str, list[float]] = {}
+ for r in scored:
+ for ev_id, score in r.scores.items():
+ evaluator_totals.setdefault(ev_id, []).append(score)
+
+ failed = [r for r in scored if r.status == "failed"]
+ for ev_id in evaluator_totals:
+ for _ in failed:
+ evaluator_totals[ev_id].append(0.0)
+
+ self.evaluator_scores = {
+ ev_id: sum(scores) / len(scores)
+ for ev_id, scores in evaluator_totals.items()
+ }
+
+ # Overall = average of item overall_scores
+ self.overall_score = sum(r.overall_score for r in scored) / len(scored)
diff --git a/src/uipath/dev/server/__init__.py b/src/uipath/dev/server/__init__.py
index 2e3ba31..af3d30d 100644
--- a/src/uipath/dev/server/__init__.py
+++ b/src/uipath/dev/server/__init__.py
@@ -11,6 +11,7 @@
import time
import webbrowser
from collections.abc import Callable
+from pathlib import Path
from typing import Any
import uvicorn
@@ -19,13 +20,15 @@
from uipath.dev.models.data import (
ChatData,
- InterruptData,
LogData,
StateData,
TraceData,
)
+from uipath.dev.models.eval_data import EvalItemResult, EvalRunState
from uipath.dev.models.execution import ExecutionRun
from uipath.dev.server.debug_bridge import WebDebugBridge
+from uipath.dev.services.cli_agent import CliAgentService
+from uipath.dev.services.eval_service import EvalService
from uipath.dev.services.run_service import RunService
logger = logging.getLogger(__name__)
@@ -81,11 +84,25 @@ def __init__(
on_trace=self._on_trace,
on_chat=self._on_chat,
on_state=self._on_state,
- on_interrupt=self._on_interrupt,
debug_bridge_factory=lambda mode: WebDebugBridge(mode=mode),
on_run_removed=self.connection_manager.remove_run_subscriptions,
)
+ self.eval_service = EvalService(
+ runtime_factory=self.runtime_factory,
+ trace_manager=self.trace_manager,
+ on_eval_run_created=self._on_eval_run_created,
+ on_eval_run_progress=self._on_eval_run_progress,
+ on_eval_run_completed=self._on_eval_run_completed,
+ )
+
+ self.cli_agent_service = CliAgentService(
+ on_output=self._on_cli_agent_output,
+ on_exit=self._on_cli_agent_exit,
+ server_port=self.port,
+ server_host=self.host,
+ )
+
def create_app(self) -> Any:
"""Create and return a FastAPI application."""
from uipath.dev.server.app import create_app
@@ -102,6 +119,11 @@ async def run_async(self) -> None:
self.port = self._find_free_port(self.host, self.port)
app = self.create_app()
+ # write the server port to .uipath/dev-server.port so the MCP can discover it
+ port_file = Path(".uipath") / "dev-server.port"
+ port_file.parent.mkdir(exist_ok=True)
+ port_file.write_text(str(self.port))
+
base_url = f"http://{self.host}:{self.port}"
self._print_banner(base_url)
@@ -111,9 +133,8 @@ async def run_async(self) -> None:
daemon=True,
).start()
- # Start file watcher if factory_creator is available
- if self.factory_creator is not None:
- self._start_watcher()
+ # Start file watcher for editor auto-refresh and factory hot-reload
+ self._start_watcher()
config = uvicorn.Config(
app,
@@ -122,12 +143,19 @@ async def run_async(self) -> None:
log_level="warning",
)
server = uvicorn.Server(config)
- await server.serve()
+ try:
+ await server.serve()
+ finally:
+ await self.shutdown()
async def shutdown(self) -> None:
"""Clean up resources before shutting down."""
logger.info("Shutting down server resources...")
+ port_file = Path(".uipath") / "dev-server.port"
+ port_file.unlink(missing_ok=True)
self._stop_watcher()
+ # Stop any active CLI agent PTY sessions
+ await self.cli_agent_service.stop_all_sessions()
# Close any active WebSocket connections
await self.connection_manager.disconnect_all()
# Give threads time to finish
@@ -171,20 +199,50 @@ async def reload_factory(self) -> None:
del sys.modules[name]
logger.debug("Flushed %d user modules", len(to_remove))
- # Recreate factory
+ # Recreate factory — preserve state file across reloads so the dev
+ # server doesn't delete state.db while other processes may use it.
self.runtime_factory = self.factory_creator()
+ try:
+ self.runtime_factory.context.keep_state_file = True # type: ignore[attr-defined]
+ except AttributeError:
+ pass
self.run_service.runtime_factory = self.runtime_factory
await self.run_service.apply_factory_settings()
self.reload_pending = False
logger.debug("Factory reloaded successfully")
+ async def _auto_reload(self, py_files: list[str]) -> None:
+ """Auto-reload factory when Python files change.
+
+ If runs are active, broadcast reload event so the frontend shows a
+ manual-reload prompt instead.
+ """
+ active = [
+ r
+ for r in self.run_service.runs.values()
+ if r.status in ("pending", "running")
+ ]
+ if active:
+ logger.debug("Runs in progress — deferring reload to user")
+ self.reload_pending = True
+ self.connection_manager.broadcast_reload(py_files)
+ return
+
+ try:
+ await self.reload_factory()
+ self.connection_manager.broadcast_reload(py_files, reloaded=True)
+ except Exception:
+ logger.warning("Auto-reload failed", exc_info=True)
+ self.reload_pending = True
+ self.connection_manager.broadcast_reload(py_files)
+
def _start_watcher(self) -> None:
"""Start the file watcher background task."""
- from uipath.dev.server.watcher import watch_python_files
+ from uipath.dev.server.watcher import watch_project_files
self._watcher_stop = asyncio.Event()
self._watcher_task = asyncio.create_task(
- watch_python_files(
+ watch_project_files(
on_change=self._on_files_changed,
stop_event=self._watcher_stop,
)
@@ -200,8 +258,28 @@ def _stop_watcher(self) -> None:
def _on_files_changed(self, changed_files: list[str]) -> None:
"""Handle file change events from the watcher."""
- self.reload_pending = True
- self.connection_manager.broadcast_reload(changed_files)
+ # Convert to relative paths with forward slashes for frontend
+ cwd = os.getcwd()
+ relative_files = []
+ for f in changed_files:
+ try:
+ relative_files.append(os.path.relpath(f, cwd).replace("\\", "/"))
+ except ValueError:
+ continue # different drive on Windows
+
+ # Broadcast files.changed for editor auto-refresh
+ if relative_files:
+ self.connection_manager.broadcast_files_changed(relative_files)
+
+ # Factory hot-reload for Python files only (use normalized relative paths)
+ py_files = [f for f in relative_files if f.endswith((".py", ".pyx"))]
+ if py_files and self.factory_creator is not None:
+ loop = self.connection_manager._get_loop()
+
+ def _schedule_reload(files: list[str] = py_files) -> None:
+ asyncio.ensure_future(self._auto_reload(files))
+
+ loop.call_soon_threadsafe(_schedule_reload)
# ------------------------------------------------------------------
# Internal callbacks
@@ -223,14 +301,41 @@ def _on_chat(self, chat_data: ChatData) -> None:
"""Broadcast chat message to subscribed WebSocket clients."""
self.connection_manager.broadcast_chat(chat_data)
- def _on_interrupt(self, interrupt_data: InterruptData) -> None:
- """Broadcast chat interrupt to subscribed WebSocket clients."""
- self.connection_manager.broadcast_interrupt(interrupt_data)
-
def _on_state(self, state_data: StateData) -> None:
"""Broadcast state transition to subscribed WebSocket clients."""
self.connection_manager.broadcast_state(state_data)
+ def _on_eval_run_created(self, run: EvalRunState) -> None:
+ """Broadcast eval run created to all connected clients."""
+ self.connection_manager.broadcast_eval_run_created(run)
+
+ def _on_eval_run_progress(
+ self,
+ run_id: str,
+ completed: int,
+ total: int,
+ item_result: EvalItemResult | None,
+ ) -> None:
+ """Broadcast eval run progress to all connected clients."""
+ self.connection_manager.broadcast_eval_run_progress(
+ run_id, completed, total, item_result
+ )
+
+ def _on_eval_run_completed(self, run: EvalRunState) -> None:
+ """Broadcast eval run completed to all connected clients."""
+ self.connection_manager.broadcast_eval_run_completed(run)
+
+ def _on_cli_agent_output(self, session_id: str, data: bytes) -> None:
+ """Broadcast CLI agent PTY output as base64."""
+ import base64
+
+ encoded = base64.b64encode(data).decode("ascii")
+ self.connection_manager.broadcast_cli_agent_output(session_id, encoded)
+
+ def _on_cli_agent_exit(self, session_id: str, exit_code: int) -> None:
+ """Broadcast CLI agent process exit."""
+ self.connection_manager.broadcast_cli_agent_exit(session_id, exit_code)
+
@staticmethod
def _find_free_port(host: str, start_port: int, max_attempts: int = 100) -> int:
"""Find a free port starting from *start_port*.
diff --git a/src/uipath/dev/server/app.py b/src/uipath/dev/server/app.py
index d608338..6c7b0d6 100644
--- a/src/uipath/dev/server/app.py
+++ b/src/uipath/dev/server/app.py
@@ -2,6 +2,7 @@
from __future__ import annotations
+import asyncio
import logging
import os
from pathlib import Path
@@ -150,22 +151,65 @@ async def _config():
# Register routes
from uipath.dev.server.routes.entrypoints import router as entrypoints_router
+ from uipath.dev.server.routes.evals import router as evals_router
+ from uipath.dev.server.routes.evaluators import router as evaluators_router
+ from uipath.dev.server.routes.files import router as files_router
from uipath.dev.server.routes.graph import router as graph_router
from uipath.dev.server.routes.reload import router as reload_router
from uipath.dev.server.routes.runs import router as runs_router
from uipath.dev.server.ws.handler import router as ws_router
if auth_enabled:
- from uipath.dev.server.auth import restore_session
+ from uipath.dev.server.auth import get_auth_state, restore_session
from uipath.dev.server.routes.auth import router as auth_router
app.include_router(auth_router, prefix="/api")
restore_session()
+ # Reload the runtime factory when authentication completes so the
+ # newly-written credentials are picked up by subsequent runs.
+ def _on_authenticated() -> None:
+ async def _safe_reload() -> None:
+ # Wait for active runs to finish before reloading
+ while any(
+ r.status in ("pending", "running")
+ for r in server.run_service.runs.values()
+ ):
+ await asyncio.sleep(1)
+ await server.reload_factory()
+
+ def _on_reload_done(t: asyncio.Task[None]) -> None:
+ try:
+ t.result()
+ except asyncio.CancelledError:
+ pass
+ except Exception:
+ logger.exception("Factory reload after login failed")
+
+ task = asyncio.create_task(_safe_reload())
+ task.add_done_callback(_on_reload_done)
+
+ get_auth_state()._on_authenticated = _on_authenticated
+
app.include_router(entrypoints_router, prefix="/api")
app.include_router(runs_router, prefix="/api")
app.include_router(graph_router, prefix="/api")
app.include_router(reload_router, prefix="/api")
+ app.include_router(evaluators_router, prefix="/api")
+ app.include_router(evals_router, prefix="/api")
+ app.include_router(files_router, prefix="/api")
+
+ from uipath.dev.server.routes.cli_agent import router as cli_agent_router
+
+ app.include_router(cli_agent_router, prefix="/api")
+
+ from uipath.dev.server.routes.mcp import router as mcp_router
+
+ app.include_router(mcp_router, prefix="/api")
+
+ from uipath.dev.server.routes.statedb import router as statedb_router
+
+ app.include_router(statedb_router, prefix="/api")
app.include_router(ws_router)
# Auto-build frontend if source is available and build is stale
diff --git a/src/uipath/dev/server/auth.py b/src/uipath/dev/server/auth.py
index c76dd54..fd6dd6d 100644
--- a/src/uipath/dev/server/auth.py
+++ b/src/uipath/dev/server/auth.py
@@ -18,12 +18,14 @@
import socketserver
import threading
import time
+from collections.abc import Callable
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any
from urllib.parse import urlencode
import httpx
+from dotenv import load_dotenv
logger = logging.getLogger(__name__)
@@ -108,6 +110,8 @@ class AuthState:
_last_tenant: str | None = None
_last_org: dict[str, str] = field(default_factory=dict)
_last_environment: str | None = None
+ # Callback invoked after successful authentication
+ _on_authenticated: Callable[[], None] | None = None
# internal
_code_verifier: str | None = None
_state: str | None = None
@@ -127,9 +131,30 @@ def get_auth_state() -> AuthState:
def reset_auth_state() -> None:
- """Reset the auth state to its initial (unauthenticated) values."""
- global _auth
- _auth = AuthState()
+ """Reset the auth state to its initial (unauthenticated) values.
+
+ Preserves registered callbacks (e.g. ``_on_authenticated``).
+ """
+ _auth.status = "unauthenticated"
+ _auth.environment = "cloud"
+ _auth.token_data = {}
+ _auth.tenants = []
+ _auth.organization = {}
+ _auth.uipath_url = None
+ _auth._last_tenant = None
+ _auth._last_org = {}
+ _auth._last_environment = None
+ _auth._code_verifier = None
+ _auth._state = None
+ _auth._port = None
+ _auth._callback_server = None
+ if _auth._wait_task and not _auth._wait_task.done():
+ _auth._wait_task.cancel()
+ _auth._wait_task = None
+ if _auth._token_event:
+ _auth._token_event.set()
+ _auth._token_event = None
+ _auth._loop = None
# ---------------------------------------------------------------------------
@@ -989,13 +1014,52 @@ def select_tenant(tenant_name: str) -> dict[str, Any]:
return {"status": "authenticated", "uipath_url": auth.uipath_url}
+def _update_env_file(env_contents: dict[str, str]) -> None:
+ """Merge *env_contents* into the CWD ``.env`` file.
+
+ New keys take priority; existing keys not in *env_contents* are preserved.
+ Comments and blank lines are kept as-is.
+ """
+ env_path = Path.cwd() / ".env"
+ lines: list[str] = []
+ seen_keys: set[str] = set()
+
+ if env_path.exists():
+ with open(env_path) as f:
+ for raw_line in f:
+ stripped = raw_line.strip()
+ if stripped.startswith("#") or "=" not in stripped:
+ # Preserve comments and blank lines
+ lines.append(raw_line)
+ continue
+ key = stripped.split("=", 1)[0]
+ if key in env_contents:
+ lines.append(f"{key}={env_contents[key]}\n")
+ else:
+ lines.append(raw_line)
+ seen_keys.add(key)
+
+ # Append new keys that weren't already in the file
+ for key, value in env_contents.items():
+ if key not in seen_keys:
+ lines.append(f"{key}={value}\n")
+
+ with open(env_path, "w") as f:
+ f.writelines(lines)
+
+
def _finalize_tenant(auth: AuthState, tenant_name: str) -> None:
"""Write .env and os.environ with the resolved credentials."""
org_name = auth.organization.get("name", "")
+ org_id = auth.organization.get("id", "")
domain = f"https://{auth.environment}.uipath.com"
uipath_url = f"{domain}/{org_name}/{tenant_name}"
access_token = auth.token_data.get("access_token", "")
+ # Resolve tenant ID from the tenants list
+ tenant = next((t for t in auth.tenants if t["name"] == tenant_name), None)
+ tenant_id = tenant["id"] if tenant else ""
+
auth.uipath_url = uipath_url
auth.status = "authenticated"
@@ -1004,35 +1068,27 @@ def _finalize_tenant(auth: AuthState, tenant_name: str) -> None:
auth._last_org = dict(auth.organization)
auth._last_environment = auth.environment
- # Update os.environ
- os.environ["UIPATH_ACCESS_TOKEN"] = access_token
- os.environ["UIPATH_URL"] = uipath_url
-
- # Write/update .env file (preserving comments, blank lines, and ordering)
- env_path = Path.cwd() / ".env"
- lines: list[str] = []
- updated_keys: set[str] = set()
- new_values = {"UIPATH_ACCESS_TOKEN": access_token, "UIPATH_URL": uipath_url}
+ # Write .env using the same approach as `uipath auth`
+ _update_env_file(
+ {
+ "UIPATH_ACCESS_TOKEN": access_token,
+ "UIPATH_URL": uipath_url,
+ "UIPATH_TENANT_ID": tenant_id,
+ "UIPATH_ORGANIZATION_ID": org_id,
+ }
+ )
- if env_path.exists():
- with open(env_path) as f:
- for raw_line in f:
- stripped = raw_line.strip()
- if "=" in stripped and not stripped.startswith("#"):
- key = stripped.split("=", 1)[0]
- if key in new_values:
- lines.append(f"{key}={new_values[key]}\n")
- updated_keys.add(key)
- continue
- lines.append(raw_line)
-
- # Append any keys that weren't already in the file
- for key, value in new_values.items():
- if key not in updated_keys:
- lines.append(f"{key}={value}\n")
+ # Reload .env into os.environ (same as CLI root: cwd + override)
+ load_dotenv(
+ dotenv_path=os.path.join(os.getcwd(), ".env"),
+ override=True,
+ )
- with open(env_path, "w") as f:
- f.writelines(lines)
+ if auth._on_authenticated:
+ try:
+ auth._on_authenticated()
+ except Exception:
+ logger.exception("Error in post-authentication callback")
def logout() -> None:
@@ -1043,6 +1099,8 @@ def logout() -> None:
os.environ.pop("UIPATH_ACCESS_TOKEN", None)
os.environ.pop("UIPATH_URL", None)
+ os.environ.pop("UIPATH_TENANT_ID", None)
+ os.environ.pop("UIPATH_ORGANIZATION_ID", None)
reset_auth_state()
diff --git a/src/uipath/dev/server/frontend/package-lock.json b/src/uipath/dev/server/frontend/package-lock.json
index 30693ab..aca4b86 100644
--- a/src/uipath/dev/server/frontend/package-lock.json
+++ b/src/uipath/dev/server/frontend/package-lock.json
@@ -8,6 +8,9 @@
"name": "uipath-dev-frontend",
"version": "0.1.0",
"dependencies": {
+ "@monaco-editor/react": "^4.7.0",
+ "@xterm/addon-fit": "^0.11.0",
+ "@xterm/xterm": "^6.0.0",
"elkjs": "^0.11.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
@@ -22,6 +25,7 @@
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@vitejs/plugin-react": "^4.3.0",
+ "rollup": "^4.59.0",
"tailwindcss": "^4.0.0",
"typescript": "~5.7.0",
"vite": "^6.0.0"
@@ -801,6 +805,29 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "node_modules/@monaco-editor/loader": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.7.0.tgz",
+ "integrity": "sha512-gIwR1HrJrrx+vfyOhYmCZ0/JcWqG5kbfG7+d3f/C1LXk2EvzAbHSg3MQ5lO2sMlo9izoAZ04shohfKLVT6crVA==",
+ "license": "MIT",
+ "dependencies": {
+ "state-local": "^1.0.6"
+ }
+ },
+ "node_modules/@monaco-editor/react": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.7.0.tgz",
+ "integrity": "sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==",
+ "license": "MIT",
+ "dependencies": {
+ "@monaco-editor/loader": "^1.5.0"
+ },
+ "peerDependencies": {
+ "monaco-editor": ">= 0.25.0 < 1",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/@reactflow/background": {
"version": "11.3.14",
"resolved": "https://registry.npmjs.org/@reactflow/background/-/background-11.3.14.tgz",
@@ -1079,9 +1106,9 @@
"license": "MIT"
},
"node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz",
- "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz",
+ "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==",
"cpu": [
"arm"
],
@@ -1093,9 +1120,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz",
- "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz",
+ "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==",
"cpu": [
"arm64"
],
@@ -1107,9 +1134,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz",
- "integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz",
+ "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==",
"cpu": [
"arm64"
],
@@ -1121,9 +1148,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz",
- "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz",
+ "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==",
"cpu": [
"x64"
],
@@ -1135,9 +1162,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz",
- "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz",
+ "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==",
"cpu": [
"arm64"
],
@@ -1149,9 +1176,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz",
- "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz",
+ "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==",
"cpu": [
"x64"
],
@@ -1163,9 +1190,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz",
- "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz",
+ "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==",
"cpu": [
"arm"
],
@@ -1177,9 +1204,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz",
- "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz",
+ "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==",
"cpu": [
"arm"
],
@@ -1191,9 +1218,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz",
- "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz",
+ "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==",
"cpu": [
"arm64"
],
@@ -1205,9 +1232,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz",
- "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz",
+ "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==",
"cpu": [
"arm64"
],
@@ -1219,9 +1246,9 @@
]
},
"node_modules/@rollup/rollup-linux-loong64-gnu": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz",
- "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz",
+ "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==",
"cpu": [
"loong64"
],
@@ -1233,9 +1260,9 @@
]
},
"node_modules/@rollup/rollup-linux-loong64-musl": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz",
- "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz",
+ "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==",
"cpu": [
"loong64"
],
@@ -1247,9 +1274,9 @@
]
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz",
- "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz",
+ "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==",
"cpu": [
"ppc64"
],
@@ -1261,9 +1288,9 @@
]
},
"node_modules/@rollup/rollup-linux-ppc64-musl": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz",
- "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz",
+ "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==",
"cpu": [
"ppc64"
],
@@ -1275,9 +1302,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz",
- "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz",
+ "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==",
"cpu": [
"riscv64"
],
@@ -1289,9 +1316,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz",
- "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz",
+ "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==",
"cpu": [
"riscv64"
],
@@ -1303,9 +1330,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz",
- "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz",
+ "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==",
"cpu": [
"s390x"
],
@@ -1317,9 +1344,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz",
- "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz",
+ "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==",
"cpu": [
"x64"
],
@@ -1331,9 +1358,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz",
- "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz",
+ "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==",
"cpu": [
"x64"
],
@@ -1345,9 +1372,9 @@
]
},
"node_modules/@rollup/rollup-openbsd-x64": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz",
- "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz",
+ "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==",
"cpu": [
"x64"
],
@@ -1359,9 +1386,9 @@
]
},
"node_modules/@rollup/rollup-openharmony-arm64": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz",
- "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz",
+ "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==",
"cpu": [
"arm64"
],
@@ -1373,9 +1400,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz",
- "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz",
+ "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==",
"cpu": [
"arm64"
],
@@ -1387,9 +1414,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz",
- "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz",
+ "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==",
"cpu": [
"ia32"
],
@@ -1401,9 +1428,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-gnu": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz",
- "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz",
+ "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==",
"cpu": [
"x64"
],
@@ -1415,9 +1442,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz",
- "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz",
+ "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==",
"cpu": [
"x64"
],
@@ -1429,49 +1456,49 @@
]
},
"node_modules/@tailwindcss/node": {
- "version": "4.1.18",
- "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.18.tgz",
- "integrity": "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==",
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.1.tgz",
+ "integrity": "sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@jridgewell/remapping": "^2.3.4",
- "enhanced-resolve": "^5.18.3",
+ "@jridgewell/remapping": "^2.3.5",
+ "enhanced-resolve": "^5.19.0",
"jiti": "^2.6.1",
- "lightningcss": "1.30.2",
+ "lightningcss": "1.31.1",
"magic-string": "^0.30.21",
"source-map-js": "^1.2.1",
- "tailwindcss": "4.1.18"
+ "tailwindcss": "4.2.1"
}
},
"node_modules/@tailwindcss/oxide": {
- "version": "4.1.18",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.18.tgz",
- "integrity": "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==",
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.1.tgz",
+ "integrity": "sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw==",
"dev": true,
"license": "MIT",
"engines": {
- "node": ">= 10"
+ "node": ">= 20"
},
"optionalDependencies": {
- "@tailwindcss/oxide-android-arm64": "4.1.18",
- "@tailwindcss/oxide-darwin-arm64": "4.1.18",
- "@tailwindcss/oxide-darwin-x64": "4.1.18",
- "@tailwindcss/oxide-freebsd-x64": "4.1.18",
- "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18",
- "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18",
- "@tailwindcss/oxide-linux-arm64-musl": "4.1.18",
- "@tailwindcss/oxide-linux-x64-gnu": "4.1.18",
- "@tailwindcss/oxide-linux-x64-musl": "4.1.18",
- "@tailwindcss/oxide-wasm32-wasi": "4.1.18",
- "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18",
- "@tailwindcss/oxide-win32-x64-msvc": "4.1.18"
+ "@tailwindcss/oxide-android-arm64": "4.2.1",
+ "@tailwindcss/oxide-darwin-arm64": "4.2.1",
+ "@tailwindcss/oxide-darwin-x64": "4.2.1",
+ "@tailwindcss/oxide-freebsd-x64": "4.2.1",
+ "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.1",
+ "@tailwindcss/oxide-linux-arm64-gnu": "4.2.1",
+ "@tailwindcss/oxide-linux-arm64-musl": "4.2.1",
+ "@tailwindcss/oxide-linux-x64-gnu": "4.2.1",
+ "@tailwindcss/oxide-linux-x64-musl": "4.2.1",
+ "@tailwindcss/oxide-wasm32-wasi": "4.2.1",
+ "@tailwindcss/oxide-win32-arm64-msvc": "4.2.1",
+ "@tailwindcss/oxide-win32-x64-msvc": "4.2.1"
}
},
"node_modules/@tailwindcss/oxide-android-arm64": {
- "version": "4.1.18",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.18.tgz",
- "integrity": "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==",
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.1.tgz",
+ "integrity": "sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg==",
"cpu": [
"arm64"
],
@@ -1482,13 +1509,13 @@
"android"
],
"engines": {
- "node": ">= 10"
+ "node": ">= 20"
}
},
"node_modules/@tailwindcss/oxide-darwin-arm64": {
- "version": "4.1.18",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.18.tgz",
- "integrity": "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==",
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.1.tgz",
+ "integrity": "sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw==",
"cpu": [
"arm64"
],
@@ -1499,13 +1526,13 @@
"darwin"
],
"engines": {
- "node": ">= 10"
+ "node": ">= 20"
}
},
"node_modules/@tailwindcss/oxide-darwin-x64": {
- "version": "4.1.18",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.18.tgz",
- "integrity": "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==",
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.1.tgz",
+ "integrity": "sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw==",
"cpu": [
"x64"
],
@@ -1516,13 +1543,13 @@
"darwin"
],
"engines": {
- "node": ">= 10"
+ "node": ">= 20"
}
},
"node_modules/@tailwindcss/oxide-freebsd-x64": {
- "version": "4.1.18",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.18.tgz",
- "integrity": "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==",
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.1.tgz",
+ "integrity": "sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA==",
"cpu": [
"x64"
],
@@ -1533,13 +1560,13 @@
"freebsd"
],
"engines": {
- "node": ">= 10"
+ "node": ">= 20"
}
},
"node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
- "version": "4.1.18",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.18.tgz",
- "integrity": "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==",
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.1.tgz",
+ "integrity": "sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw==",
"cpu": [
"arm"
],
@@ -1550,13 +1577,13 @@
"linux"
],
"engines": {
- "node": ">= 10"
+ "node": ">= 20"
}
},
"node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
- "version": "4.1.18",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.18.tgz",
- "integrity": "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==",
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.1.tgz",
+ "integrity": "sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ==",
"cpu": [
"arm64"
],
@@ -1567,13 +1594,13 @@
"linux"
],
"engines": {
- "node": ">= 10"
+ "node": ">= 20"
}
},
"node_modules/@tailwindcss/oxide-linux-arm64-musl": {
- "version": "4.1.18",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.18.tgz",
- "integrity": "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==",
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.1.tgz",
+ "integrity": "sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==",
"cpu": [
"arm64"
],
@@ -1584,13 +1611,13 @@
"linux"
],
"engines": {
- "node": ">= 10"
+ "node": ">= 20"
}
},
"node_modules/@tailwindcss/oxide-linux-x64-gnu": {
- "version": "4.1.18",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.18.tgz",
- "integrity": "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==",
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.1.tgz",
+ "integrity": "sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==",
"cpu": [
"x64"
],
@@ -1601,13 +1628,13 @@
"linux"
],
"engines": {
- "node": ">= 10"
+ "node": ">= 20"
}
},
"node_modules/@tailwindcss/oxide-linux-x64-musl": {
- "version": "4.1.18",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.18.tgz",
- "integrity": "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==",
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.1.tgz",
+ "integrity": "sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==",
"cpu": [
"x64"
],
@@ -1618,13 +1645,13 @@
"linux"
],
"engines": {
- "node": ">= 10"
+ "node": ">= 20"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi": {
- "version": "4.1.18",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.18.tgz",
- "integrity": "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==",
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.1.tgz",
+ "integrity": "sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==",
"bundleDependencies": [
"@napi-rs/wasm-runtime",
"@emnapi/core",
@@ -1640,21 +1667,21 @@
"license": "MIT",
"optional": true,
"dependencies": {
- "@emnapi/core": "^1.7.1",
- "@emnapi/runtime": "^1.7.1",
+ "@emnapi/core": "^1.8.1",
+ "@emnapi/runtime": "^1.8.1",
"@emnapi/wasi-threads": "^1.1.0",
- "@napi-rs/wasm-runtime": "^1.1.0",
+ "@napi-rs/wasm-runtime": "^1.1.1",
"@tybys/wasm-util": "^0.10.1",
- "tslib": "^2.4.0"
+ "tslib": "^2.8.1"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
- "version": "4.1.18",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.18.tgz",
- "integrity": "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==",
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.1.tgz",
+ "integrity": "sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA==",
"cpu": [
"arm64"
],
@@ -1665,13 +1692,13 @@
"win32"
],
"engines": {
- "node": ">= 10"
+ "node": ">= 20"
}
},
"node_modules/@tailwindcss/oxide-win32-x64-msvc": {
- "version": "4.1.18",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.18.tgz",
- "integrity": "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==",
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.1.tgz",
+ "integrity": "sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ==",
"cpu": [
"x64"
],
@@ -1682,19 +1709,19 @@
"win32"
],
"engines": {
- "node": ">= 10"
+ "node": ">= 20"
}
},
"node_modules/@tailwindcss/vite": {
- "version": "4.1.18",
- "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.18.tgz",
- "integrity": "sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA==",
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.2.1.tgz",
+ "integrity": "sha512-TBf2sJjYeb28jD2U/OhwdW0bbOsxkWPwQ7SrqGf9sVcoYwZj7rkXljroBO9wKBut9XnmQLXanuDUeqQK0lGg/w==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@tailwindcss/node": "4.1.18",
- "@tailwindcss/oxide": "4.1.18",
- "tailwindcss": "4.1.18"
+ "@tailwindcss/node": "4.2.1",
+ "@tailwindcss/oxide": "4.2.1",
+ "tailwindcss": "4.2.1"
},
"peerDependencies": {
"vite": "^5.2.0 || ^6 || ^7"
@@ -2053,9 +2080,9 @@
"license": "MIT"
},
"node_modules/@types/react": {
- "version": "19.2.13",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.13.tgz",
- "integrity": "sha512-KkiJeU6VbYbUOp5ITMIc7kBfqlYkKA5KhEHVrGMmUUMt7NeaZg65ojdPk+FtNrBAOXNVM5QM72jnADjM+XVRAQ==",
+ "version": "19.2.14",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
+ "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
"license": "MIT",
"dependencies": {
"csstype": "^3.2.2"
@@ -2071,6 +2098,14 @@
"@types/react": "^19.2.0"
}
},
+ "node_modules/@types/trusted-types": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
+ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
+ "license": "MIT",
+ "optional": true,
+ "peer": true
+ },
"node_modules/@types/unist": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
@@ -2104,6 +2139,21 @@
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
}
},
+ "node_modules/@xterm/addon-fit": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.11.0.tgz",
+ "integrity": "sha512-jYcgT6xtVYhnhgxh3QgYDnnNMYTcf8ElbxxFzX0IZo+vabQqSPAjC3c1wJrKB5E19VwQei89QCiZZP86DCPF7g==",
+ "license": "MIT"
+ },
+ "node_modules/@xterm/xterm": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-6.0.0.tgz",
+ "integrity": "sha512-TQwDdQGtwwDt+2cgKDLn0IRaSxYu1tSUjgKarSDkUM0ZNiSRXFpjxEsvc/Zgc5kq5omJ+V0a8/kIM2WD3sMOYg==",
+ "license": "MIT",
+ "workspaces": [
+ "addons/*"
+ ]
+ },
"node_modules/bail": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
@@ -2115,13 +2165,16 @@
}
},
"node_modules/baseline-browser-mapping": {
- "version": "2.9.19",
- "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz",
- "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==",
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz",
+ "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==",
"dev": true,
"license": "Apache-2.0",
"bin": {
- "baseline-browser-mapping": "dist/cli.js"
+ "baseline-browser-mapping": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
}
},
"node_modules/browserslist": {
@@ -2159,9 +2212,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001769",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz",
- "integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==",
+ "version": "1.0.30001775",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001775.tgz",
+ "integrity": "sha512-s3Qv7Lht9zbVKE9XoTyRG6wVDCKdtOFIjBGg3+Yhn6JaytuNKPIjBMTMIY1AnOH3seL5mvF+x33oGAyK3hVt3A==",
"dev": true,
"funding": [
{
@@ -2425,10 +2478,20 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/dompurify": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.7.tgz",
+ "integrity": "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==",
+ "license": "(MPL-2.0 OR Apache-2.0)",
+ "peer": true,
+ "optionalDependencies": {
+ "@types/trusted-types": "^2.0.7"
+ }
+ },
"node_modules/electron-to-chromium": {
- "version": "1.5.286",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz",
- "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==",
+ "version": "1.5.302",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz",
+ "integrity": "sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==",
"dev": true,
"license": "ISC"
},
@@ -2439,9 +2502,9 @@
"license": "EPL-2.0"
},
"node_modules/enhanced-resolve": {
- "version": "5.19.0",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz",
- "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==",
+ "version": "5.20.0",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.0.tgz",
+ "integrity": "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2776,9 +2839,9 @@
}
},
"node_modules/lightningcss": {
- "version": "1.30.2",
- "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz",
- "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==",
+ "version": "1.31.1",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.31.1.tgz",
+ "integrity": "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==",
"dev": true,
"license": "MPL-2.0",
"dependencies": {
@@ -2792,23 +2855,23 @@
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
- "lightningcss-android-arm64": "1.30.2",
- "lightningcss-darwin-arm64": "1.30.2",
- "lightningcss-darwin-x64": "1.30.2",
- "lightningcss-freebsd-x64": "1.30.2",
- "lightningcss-linux-arm-gnueabihf": "1.30.2",
- "lightningcss-linux-arm64-gnu": "1.30.2",
- "lightningcss-linux-arm64-musl": "1.30.2",
- "lightningcss-linux-x64-gnu": "1.30.2",
- "lightningcss-linux-x64-musl": "1.30.2",
- "lightningcss-win32-arm64-msvc": "1.30.2",
- "lightningcss-win32-x64-msvc": "1.30.2"
+ "lightningcss-android-arm64": "1.31.1",
+ "lightningcss-darwin-arm64": "1.31.1",
+ "lightningcss-darwin-x64": "1.31.1",
+ "lightningcss-freebsd-x64": "1.31.1",
+ "lightningcss-linux-arm-gnueabihf": "1.31.1",
+ "lightningcss-linux-arm64-gnu": "1.31.1",
+ "lightningcss-linux-arm64-musl": "1.31.1",
+ "lightningcss-linux-x64-gnu": "1.31.1",
+ "lightningcss-linux-x64-musl": "1.31.1",
+ "lightningcss-win32-arm64-msvc": "1.31.1",
+ "lightningcss-win32-x64-msvc": "1.31.1"
}
},
"node_modules/lightningcss-android-arm64": {
- "version": "1.30.2",
- "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz",
- "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==",
+ "version": "1.31.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.31.1.tgz",
+ "integrity": "sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==",
"cpu": [
"arm64"
],
@@ -2827,9 +2890,9 @@
}
},
"node_modules/lightningcss-darwin-arm64": {
- "version": "1.30.2",
- "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz",
- "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==",
+ "version": "1.31.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.31.1.tgz",
+ "integrity": "sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==",
"cpu": [
"arm64"
],
@@ -2848,9 +2911,9 @@
}
},
"node_modules/lightningcss-darwin-x64": {
- "version": "1.30.2",
- "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz",
- "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==",
+ "version": "1.31.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.31.1.tgz",
+ "integrity": "sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==",
"cpu": [
"x64"
],
@@ -2869,9 +2932,9 @@
}
},
"node_modules/lightningcss-freebsd-x64": {
- "version": "1.30.2",
- "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz",
- "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==",
+ "version": "1.31.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.31.1.tgz",
+ "integrity": "sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==",
"cpu": [
"x64"
],
@@ -2890,9 +2953,9 @@
}
},
"node_modules/lightningcss-linux-arm-gnueabihf": {
- "version": "1.30.2",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz",
- "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==",
+ "version": "1.31.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.31.1.tgz",
+ "integrity": "sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==",
"cpu": [
"arm"
],
@@ -2911,9 +2974,9 @@
}
},
"node_modules/lightningcss-linux-arm64-gnu": {
- "version": "1.30.2",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz",
- "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==",
+ "version": "1.31.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.31.1.tgz",
+ "integrity": "sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==",
"cpu": [
"arm64"
],
@@ -2932,9 +2995,9 @@
}
},
"node_modules/lightningcss-linux-arm64-musl": {
- "version": "1.30.2",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz",
- "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==",
+ "version": "1.31.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.31.1.tgz",
+ "integrity": "sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==",
"cpu": [
"arm64"
],
@@ -2953,9 +3016,9 @@
}
},
"node_modules/lightningcss-linux-x64-gnu": {
- "version": "1.30.2",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz",
- "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==",
+ "version": "1.31.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.31.1.tgz",
+ "integrity": "sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==",
"cpu": [
"x64"
],
@@ -2974,9 +3037,9 @@
}
},
"node_modules/lightningcss-linux-x64-musl": {
- "version": "1.30.2",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz",
- "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==",
+ "version": "1.31.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.31.1.tgz",
+ "integrity": "sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==",
"cpu": [
"x64"
],
@@ -2995,9 +3058,9 @@
}
},
"node_modules/lightningcss-win32-arm64-msvc": {
- "version": "1.30.2",
- "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz",
- "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==",
+ "version": "1.31.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.31.1.tgz",
+ "integrity": "sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==",
"cpu": [
"arm64"
],
@@ -3016,9 +3079,9 @@
}
},
"node_modules/lightningcss-win32-x64-msvc": {
- "version": "1.30.2",
- "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz",
- "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==",
+ "version": "1.31.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.31.1.tgz",
+ "integrity": "sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==",
"cpu": [
"x64"
],
@@ -3091,6 +3154,19 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/marked": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-14.0.0.tgz",
+ "integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==",
+ "license": "MIT",
+ "peer": true,
+ "bin": {
+ "marked": "bin/marked.js"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
"node_modules/mdast-util-find-and-replace": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz",
@@ -3108,9 +3184,9 @@
}
},
"node_modules/mdast-util-from-markdown": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz",
- "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz",
+ "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==",
"license": "MIT",
"dependencies": {
"@types/mdast": "^4.0.0",
@@ -3924,6 +4000,17 @@
],
"license": "MIT"
},
+ "node_modules/monaco-editor": {
+ "version": "0.55.1",
+ "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.55.1.tgz",
+ "integrity": "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "dompurify": "3.2.7",
+ "marked": "14.0.0"
+ }
+ },
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -4200,9 +4287,9 @@
}
},
"node_modules/rollup": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz",
- "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz",
+ "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4216,31 +4303,31 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.57.1",
- "@rollup/rollup-android-arm64": "4.57.1",
- "@rollup/rollup-darwin-arm64": "4.57.1",
- "@rollup/rollup-darwin-x64": "4.57.1",
- "@rollup/rollup-freebsd-arm64": "4.57.1",
- "@rollup/rollup-freebsd-x64": "4.57.1",
- "@rollup/rollup-linux-arm-gnueabihf": "4.57.1",
- "@rollup/rollup-linux-arm-musleabihf": "4.57.1",
- "@rollup/rollup-linux-arm64-gnu": "4.57.1",
- "@rollup/rollup-linux-arm64-musl": "4.57.1",
- "@rollup/rollup-linux-loong64-gnu": "4.57.1",
- "@rollup/rollup-linux-loong64-musl": "4.57.1",
- "@rollup/rollup-linux-ppc64-gnu": "4.57.1",
- "@rollup/rollup-linux-ppc64-musl": "4.57.1",
- "@rollup/rollup-linux-riscv64-gnu": "4.57.1",
- "@rollup/rollup-linux-riscv64-musl": "4.57.1",
- "@rollup/rollup-linux-s390x-gnu": "4.57.1",
- "@rollup/rollup-linux-x64-gnu": "4.57.1",
- "@rollup/rollup-linux-x64-musl": "4.57.1",
- "@rollup/rollup-openbsd-x64": "4.57.1",
- "@rollup/rollup-openharmony-arm64": "4.57.1",
- "@rollup/rollup-win32-arm64-msvc": "4.57.1",
- "@rollup/rollup-win32-ia32-msvc": "4.57.1",
- "@rollup/rollup-win32-x64-gnu": "4.57.1",
- "@rollup/rollup-win32-x64-msvc": "4.57.1",
+ "@rollup/rollup-android-arm-eabi": "4.59.0",
+ "@rollup/rollup-android-arm64": "4.59.0",
+ "@rollup/rollup-darwin-arm64": "4.59.0",
+ "@rollup/rollup-darwin-x64": "4.59.0",
+ "@rollup/rollup-freebsd-arm64": "4.59.0",
+ "@rollup/rollup-freebsd-x64": "4.59.0",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.59.0",
+ "@rollup/rollup-linux-arm-musleabihf": "4.59.0",
+ "@rollup/rollup-linux-arm64-gnu": "4.59.0",
+ "@rollup/rollup-linux-arm64-musl": "4.59.0",
+ "@rollup/rollup-linux-loong64-gnu": "4.59.0",
+ "@rollup/rollup-linux-loong64-musl": "4.59.0",
+ "@rollup/rollup-linux-ppc64-gnu": "4.59.0",
+ "@rollup/rollup-linux-ppc64-musl": "4.59.0",
+ "@rollup/rollup-linux-riscv64-gnu": "4.59.0",
+ "@rollup/rollup-linux-riscv64-musl": "4.59.0",
+ "@rollup/rollup-linux-s390x-gnu": "4.59.0",
+ "@rollup/rollup-linux-x64-gnu": "4.59.0",
+ "@rollup/rollup-linux-x64-musl": "4.59.0",
+ "@rollup/rollup-openbsd-x64": "4.59.0",
+ "@rollup/rollup-openharmony-arm64": "4.59.0",
+ "@rollup/rollup-win32-arm64-msvc": "4.59.0",
+ "@rollup/rollup-win32-ia32-msvc": "4.59.0",
+ "@rollup/rollup-win32-x64-gnu": "4.59.0",
+ "@rollup/rollup-win32-x64-msvc": "4.59.0",
"fsevents": "~2.3.2"
}
},
@@ -4280,6 +4367,12 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/state-local": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz",
+ "integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==",
+ "license": "MIT"
+ },
"node_modules/stringify-entities": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
@@ -4313,9 +4406,9 @@
}
},
"node_modules/tailwindcss": {
- "version": "4.1.18",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz",
- "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==",
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.1.tgz",
+ "integrity": "sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==",
"dev": true,
"license": "MIT"
},
diff --git a/src/uipath/dev/server/frontend/package.json b/src/uipath/dev/server/frontend/package.json
index 9638678..1bd7b62 100644
--- a/src/uipath/dev/server/frontend/package.json
+++ b/src/uipath/dev/server/frontend/package.json
@@ -9,6 +9,9 @@
"preview": "vite preview"
},
"dependencies": {
+ "@monaco-editor/react": "^4.7.0",
+ "@xterm/addon-fit": "^0.11.0",
+ "@xterm/xterm": "^6.0.0",
"elkjs": "^0.11.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
@@ -23,6 +26,7 @@
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@vitejs/plugin-react": "^4.3.0",
+ "rollup": "^4.59.0",
"tailwindcss": "^4.0.0",
"typescript": "~5.7.0",
"vite": "^6.0.0"
diff --git a/src/uipath/dev/server/frontend/src/App.tsx b/src/uipath/dev/server/frontend/src/App.tsx
index 8249df3..cf21e22 100644
--- a/src/uipath/dev/server/frontend/src/App.tsx
+++ b/src/uipath/dev/server/frontend/src/App.tsx
@@ -6,18 +6,34 @@ import { useWebSocket } from "./store/useWebSocket";
import { listRuns, listEntrypoints, getRun } from "./api/client";
import type { RunDetail } from "./types/run";
import { useHashRoute } from "./hooks/useHashRoute";
+import type { Section } from "./hooks/useHashRoute";
import { useIsMobile } from "./hooks/useIsMobile";
-import Sidebar from "./components/layout/Sidebar";
+import ActivityBar from "./components/layout/ActivityBar";
+import DebugSidebar from "./components/layout/DebugSidebar";
import StatusBar from "./components/layout/StatusBar";
import NewRunPanel from "./components/runs/NewRunPanel";
import SetupView from "./components/runs/SetupView";
import RunDetailsPanel from "./components/runs/RunDetailsPanel";
import ReloadToast from "./components/shared/ReloadToast";
+import ToastContainer from "./components/shared/ToastContainer";
+import { useEvalStore } from "./store/useEvalStore";
+import { listEvalSets, listEvaluators, listEvalRuns, listLocalEvaluators } from "./api/eval-client";
+import EvalsSidebar from "./components/evals/EvalsSidebar";
+import EvalSetDetail from "./components/evals/EvalSetDetail";
+import EvalRunResults from "./components/evals/EvalRunResults";
+import CreateEvalSetView from "./components/evals/CreateEvalSetView";
+import EvaluatorsSidebar from "./components/evaluators/EvaluatorsSidebar";
+import EvaluatorsView from "./components/evaluators/EvaluatorDetail";
+import CreateEvaluatorView from "./components/evaluators/CreateEvaluatorView";
+import ExplorerSidebar from "./components/explorer/ExplorerSidebar";
+import ExplorerContent from "./components/explorer/ExplorerContent";
export default function App() {
const ws = useWebSocket();
const isMobile = useIsMobile();
const [sidebarOpen, setSidebarOpen] = useState(false);
+ const [sidebarWidth, setSidebarWidth] = useState(248);
+ const [isDraggingSidebar, setIsDraggingSidebar] = useState(false);
const {
runs,
selectedRunId,
@@ -33,14 +49,30 @@ export default function App() {
setActiveNode,
removeActiveNode,
} = useRunStore();
- const { view, runId: routeRunId, setupEntrypoint, setupMode, navigate } = useHashRoute();
+ const {
+ section,
+ view,
+ runId: routeRunId,
+ setupEntrypoint,
+ setupMode,
+ evalCreating,
+ evalSetId,
+ evalRunId,
+ evalRunItemName,
+ evaluatorCreateType,
+ evaluatorId,
+ evaluatorFilter,
+ navigate,
+ } = useHashRoute();
+
+ const { setEvalSets, setEvaluators, setLocalEvaluators, setEvalRuns } = useEvalStore();
// Sync route runId → store selection
useEffect(() => {
- if (view === "details" && routeRunId && routeRunId !== selectedRunId) {
+ if (section === "debug" && view === "details" && routeRunId && routeRunId !== selectedRunId) {
selectRun(routeRunId);
}
- }, [view, routeRunId, selectedRunId, selectRun]);
+ }, [section, view, routeRunId, selectedRunId, selectRun]);
// Load existing runs, entrypoints, auth status, and config on mount
const initAuth = useAuthStore((s) => s.init);
@@ -54,6 +86,49 @@ export default function App() {
initConfig();
}, [setRuns, setEntrypoints, initAuth, initConfig]);
+ // Load eval data when switching to evals/evaluators section
+ useEffect(() => {
+ if (section === "evals") {
+ listEvalSets().then((sets) => setEvalSets(sets)).catch(console.error);
+ listEvalRuns().then((runs) => setEvalRuns(runs)).catch(console.error);
+ }
+ if (section === "evals" || section === "evaluators") {
+ listEvaluators().then((evs) => setEvaluators(evs)).catch(console.error);
+ listLocalEvaluators().then((evs) => setLocalEvaluators(evs)).catch(console.error);
+ }
+ }, [section, setEvalSets, setEvaluators, setLocalEvaluators, setEvalRuns]);
+
+ // Auto-select latest run or first eval set when navigating to evals with no selection
+ const evalSets = useEvalStore((s) => s.evalSets);
+ const evalRuns = useEvalStore((s) => s.evalRuns);
+ useEffect(() => {
+ if (section !== "evals" || evalCreating || evalSetId || evalRunId) return;
+ // Pick latest run by start_time
+ const runs = Object.values(evalRuns).sort(
+ (a, b) => new Date(b.start_time ?? 0).getTime() - new Date(a.start_time ?? 0).getTime(),
+ );
+ if (runs.length > 0) {
+ navigate(`#/evals/runs/${runs[0].id}`);
+ return;
+ }
+ // Fallback: first eval set
+ const sets = Object.values(evalSets);
+ if (sets.length > 0) {
+ navigate(`#/evals/sets/${sets[0].id}`);
+ }
+ }, [section, evalCreating, evalSetId, evalRunId, evalRuns, evalSets, navigate]);
+
+ // Keyboard shortcuts
+ useEffect(() => {
+ const onKeyDown = (e: KeyboardEvent) => {
+ if (e.key === "Escape" && sidebarOpen) {
+ setSidebarOpen(false);
+ }
+ };
+ window.addEventListener("keydown", onKeyDown);
+ return () => window.removeEventListener("keydown", onKeyDown);
+ }, [sidebarOpen]);
+
const selectedRun = selectedRunId ? runs[selectedRunId] : null;
// Shared helper: apply a full run detail response to the store
@@ -169,70 +244,233 @@ export default function App() {
}, [selectedRunId, selectedRun?.status, applyRunDetail]);
const handleRunCreated = (runId: string) => {
- navigate(`#/runs/${runId}/traces`);
+ navigate(`#/debug/runs/${runId}/traces`);
selectRun(runId);
setSidebarOpen(false);
};
const handleSelectRun = (runId: string) => {
- navigate(`#/runs/${runId}/traces`);
+ navigate(`#/debug/runs/${runId}/traces`);
selectRun(runId);
setSidebarOpen(false);
};
const handleNewRun = () => {
- navigate("#/new");
+ navigate("#/debug/new");
setSidebarOpen(false);
};
+ const handleSectionChange = (s: Section) => {
+ if (s === "debug") navigate("#/debug/new");
+ else if (s === "evals") navigate("#/evals");
+ else if (s === "evaluators") navigate("#/evaluators");
+ else if (s === "explorer") navigate("#/explorer");
+ };
+
+ // --- Sidebar col resize ---
+ const onSidebarResizeStart = useCallback((e: React.MouseEvent | React.TouchEvent) => {
+ e.preventDefault();
+ setIsDraggingSidebar(true);
+
+ const startX = "touches" in e ? e.touches[0].clientX : e.clientX;
+ const startW = sidebarWidth;
+
+ const onMove = (ev: MouseEvent | TouchEvent) => {
+ const clientX = "touches" in ev ? ev.touches[0].clientX : ev.clientX;
+ const newW = Math.max(200, Math.min(480, startW + (clientX - startX)));
+ setSidebarWidth(newW);
+ };
+
+ const onUp = () => {
+ setIsDraggingSidebar(false);
+ document.removeEventListener("mousemove", onMove);
+ document.removeEventListener("mouseup", onUp);
+ document.removeEventListener("touchmove", onMove);
+ document.removeEventListener("touchend", onUp);
+ document.body.style.cursor = "";
+ document.body.style.userSelect = "";
+ };
+
+ document.body.style.cursor = "col-resize";
+ document.body.style.userSelect = "none";
+ document.addEventListener("mousemove", onMove);
+ document.addEventListener("mouseup", onUp);
+ document.addEventListener("touchmove", onMove, { passive: false });
+ document.addEventListener("touchend", onUp);
+ }, [sidebarWidth]);
+
+ // --- Render main content based on section ---
+ const renderMainContent = () => {
+ if (section === "explorer") {
+ return ;
+ }
+
+ if (section === "evals") {
+ if (evalCreating) return ;
+ if (evalRunId) return ;
+ if (evalSetId) return ;
+ return ;
+ }
+
+ if (section === "evaluators") {
+ if (evaluatorCreateType) {
+ return ;
+ }
+ return ;
+ }
+
+ // Debug section
+ if (view === "new") {
+ return ;
+ }
+ if (view === "setup" && setupEntrypoint && setupMode) {
+ return (
+
+ );
+ }
+ if (selectedRun) {
+ return ;
+ }
+ return (
+
+ Select a run or create a new one
+
+ );
+ };
+
+ // --- Mobile layout ---
+ if (isMobile) {
+ return (
+
+
+ {!sidebarOpen && (
+
+ )}
+ {sidebarOpen && (
+ <>
+
setSidebarOpen(false)}
+ />
+
+ >
+ )}
+
+ {renderMainContent()}
+
+
+
+
+
+
+ );
+ }
+
+ // --- Desktop layout ---
return (
-
- {/* Mobile hamburger button */}
- {isMobile && !sidebarOpen && (
-
- )}
-
setSidebarOpen(false)}
+
+ {/* Left aside: shared header + ActivityBar + section sidebar */}
+
+
- {view === "new" ? (
-
- ) : view === "setup" && setupEntrypoint && setupMode ? (
-
- ) : selectedRun ? (
-
- ) : (
-
- Select a run or create a new one
-
- )}
+ {renderMainContent()}
+
);
}
diff --git a/src/uipath/dev/server/frontend/src/api/cli-agent-client.ts b/src/uipath/dev/server/frontend/src/api/cli-agent-client.ts
new file mode 100644
index 0000000..85d0bbb
--- /dev/null
+++ b/src/uipath/dev/server/frontend/src/api/cli-agent-client.ts
@@ -0,0 +1,11 @@
+export interface CliAgentInfo {
+ id: string;
+ name: string;
+ installed: boolean;
+}
+
+export async function listCliAgents(): Promise
{
+ const res = await fetch("/api/cli-agent/available");
+ if (!res.ok) return [];
+ return res.json();
+}
diff --git a/src/uipath/dev/server/frontend/src/api/cli-terminal-bridge.ts b/src/uipath/dev/server/frontend/src/api/cli-terminal-bridge.ts
new file mode 100644
index 0000000..3a917bb
--- /dev/null
+++ b/src/uipath/dev/server/frontend/src/api/cli-terminal-bridge.ts
@@ -0,0 +1,21 @@
+/**
+ * Module-level registry for routing WebSocket PTY output to xterm instances.
+ * The CliAgentTerminal component registers its writer on mount and
+ * deregisters on unmount.
+ */
+
+type TerminalWriter = (data: Uint8Array) => void;
+
+const writers = new Map();
+
+export function registerTerminalWriter(sessionId: string, writer: TerminalWriter): void {
+ writers.set(sessionId, writer);
+}
+
+export function unregisterTerminalWriter(sessionId: string): void {
+ writers.delete(sessionId);
+}
+
+export function getTerminalWriter(sessionId: string): TerminalWriter | undefined {
+ return writers.get(sessionId);
+}
diff --git a/src/uipath/dev/server/frontend/src/api/eval-client.ts b/src/uipath/dev/server/frontend/src/api/eval-client.ts
new file mode 100644
index 0000000..ae24d7f
--- /dev/null
+++ b/src/uipath/dev/server/frontend/src/api/eval-client.ts
@@ -0,0 +1,185 @@
+import type { EvaluatorInfo, LocalEvaluator, LlmModel, EvalSetSummary, EvalSetDetail, EvalItem, EvalRunSummary, EvalRunDetail } from "../types/eval";
+
+const BASE = "/api";
+
+async function fetchJson(url: string, options?: RequestInit): Promise {
+ const res = await fetch(url, options);
+ if (!res.ok) {
+ let errorDetail;
+ try {
+ const body = await res.json();
+ errorDetail = body.detail || res.statusText;
+ } catch {
+ errorDetail = res.statusText;
+ }
+ const error = new Error(`HTTP ${res.status}`);
+ (error as any).detail = errorDetail;
+ (error as any).status = res.status;
+ throw error;
+ }
+ return res.json();
+}
+
+export async function listEvaluators(): Promise {
+ return fetchJson(`${BASE}/evaluators`);
+}
+
+export async function listLlmModels(): Promise {
+ return fetchJson(`${BASE}/llm-models`);
+}
+
+export async function listEvalSets(): Promise {
+ return fetchJson(`${BASE}/eval-sets`);
+}
+
+export async function createEvalSet(body: {
+ name: string;
+ evaluator_refs: string[];
+}): Promise {
+ return fetchJson(`${BASE}/eval-sets`, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(body),
+ });
+}
+
+export async function addEvalItem(
+ evalSetId: string,
+ item: {
+ name: string;
+ inputs: Record;
+ expected_output: unknown;
+ evaluation_criterias?: Record>;
+ },
+): Promise {
+ return fetchJson(`${BASE}/eval-sets/${encodeURIComponent(evalSetId)}/items`, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(item),
+ });
+}
+
+export async function updateEvalItem(
+ evalSetId: string,
+ itemName: string,
+ body: {
+ name?: string;
+ inputs?: Record;
+ expected_output?: unknown;
+ expected_behavior?: string;
+ simulation_instructions?: string;
+ },
+): Promise {
+ return fetchJson(
+ `${BASE}/eval-sets/${encodeURIComponent(evalSetId)}/items/${encodeURIComponent(itemName)}`,
+ {
+ method: "PUT",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(body),
+ },
+ );
+}
+
+export async function updateEvalItemEvaluators(
+ evalSetId: string,
+ itemName: string,
+ evaluationCriterias: Record,
+): Promise {
+ return fetchJson(
+ `${BASE}/eval-sets/${encodeURIComponent(evalSetId)}/items/${encodeURIComponent(itemName)}/evaluators`,
+ {
+ method: "PATCH",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ evaluation_criterias: evaluationCriterias }),
+ },
+ );
+}
+
+export async function deleteEvalItem(
+ evalSetId: string,
+ itemName: string,
+): Promise {
+ await fetchJson(`${BASE}/eval-sets/${encodeURIComponent(evalSetId)}/items/${encodeURIComponent(itemName)}`, {
+ method: "DELETE",
+ });
+}
+
+export async function getEvalSet(id: string): Promise {
+ return fetchJson(`${BASE}/eval-sets/${encodeURIComponent(id)}`);
+}
+
+export async function startEvalRun(evalSetId: string): Promise {
+ return fetchJson(`${BASE}/eval-sets/${encodeURIComponent(evalSetId)}/runs`, {
+ method: "POST",
+ });
+}
+
+export async function listEvalRuns(): Promise {
+ return fetchJson(`${BASE}/eval-runs`);
+}
+
+export async function getEvalRun(id: string): Promise {
+ return fetchJson(`${BASE}/eval-runs/${encodeURIComponent(id)}`);
+}
+
+export async function listLocalEvaluators(): Promise {
+ return fetchJson(`${BASE}/local-evaluators`);
+}
+
+export async function createLocalEvaluator(body: {
+ name: string;
+ description: string;
+ evaluator_type_id: string;
+ config: Record;
+}): Promise {
+ return fetchJson(`${BASE}/local-evaluators`, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(body),
+ });
+}
+
+export async function updateEvalSetEvaluators(
+ evalSetId: string,
+ evaluatorRefs: string[],
+): Promise {
+ return fetchJson(`${BASE}/eval-sets/${encodeURIComponent(evalSetId)}/evaluators`, {
+ method: "PATCH",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ evaluator_refs: evaluatorRefs }),
+ });
+}
+
+export async function scaffoldCustomEvaluator(body: {
+ name: string;
+ description?: string;
+}): Promise<{ file_path: string; filename: string; class_name: string; evaluator_id: string }> {
+ return fetchJson(`${BASE}/custom-evaluators/scaffold`, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(body),
+ });
+}
+
+export async function registerCustomEvaluator(filename: string): Promise<{ evaluator_id: string; spec_path: string }> {
+ return fetchJson(`${BASE}/custom-evaluators/register`, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ filename }),
+ });
+}
+
+export async function updateLocalEvaluator(
+ id: string,
+ body: {
+ description?: string;
+ evaluator_type_id?: string;
+ config?: Record;
+ },
+): Promise {
+ return fetchJson(`${BASE}/local-evaluators/${encodeURIComponent(id)}`, {
+ method: "PUT",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(body),
+ });
+}
diff --git a/src/uipath/dev/server/frontend/src/api/explorer-client.ts b/src/uipath/dev/server/frontend/src/api/explorer-client.ts
new file mode 100644
index 0000000..e178222
--- /dev/null
+++ b/src/uipath/dev/server/frontend/src/api/explorer-client.ts
@@ -0,0 +1,37 @@
+import type { FileEntry, FileContent } from "../types/explorer";
+
+const BASE = "/api";
+
+async function fetchJson(url: string, options?: RequestInit): Promise {
+ const res = await fetch(url, options);
+ if (!res.ok) {
+ let errorDetail;
+ try {
+ const body = await res.json();
+ errorDetail = body.detail || res.statusText;
+ } catch {
+ errorDetail = res.statusText;
+ }
+ const error = new Error(`HTTP ${res.status}`);
+ (error as any).detail = errorDetail;
+ (error as any).status = res.status;
+ throw error;
+ }
+ return res.json();
+}
+
+export async function listDirectory(path: string): Promise {
+ return fetchJson(`${BASE}/files/tree?path=${encodeURIComponent(path)}`);
+}
+
+export async function readFile(path: string): Promise {
+ return fetchJson(`${BASE}/files/content?path=${encodeURIComponent(path)}`);
+}
+
+export async function saveFile(path: string, content: string): Promise {
+ await fetchJson(`${BASE}/files/content?path=${encodeURIComponent(path)}`, {
+ method: "PUT",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ content }),
+ });
+}
diff --git a/src/uipath/dev/server/frontend/src/api/statedb-client.ts b/src/uipath/dev/server/frontend/src/api/statedb-client.ts
new file mode 100644
index 0000000..cfd2556
--- /dev/null
+++ b/src/uipath/dev/server/frontend/src/api/statedb-client.ts
@@ -0,0 +1,54 @@
+import type {
+ StateDbTable,
+ StateDbTableData,
+ StateDbQueryResult,
+} from "../types/statedb";
+
+const BASE = "/api";
+
+async function fetchJson(url: string, options?: RequestInit): Promise {
+ const res = await fetch(url, options);
+ if (!res.ok) {
+ let errorDetail;
+ try {
+ const body = await res.json();
+ errorDetail = body.detail || res.statusText;
+ } catch {
+ errorDetail = res.statusText;
+ }
+ const error = new Error(`HTTP ${res.status}`);
+ (error as any).detail = errorDetail;
+ (error as any).status = res.status;
+ throw error;
+ }
+ return res.json();
+}
+
+export async function getStateDbStatus(): Promise<{ exists: boolean }> {
+ return fetchJson(`${BASE}/statedb/status`);
+}
+
+export async function getStateDbTables(): Promise {
+ return fetchJson(`${BASE}/statedb/tables`);
+}
+
+export async function getStateDbTableData(
+ table: string,
+ limit = 100,
+ offset = 0,
+): Promise {
+ return fetchJson(
+ `${BASE}/statedb/tables/${encodeURIComponent(table)}?limit=${limit}&offset=${offset}`,
+ );
+}
+
+export async function executeStateDbQuery(
+ sql: string,
+ limit?: number,
+): Promise {
+ return fetchJson(`${BASE}/statedb/query`, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ sql, limit }),
+ });
+}
diff --git a/src/uipath/dev/server/frontend/src/api/websocket.ts b/src/uipath/dev/server/frontend/src/api/websocket.ts
index 64bc1a7..032c1ed 100644
--- a/src/uipath/dev/server/frontend/src/api/websocket.ts
+++ b/src/uipath/dev/server/frontend/src/api/websocket.ts
@@ -123,4 +123,20 @@ export class WsClient {
setBreakpoints(runId: string, breakpoints: string[]): void {
this.send("debug.set_breakpoints", { run_id: runId, breakpoints });
}
+
+ sendCliAgentStart(agentId: string, sessionId: string, cols: number, rows: number): void {
+ this.send("cli_agent.start", { agent_id: agentId, session_id: sessionId, cols, rows });
+ }
+
+ sendCliAgentInput(sessionId: string, data: string): void {
+ this.send("cli_agent.input", { session_id: sessionId, data });
+ }
+
+ sendCliAgentResize(sessionId: string, cols: number, rows: number): void {
+ this.send("cli_agent.resize", { session_id: sessionId, cols, rows });
+ }
+
+ sendCliAgentStop(sessionId: string): void {
+ this.send("cli_agent.stop", { session_id: sessionId });
+ }
}
diff --git a/src/uipath/dev/server/frontend/src/components/chat/ChatInput.tsx b/src/uipath/dev/server/frontend/src/components/chat/ChatInput.tsx
index 55a3d72..95ec9e5 100644
--- a/src/uipath/dev/server/frontend/src/components/chat/ChatInput.tsx
+++ b/src/uipath/dev/server/frontend/src/components/chat/ChatInput.tsx
@@ -36,13 +36,14 @@ export default function ChatInput({ onSend, disabled, placeholder }: Props) {
onKeyDown={handleKeyDown}
disabled={disabled}
placeholder={placeholder ?? "Message..."}
- className="flex-1 bg-transparent text-sm py-1 focus:outline-none disabled:opacity-40 placeholder:text-[var(--text-muted)]"
+ className="flex-1 bg-transparent text-sm py-1 disabled:opacity-40 placeholder:text-[var(--text-muted)]"
style={{ color: "var(--text-primary)" }}
/>